c++templatesreflectionmacros

C++ automatic getter and setters using templates, reflection, and macros


I'm making a small library that will (among other things) allow users to provide a class, A as a template argument to a base class, Base, and have all the (public) class fields of A get converted into getters/setters in Base. Here is a code example:

struct A {
  float x, y, z;
}

template<typename T>
class Base {
  public:

  // some code
}

Then, if I instantiate a new instance of Base,

Base<A> b = Base<A>(A);
b.get_x(); // should work, since `A` has the field x
b.get_w(); // doesn’t work, since `A` has no field w

And, the user shouldn’t need to alter the definition of A to be able to be a template for Base.

I'm pretty sure getting functionality like this isn’t possible in the way I described above, but it may be possible using templates

b.get<A::x>() // or something similar to this?

I think this type of functionality exists with Boost’s PFR library, but it would would only allow me to input the index of the struct A’s fields. e.g.

b.get<0>() // this would work
b.get_x(); // not sure how I could implement this
b.get<A::x>() ; // again, I don’t know how to implement something like this

I wasn't able to write code that implements the first example, b.get<0>() as I’m not too familiar with PFR. But based on the README, the latter two examples may not be supported since PFR only gives access to fields by index and not by name.

Is there any way to get something like this working? It should be able to extend beyond just getters and setters, but allow me to alter the output before getting and the input before setting (so an automatic getter/setter macro wouldn’t really work)


Solution

  • C++ doesn't have reflection (yet) so automatically implementing b.get_x() is indeed not possible.

    You can implement your second version with member pointers though:

    #include <iostream>
    
    struct A {
      float x, y, z;
    };
    
    template<typename T>
    class Base: private T {
    public:
      template<typename Value>
      const Value& get(Value T::* member) const
      {
        return this->*member;
      }
    
      template<typename Value, typename NewValue>
      void set(Value T::* member, NewValue&& value)
      {
        this->*member = std::forward<NewValue>(value);
      }
    };
    
    int main()
    {
      Base<A> a;
      a.set(&A::x, 10.5f);
      std::cout << a.get(&A::x);
    }