C++ その文字列テーブルはもう古い

その文字列テーブルは動的?静的?

今次のような文字列テーブルがあるとしましょう

foo.inl

DEFINE_FOO( foo, "Foo", "あああ" )
DEFINE_FOO( bar, "Bar", "いいい" )
DEFINE_FOO( baz, "Baz", "ううう" )
DEFINE_FOO( qux, "Qux", "えええ" )

foo.h

enum eFoo {
#undef DEFINE_FOO
#define DEFINE_FOO( id, text1, text2 ) eFoo_##id,
#include "foo.inl"
#undef DEFINE_FOO
    elementsof_eFoo
};
const char* get_foo_text1( eFoo foo );
const char* get_foo_text2( eFoo foo );
#define FOO_TEXT1( foo ) get_foo_text1( eFoo_##foo )
#define FOO_TEXT2( foo ) get_foo_text2( eFoo_##foo )

foo.cpp

namespace {
const char* const c_foo_text1[] = {
#undef DEFINE_FOO
#define DEFINE_FOO( id, text1, text2 ) text1,
#include "foo.inl"
#undef DEFINE_FOO
};
const char* const c_foo_text2[] = {
#undef DEFINE_FOO
#define DEFINE_FOO( id, text1, text2 ) text2,
#include "foo.inl"
#undef DEFINE_FOO
};
}
const char* get_foo_text1( eFoo foo ){
    assert( foo < elementsof_eFoo );
    return c_foo_text1[foo];
}
const char* get_foo_text2( eFoo foo ){
    assert( foo < elementsof_eFoo );
    return c_foo_text2[foo];
}

使用例

debug_printf( "foo = '%s'\n", FOO_TEXT1( baz ));

enum型とそれに関連付けられたテキストがあります
これが動的に解決されるべきものであるならこれで問題はありません
動的というのは実行時ということですが、その実行時に解決されるべきものというのは enumの値がファイルに保存されるものであったり、ネットワークから取得するものであったり、ユーザーが入力するものであったり、そういったもののことです

一方静的というのはコンパイル時ということですが、そのコンパイル時に解決されるべきものというのは、ソースコード中で指定されているもののことです
上記の使用例ではソースコード中で識別子を指定していますね
こういった使い方は静的に解決されるべきものです

静的に解決されるべきものであるならこういった文字列テーブルはもう古いのです

現代的な静的な解決方法

C++コンパイル時の問題解決に使うものと言えばもちろんテンプレートです
上記にあるような文字列テーブルはテンプレートに置き換えることができます

foo.h

#undef DEFINE_FOO
#define DEFINE_FOO( id, text1, text2 ) struct tagFoo_##id{}; extern tagFoo_##id eFoo_##id;
#include "foo.inl"
#undef DEFINE_FOO

template<typename eFoo> struct get_foo_text1_t;
#undef	DEFINE_FOO
#define	DEFINE_FOO( id, text1, text2 )	template<> struct get_foo_text1_t<tagFoo_##id> { static const char* get_foo_text1(); };
#include "foo.inl"
#undef	DEFINE_FOO

template<typename eFoo> struct get_foo_text2_t;
#undef	DEFINE_FOO
#define	DEFINE_FOO( id, text1, text2 )	template<> struct get_foo_text2_t<tagFoo_##id> { static const char* get_foo_text2(); };
#include "foo.inl"
#undef	DEFINE_FOO

template<typename eFoo> const char*	get_foo_text1(eFoo){ return	get_foo_text1_t<eFoo>::get_foo_text1(); }
template<typename eFoo> const char*	get_foo_text2(eFoo){ return	get_foo_text2_t<eFoo>::get_foo_text2(); }
#define FOO_TEXT1( foo ) get_foo_text1( eFoo_##foo )
#define FOO_TEXT2( foo ) get_foo_text2( eFoo_##foo )

foo.cpp

#undef	DEFINE_FOO
#define	DEFINE_FOO( id, text1, text2 )	tagFoo_##id	eFoo_##id;
#include "foo.inl"
#undef	DEFINE_FOO

#undef	DEFINE_FOO
#define	DEFINE_FOO( id, text1, text2 )	const char* get_foo_text1_t<tagFoo_##id>::get_foo_text1(){ return	text1; }
#include "foo.inl"
#undef	DEFINE_FOO

#undef	DEFINE_FOO
#define	DEFINE_FOO( id, text1, text2 )	const char* get_foo_text2_t<tagFoo_##id>::get_foo_text2(){ return	text2; }
#include "foo.inl"
#undef	DEFINE_FOO

元のコードには指定されたものが範囲外だったら assert するという処理がありましたが、テンプレートでは指定されたものがあるか無いかといったことはコンパイル時に解決されるため、そうしたものは必要ありません

enum を使った実装

上記の例では get_foo_text1 と get_foo_text2 に渡された関数の引数から型を判別できるように enum ではなくタグ構造体を使うようにしてあります
この仕組みを template argument deduction を使った tag dispatching といいます
template argument deduction は以前にやりましたね。覚えていますか?
C++ 配列の要素数を静的に取得する方法
C++ コンパイル時に値の型を知る方法

enum の値を関数の引数に渡してしまうと渡された側の関数ではもうコンパイル時に enum の値を見ることはできなくなってしまいます
しかし enum の値を関数の引数ではなくテンプレートの引数に渡すのであれば渡された側の関数でも enum の値を見ることはできるので、関数の引数を使わなくても構わないなら enum で実装することもできます

foo.h

enum eFoo {
#undef DEFINE_FOO
#define DEFINE_FOO( id, text1, text2 ) eFoo_##id,
#include "foo.inl"
#undef DEFINE_FOO
    elementsof_eFoo
};

template<eFoo> struct get_foo_text1_t;
#undef	DEFINE_FOO
#define	DEFINE_FOO( id, text1, text2 )	template<> struct get_foo_text1_t<eFoo_##id> { static const char* get_foo_text1(); };
#include "foo.inl"
#undef	DEFINE_FOO

template<eFoo> struct get_foo_text2_t;
#undef	DEFINE_FOO
#define	DEFINE_FOO( id, text1, text2 )	template<> struct get_foo_text2_t<eFoo_##id> { static const char* get_foo_text2(); };
#include "foo.inl"
#undef	DEFINE_FOO

#define FOO_TEXT1( foo ) get_foo_text1_t<eFoo_##foo>::get_foo_text1()
#define FOO_TEXT2( foo ) get_foo_text2_t<eFoo_##foo>::get_foo_text2()

foo.cpp

#undef	DEFINE_FOO
#define	DEFINE_FOO( id, text1, text2 )	const char* get_foo_text1_t<eFoo_##id>::get_foo_text1(){ return	text1; }
#include "foo.inl"
#undef	DEFINE_FOO

#undef	DEFINE_FOO
#define	DEFINE_FOO( id, text1, text2 )	const char* get_foo_text2_t<eFoo_##id>::get_foo_text2(){ return	text2; }
#include "foo.inl"
#undef	DEFINE_FOO

staticなメンバ変数を使った実装

上記の例では staticなメンバ関数を使って実装してありますが、 staticなメンバ変数を使って実装することもできます

foo.h

template<eFoo> struct get_foo_text1_t;
#undef	DEFINE_FOO
#define	DEFINE_FOO( id, text1, text2 )	template<> struct get_foo_text1_t<eFoo_##id> { static const char* const value; };
#include "foo.inl"
#undef	DEFINE_FOO

template<eFoo> struct get_foo_text2_t;
#undef	DEFINE_FOO
#define	DEFINE_FOO( id, text1, text2 )	template<> struct get_foo_text2_t<eFoo_##id> { static const char* const value; };
#include "foo.inl"
#undef	DEFINE_FOO

#define FOO_TEXT1( foo ) get_foo_text1_t<eFoo_##foo>::value
#define FOO_TEXT2( foo ) get_foo_text2_t<eFoo_##foo>::value

foo.cpp

#undef	DEFINE_FOO
#define	DEFINE_FOO( id, text1, text2 )	const char* const get_foo_text1_t<eFoo_##id>::value = text1;
#include "foo.inl"
#undef	DEFINE_FOO

#undef	DEFINE_FOO
#define	DEFINE_FOO( id, text1, text2 )	const char* const get_foo_text2_t<eFoo_##id>::value = text2;
#include "foo.inl"
#undef	DEFINE_FOO

静的な解決にはテンプレート

静的な解決にはテンプレートです