4 ms·
I'm doing something similar in C++ for vertex attributes, integration with Lua et.al. First I define the struct: INTROSPECTION_DEFINE_STRUCT(S, (int, anI
by Kristine1975 11y ago
I'm doing something similar in C++ for vertex attributes, integration with Lua et.al. First I define the struct:
INTROSPECTION_DEFINE_STRUCT(S,
(int, anInt),
(bool, aBool),
(float, aFloat),
(double, aDouble),
(std::string, aString)
);
Then I create an instance of it:
S s{ 23, true, 37.5f, 42.25, "String" };
And finally I can e.g. iterate over all fields and output their value using a generic lambda:
forEachField(s, [](auto v) {
std::cout << v;
});
There are also definitions for the field's type, offset, name etc. generated by the macro, so it's easy to create a binding for vertex attributes, map the struct to a table in Lua, serialize/deserialize it and so on.
I'm really hoping C++ gets introspection soon. There are some proposals like https://isocpp.org/files/papers/n3996.pdf https://isocpp.org/files/papers/n3996.pdf and a working group at https://groups.google.com/a/isocpp.org/forum/#!forum/reflection https://groups.google.com/a/isocpp.org/forum/#!forum/reflect... but I'm not holding my breath for 2017.
- dman 11y agoIs the code for this available somewhere?
- Kristine1975 11y agoNot at the moment, no. But it's not rocket science. The macro essentially creates a struct like this one: struct S { // How many fields are in this struct? static constexpr unsigned int FIELD_COUNT = 5; // Full specializations are not allowed inside classes, so add a dummy parameter <T> that is equivalent to S template <unsigned int Index, typename T = S> struct FieldTraits; // Partial specialization for the first field template <typename T> struct FieldTraits<0, T> { using type = int; static constexpr std::size_t OFFSET = offsetof(S, anInt); static constexpr const char* name() { return "anInt"; } static constexpr type& value(S& self) { return self.anInt; } }; // The first field itself int anInt; // Partial specialization for the second field template <typename T> struct FieldTraits<1, T> { using type = bool; static constexpr std::size_t OFFSET = offsetof(S, aBool); static constexpr const char* name() { return "aBool"; } static constexpr type& value(S& self) { return self.aBool; } }; // The second field itself bool aBool; ... }; Now that we have the meta-information about the struct's fields, we can process them: // Meta-function to process a struct's fields template <typename T, unsigned int FieldIndex, unsigned int FieldCount> struct ForEach { template <typename Functor> static void apply(T& object, Functor f) { // Get the field's value and call the functor with it auto value = T::FieldTraits<FieldIndex>::value(object); f(value); // Next field ForEach<T, FieldIndex + 1, FieldCount>::apply(object, f); }; }; // Partial specialization to end iteration template <typename T, unsigned int FieldCount> struct ForEach<T, FieldCount, FieldCount> { template <typename Functor> static void apply(T& object, Functor f) { }; }; And finally a bit of syntactic sugar: template <typename T, typename Functor> void forEachField(T& object, Functor f) { ForEach<T, 0, T::FIELD_COUNT>::apply(object, f); } *(omitted for the sake of clarity: dealing with const objects, using rvalue references and std::forward for the functor to make the code work with non-copyable functors)
- dman 11y agoThanks for taking the time to write this!
- helmut_hed 11y agoHave you used Boost.Fusion? This looks very much like BOOST_FUSION_ADAPT_STRUCT and seems to have similar design goals