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
CodePudding user response:
Do you have any suggestion of how can it be done?
Why all the decltype and -> and variadic macro with __VA_ARGS__ and RETURNS and ... Just:
#include <vector>
#include <string>
#define DECL_GETTER(name) \
auto get_##name() { return this->name; }
class foo {
private:
int a = 1;
std::vector<int> b = std::vector<int>(3, 1);
std::string c = "pika-chuuu";
public:
DECL_GETTER(a)
DECL_GETTER(b)
DECL_GETTER(c)
};
int main() {
foo f;
f.get_a();
f.get_b();
f.get_c();
}
I would also add const overload when by it.
You may want to research QT properties system, that is basically like a more advanced version of this.
Subjective: TBH with such one macro you are making abstractions where they are not needed. Is it worth the time? It hides some obvious code with non-obvious macro, makes IDE "jump to definition" harder, makes maintenance and reasoning harder. Renaming variables with IDE features will be harder. Consider just writing those getters by hand verbatim - consider readable and clear code that is obvious on the first look, even when you have to sacrifice a little repetition and type () { return some more characters. Configure your IDE, so you can have "create a getter/setter for this member function" action to speed up your development (see for example QT Creator, it's one feature that is really nice there).
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)?
C is a language without reflection, so basically no, or it would require way more boilerplate code than it is worth.
CodePudding user response:
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
}
