はじめに
ゲームとか作ってると雑にIDを割り振りたくなる場面があったりしますよね。
(なんかオブジェ生成するたびにIdをインクリメントして設定したりしてました)
ただ、雑にインクリメントするだけだと、使い終わったIdを再利用したりできず、インクリメントしつづけていつかオーバーフローするんじゃないかといった恐怖があったので、
使用しなくなったIDを再利用もできるような、簡単なID生成器を作りました。
追記
コメントいただいたのですが、そもそも64bit整数で管理する分には非現実レベルに長時間動かし続けない限りオーバフローすることはなさそうでした。
ようやくオーバフローした頃には、既に自分も墓の中にいるので大丈夫そうですね!
実装
いたってシンプルです。
IdGenerator.hpp
#include <cstdint>
#include <queue>
#include <limits>
#include <exception>
/// <summary>
/// Id生成器
/// </summary>
class IdGenerator
{
public:
using value_type = std::int64_t;
public:
/// <summary>
/// Idの生成
/// </summary>
/// <returns></returns>
value_type createId()
{
if (!m_freeIds.empty()) {
auto id = m_freeIds.front();
m_freeIds.pop();
return id;
}
if (m_nextId == std::numeric_limits<value_type>::max()) [[unlikely]] {
throw std::runtime_error("can't be generated Id by max limit.");
// Fail Safeでもよい
}
return m_nextId++;
}
/// <summary>
/// Idの開放
/// </summary>
/// <param name="id"></param>
void releaseId(value_type id)
{
m_freeIds.push(id);
}
/// <summary>
/// リセット
/// </summary>
void reset()
{
m_nextId = 0;
// queue clear
std::queue<value_type> empty;
m_freeIds.swap(empty);
}
private:
value_type m_nextId = 0;
std::queue<value_type> m_freeIds;
};
基本はインクリメントしていく、使い終わったIDはreleaseId
メソッドに渡すことで
キューにため込んでおき、キューが空じゃない時はそこからポップしてIdを返すようにしています。
⚠️未生成なIdをリリースしたらバグりますが、いちいちvalidationするのもコストなのでその辺はやってないです。
使い方
IdGenerator idGen;
// ID生成
auto id_0 = idGen.createId(); // 0
// ID生成
auto id_1 = idGen.createId(); // 1
// ID 0 使い終わった
idGen.releaseId(id_0);
// ID生成 再利用される
auto new_id_0 = idGen.createId(); // 0
// ID生成
auto id_2 = idGen.createId(); // 2
まとめ
今回は小ネタでしたが、簡単に使えるID生成器を紹介しました。
使い終わったIDを再利用してオーバーフローしないように安心感を得ましょう。