An easy-to-use reflection realized under C++14. Only minimum modification is needed for existed codes.
What's good about SSR?
-
Users can use a name to create an instance of a class without knowing its type.
-
Users can get "reflected" variables of a class, iterate over them, and get their names, types, and values.
-
You can bind other reflection systems with it - Protobuf reflection, for example.
Only one line of code is needed to mark the class as using reflection.
// in .h file
class ReflectA {
public:
USE_REFLECTION_CLASS();
// What is desired to be caught by reflection
int32_t a_;
// What is NOT desired to be caught by reflection
int32_t b_;
};
In the realization .cc
file that included the previous .h
file, one line of code is also needed to declare its descriptor as well as variables to be reflected.
// in .cc file
// First parameter is typename of class, followed by name of reflected variables
ADD_REFLECTION_CLASS_MEMBER(ReflectA, a_);
As the class has been reflected, you can get all info you need through descriptor, or create new instances by type name.
// Scenario1: an instance existed
ReflectA a;
auto* desc_a = prophet::reflection::DescriptorAccessor<ReflectA>::Get();
desc_a->GetFieldName(0); // "a_"
desc_a->MutableFieldValueById(&a, 0); // a void* ptr to a_ in a
desc_a->MutableFieldValueByName(&a, "a_"); // a void* ptr to a_ in a
// Scenario2: no instance existed
auto* b = NEW_CLASS_PTR("ReflectA");
auto* desc_b = DESC("ReflectA"); // same as desc_a in scenario 1
desc_b->MutableFieldValueById(b, 0); // a void* ptr to a_ in b
For OOP, polymorphism is an important feature. If you need to find descriptor of derived class pointed by a base pointer, here's a simple example on how to do so.
class BaseClass {
public:
// DEFINE REFLECTIONS
virtual std::string GetClassName() const { return class_name; }
private:
std::string class_name{"BaseClass"};
};
class DerivedClassA {
public:
// DEFINE REFLECTIONS
virtual std::string GetClassName() override const { return class_name; }
private:
std::string class_name{"DerivedClassA"};
};
class DerivedClassB {
public:
// DEFINE REFLECTIONS
virtual std::string GetClassName() override const { return class_name; }
private:
std::string class_name{"DerivedClassB"};
};
// When use
// Assume that we have a std::vector<BaseClass*> class_group
for (auto* class : class_group) {
auto* class_desc = DESC(class->GetClassName());
}
// We are also providing RTTI-based methods and in-class methods
for (auto* class : class_group) {
auto* class_desc = DESC_BY_BASE_PTR(class);
}
void ReflectA::Iterate() {
auto* curr_desc = DESC_BY_THIS(this);
}
Status | Pre-defined Types |
---|---|
✔️ | int32_t |
✔️ | int64_t |
✔️ | float |
✔️ | double |
✔️ | bool |
✔️ | std::string |
✔️ | char |
✔️ | glm::vec3 |
✔️ | glm::vec4 |
✔️ | glm::mat3 |
✔️ | glm::mat4 |
✔️ | glm::quat |
✔️ | google::protobuf::Message |
Status | Containers |
---|---|
✔️ | std::vector |
✔️ | std::map |
✔️ | std::unordered_map |
✔️ | std::set |
✔️ | std::unordered_set |
❌ | std::list |
❌ | std::deque |
✔️ | std::unique_ptr |
✔️ | std::shared_ptr |
❌ | std::pair |