C++は静的型付け言語であり、実行時にクラス名からオブジェクトを生成する機能(リフレクション)を標準で提供していない。しかし、特定の要件(例:設定ファイルからのオブジェクト復元や汎用的なシリアライズ機構)において、このような機能が求められることがある。本稿では、マクロとファクトリパターンを活用して、C++で動的オブジェクト生成およびプロパティアクセスを実現する方法を紹介する。
ファクトリクラスの設計
まず、クラス名とそのコンストラクタ関数ポインタをマッピングするファクトリクラスを定義する。
using CreatorFunc = void*(*)();
class ObjectFactory {
public:
static ObjectFactory& getInstance() {
static ObjectFactory instance;
return instance;
}
void* create(const std::string& className) {
auto it = creators.find(className);
return (it != creators.end()) ? it->second() : nullptr;
}
void registerClass(const std::string& name, CreatorFunc func) {
creators[name] = func;
}
private:
std::unordered_map<std::string, CreatorFunc> creators;
};
自動登録機構
クラス定義時に自動的にファクトリに登録される仕組みを、静的メンバ変数とマクロで実現する。
class AutoRegistrar {
public:
AutoRegistrar(const std::string& name, CreatorFunc func) {
ObjectFactory::getInstance().registerClass(name, func);
}
};
#define DECLARE_DYNAMIC_CLASS(ClassName) \
static AutoRegistrar registrar; \
static void* createInstance();
#define IMPLEMENT_DYNAMIC_CLASS(ClassName) \
AutoRegistrar ClassName::registrar(#ClassName, &ClassName::createInstance); \
void* ClassName::createInstance() { return new ClassName(); }
プロパティアクセスの抽象化
各クラスのプロパティに対して、共通のsetter/getterインターフェースを提供するために、基底クラスとマクロを導入する。
using PropertySetter = void(*)(void*, const void*);
class DynamicObject {
public:
virtual ~DynamicObject() = default;
std::unordered_map<std::string, PropertySetter> propertySetters;
template<typename T>
void setProperty(const std::string& name, const T& value) {
auto it = propertySetters.find(name);
if (it != propertySetters.end()) {
it->second(this, &value);
}
}
};
#define DEFINE_PROPERTY(ClassType, Type, Name) \
Type m_##Name; \
static void set##Name(void* obj, const void* val) { \
static_cast<ClassType*>(obj)->m_##Name = *static_cast<const Type*>(val); \
} \
Type get##Name() const { return m_##Name; } \
void register##Name() { \
propertySetters[#Name] = &set##Name; \
}
使用例
上記機構を用いた具体的なクラス定義と利用方法を示す。
class Person : public DynamicObject {
public:
DECLARE_DYNAMIC_CLASS(Person)
DEFINE_PROPERTY(Person, int, Age)
DEFINE_PROPERTY(Person, std::string, Name)
Person() {
registerAge();
registerName();
}
static void* createInstance();
};
IMPLEMENT_DYNAMIC_CLASS(Person)
// 利用側
int main() {
auto* obj = static_cast<Person*>(ObjectFactory::getInstance().create("Person"));
if (obj) {
obj->setProperty("Age", 30);
obj->setProperty("Name", std::string("Alice"));
std::cout << "Name: " << obj->getName() << ", Age: " << obj->getAge() << std::endl;
delete obj;
}
return 0;
}
シリアライズへの応用
この仕組みを拡張することで、XMLやJSONなどの外部フォーマットからのオブジェクト復元(デシリアライズ)が可能になる。たとえば、TinyXML2を用いてXML要素をパースし、setPropertyを呼び出すことで、階層的なデータ構造の復元も実現できる。ただし、オブジェクトの入れ子(再帰的構造)を扱うには、追加のメタ情報(例:プロパティが参照型かどうか)が必要となる。