Reputation: 1382
I would like to simplify the following
class A {
int a;
int b;
int c;
std::vector<int*> addrs;
public:
A() : addrs{ &a, &b, &c } {}
};
so that I don't have the write the list of fields in two places, i.e. the declarations and the initializer for addrs
. Is there some way to use a macro to collect the declarations and use them later. E.g.,
class A {
VAR_DECL(a);
VAR_DECL(b);
VAR_DECL(c);
std::vector<int*> addrs;
public:
A() : addrs{ VAR_ADDRESSES } {}
};
For context this is intended for implementing some kind of property introspection system.
Upvotes: 2
Views: 448
Reputation: 217235
You may use union :
class A {
A() {
static_assert(&u.a == &u.vars[0], "&u.a == &u.vars[0] failed");
static_assert(&u.b == &u.vars[1], "&u.b == &u.vars[1] failed");
static_assert(&u.c == &u.vars[2], "&u.c == &u.vars[2] failed");
}
private:
union {
struct {
int a;
int b;
int c;
};
int vars[3];
} u;
};
Upvotes: 0
Reputation: 4950
I usually refrain from "Don't do this, you really want to do that instead" answers. But in this case the problem is too obvious.
You are allocating memory on the heap for information that is available at compile time. That is horrible.
Your implementation unnecessarily breaks the default copy and move constructor behavior. I hope you are aware of that. I hope everyone reusing that code is aware of that.
I guess what you are trying to achieve is a generic way to visit all your members. Do something like the following:
class A {
int a;
int b;
int c;
public:
A() {}
template<class F> ForEachMember(F f) {
f(a);
f(b);
f(c);
}
};
This supports members of different type if the F::operator()
is overloaded.
If that is a frequent pattern through your code, and you think repeating the member names is error-prone, you could use boost::tuple
and boost::fusion
:
#include <boost/fusion/algorithm/iteration/for_each.hpp>
#include <boost/fusion/include/for_each.hpp>
#include <boost/fusion/adapted/boost_tuple.hpp>
#include <boost/fusion/include/boost_tuple.hpp>
class A : boost::tuple<int, int, int> {
template<class F> ForEachMember(F f) {
boost::fusion::for_each( *this, f );
}
// if necessary, write getter/setter with pretty names
int& a() { return get<0>(); }
};
Upvotes: 2
Reputation:
You could eliminate the address vector and iterate over the members (I kept that vector here, though)
#include <iostream>
#include <tuple>
#include <vector>
// Dedicated function
template <typename T, std::size_t I = 0, typename ...Tuple>
inline typename std::enable_if<I == sizeof...(Tuple), void>::type
collect_addresses(std::vector<T*>& result, std::tuple<Tuple...>&) {
}
template <typename T, std::size_t I = 0, typename ...Tuple>
inline typename std::enable_if<I < sizeof...(Tuple), void>::type
collect_addresses(std::vector<T*>& result, std::tuple<Tuple...>& tuple) {
result.push_back(&std::get<I>(tuple));
collect_addresses<T, I + 1, Tuple...>(result, tuple);
}
template <typename T, typename ...Tuple>
inline std::vector<T*> collect_addresses(std::tuple<Tuple...>& tuple) {
std::vector<T*> result;
result.reserve(sizeof...(Tuple));
collect_addresses(result, tuple);
return result;
}
// Static function [Tuple]
template <typename Function, std::size_t I = 0, typename ...Tuple>
inline typename std::enable_if<I == sizeof...(Tuple), void>::type
invoke_tuple(const Function&, std::tuple<Tuple...>&) {
}
template <typename Function, std::size_t I = 0, typename ...Tuple>
inline typename std::enable_if<I < sizeof...(Tuple), void>::type
invoke_tuple(const Function& function, std::tuple<Tuple...>& tuple) {
function(std::get<I>(tuple));
invoke_tuple<Function, I + 1, Tuple...>(function, tuple);
}
// Member function [Tuple]
template <typename Instance, typename Function, std::size_t I = 0, typename ...Tuple>
inline typename std::enable_if<I == sizeof...(Tuple), void>::type
invoke_tuple(Instance&, const Function&, std::tuple<Tuple...>&) {
}
template <typename Instance, typename Function, std::size_t I = 0, typename ...Tuple>
inline typename std::enable_if<I < sizeof...(Tuple), void>::type
invoke_tuple(Instance& instance, const Function& function, std::tuple<Tuple...>& tuple) {
(instance.*function)(std::get<I>(tuple));
invoke_tuple<Instance, Function, I + 1, Tuple...>(instance, function, tuple);
}
// Static function [Variadic Template]
template <typename Function>
inline void invoke(const Function&) {
}
template <typename Function, typename T, typename ...Args>
inline void invoke(const Function& function, T& value, Args&... args) {
function(value);
invoke(function, args...);
}
// Member function [Variadic Template]
template <typename Instance, typename Function>
inline void invoke(Instance&, const Function&) {
}
template <typename Instance, typename Function, typename T, typename ...Args>
inline void invoke(Instance& instance, const Function& function, T& value, Args&... args) {
(instance.*function)(value);
invoke(instance, function, args...);
}
class A {
// public in this test
public:
std::tuple<int, int, int> params;
std::vector<int*> addrs;
A() : addrs(collect_addresses<int>(params))
{}
};
class B {
private:
typedef std::tuple<int, int, int> Params;
// public in this test
public:
Params params;
std::vector<int*> addrs;
B()
{
addrs.reserve(std::tuple_size<Params>::value);
invoke_tuple([this](int& i) { addrs.push_back(&i); }, params);
}
};
class C {
// public in this test
public:
int a;
int b;
int c;
std::vector<int*> addrs;
C()
{
addrs.reserve(3);
invoke([this](int& i) { addrs.push_back(&i); }, a, b, c);
}
};
int main(){
A a;
for(int* p: a.addrs) std::cout << (const void*)p << std::endl;
B b;
for(int* p: b.addrs) std::cout << (const void*)p << std::endl;
C c;
for(int* p: c.addrs) std::cout << (const void*)p << std::endl;
}
Upvotes: 0
Reputation: 171127
You could do it using Boost Preprocessor.
#define VARIABLES (a)(b)(c)
#define DECLARE_MEMBER(maR, maType, maId) \
maType maId;
#define TAKE_ADDRESS(maR, maUnused, maIndex, maId) \
BOOST_PP_COMMA_IF(maIndex) & maId
class A {
BOOST_PP_SEQ_FOR_EACH(DECLARE_MEMBER, int, VARIABLES)
std::vector<int*> addrs;
public:
A() : addrs { BOOST_PP_SEQ_FOR_EACH_I(TAKE_ADDRESS, %%, VARIABLES) } {}
};
// Now you can clean up:
#undef DECLARE_MEMBER
#undef TAKE_ADDRESS
// In case you no longer need the list, also:
#undef VARIABLES
Upvotes: 6