Search code examples
c++getterpreprocessor

Create macro to generate getters for any class c++


Recently I started thinking how to generalize access to private data members through a generalized class/function by name. The reason is that I have a lot of private members and creating a getter for each one is bothersome. I tried to use preprocessor macros to do the following

#define RETURNS(...) -> decltype((__VA_ARGS__))  { return (__VA_ARGS__); }
#define GET(classname, name, funcname) auto funcname() RETURNS(classname->name);

class foo {
private: 
    int a = 1;
    std::vector<int> b = std::vector<int>(3, 1);
    std::string c = "pika-chuuu";
public:
    foo() = default;
    ~foo() = default;

    GET(this, a, getter);
};

int main(const int argc, char* argv[]) {

    foo obj;
    std::cout << obj.getter();
    return 0;
}

This one compiles, but is there a way I can create a getter in foo, which takes the name of a variable at run-time and returns this->(name)? Using this approach I can reduce the code for introducing getters, nevertheless the more data members there are, the more getters I need, but I would like to have one to get access to any data member by name. Do you have any suggestion of how can it be done? I'm looking for a syntax like this:

#define RETURNS(...) -> decltype((__VA_ARGS__))  { return (__VA_ARGS__); }
#define GET(classname, name) RETURNS(classname->name);
class foo {
private: 
    int a = 1;
    std::vector<int> b = std::vector<int>(3, 1);
    std::string c = "pika-chuuu";
public:
    foo() = default;
    ~foo() = default;

    auto getter(auto x) GET(this, x);
};

Here x is the name I put as input, either a,b or c


Solution

  • Revision:

    As @HolyBlackCat mentioned, there is no need to heap allocation, and you should use the impl class as object directly:

    class foo{
        struct foo_data
        {
            int i;
            std::string s;
        };
        foo_data data;
    public:
        template<typename ... Args>
        foo(Args&& ... args)
        : data(std::forward<Args>(args)...)
        {}
    
        foo_data const* operator->() const   // Returns a pointer to const data
        {
            return &data;
        }
    };
    

    Then to access the data:

    int main()
    {
        auto f = foo(3, "hello");
        std::cout << f->i << f->s;          // Accessing data, prints: "3hello"
        // f->i += 1;                       // Error: assignment of member in read-only object
    }