I am implementing some rudimentary reflection in C++ for an ultra modular architecture where virtually all features are loaded as plugins and interpreted dynamically at run-
Since you mentioned you're in VisualStudio, the macros I wrote below may not work for you (in my experience, macros can be obnoxious cross-platform). So, here's a Python sample that you can run as a pre-build script generate the *.cpp files based on the names in a text file.
create_classes.py
template = """Reflection::Type * {0}::GetType() {{
return &_type;
}}
// static type info
{0} {0}::_refObj;
Reflection::Type {0}::_type(_refObj, "{0}", {0}::IsAssignableFrom);
Reflection::Type * {0}::Type() {{
return &_type;
}}
bool {0}::IsAssignableFrom(Reflection::Type * type) {{
return dynamic_cast<{0}*>(&type->RefObj()) != nullptr;
}}
"""
if __name__ == '__main__':
with open('classes', 'r') as classes:
for class_name in classes:
class_name = class_name.strip()
with open('{0}.cpp'.format(class_name), 'w') as source:
source.write(template.format(class_name))
classes (text file)
Blossom
Bubbles
Buttercup
Which will create Blossom.cpp, Bubbles.cpp, and Buttercup.cpp using the template above. Getting the correct names into the 'classes' text file is up to you. :)
I'm sure you can adapt this to split each definition across *.hpp and *.cpp, let me know if this is helpful.
As much as I dislike macros (and I still advise against using them when possible!) here are macros that will generate your code. I haven't tested them thoroughly, so they could shoot you in the foot. They're also named poorly (unclear what the macros do from their names), but this is the idea.
macros.cpp
#define GET_TYPE_METHOD(X) \
Reflection::Type * X::GetType() { return &_type; }
#define GET_REF_OBJ(X) X X::_refObj;
#define GET_UNDERSCORE_TYPE(X) \
Reflection::Type X::_type(_refObj, #X, X::IsAssignableFrom);
#define GET_TYPE(X) \
Reflection::Type * X::Type() { return &_type; }
#define GET_IS_ASSIGNABLE_FROM(X) \
bool X::IsAssignableFrom(Reflection::Type * type) { return dynamic_cast(&type->RefObj()) != nullptr; }
GET_TYPE_METHOD(Keeler)
GET_REF_OBJ(Keeler)
GET_UNDERSCORE_TYPE(Keeler)
GET_TYPE(Keeler)
GET_IS_ASSIGNABLE_FROM(Keeler)
If you run g++ -E macros.cpp
, you get the preprocessor output. Check out what the preprocessor thinks:
$ g++ -E macros.cpp
# 1 "macros.cpp"
# 1 ""
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "" 2
# 1 "macros.cpp"
# 16 "macros.cpp"
Reflection::Type * Keeler::GetType() { return &_type; }
Keeler Keeler::_refObj;
Reflection::Type Keeler::_type(_refObj, "Keeler", Keeler::IsAssignableFrom);
Reflection::Type * Keeler::Type() { return &_type; }
bool Keeler::IsAssignableFrom(Reflection::Type * type) { return dynamic_cast(&type->RefObj()) != nullptr; }
Is this along the lines of what you were looking for?