Reputation: 8511
I would like to iterate over the members of a POD struct, and the only way I can imagine that it could work is with templates. But for this, I need something that solves this Problem:
template <typename ...T>
void bar(T ...t) {
/* ... do something ... */
}
template <typename T>
void foo(T t) {
bar(magical_decompose(t));
}
struct MyPod {
int i;
double d;
float f;
};
int main() {
MyPod myPod = {1,2,3};
foo(myPod);
}
bar(magical_decompose(t));
should be transformed into bar(t.i, t.d, t.f)
by the template. I don't know if it is possible, but I would like it to be possible. Does anybody have an idea how I could accomplish this?
Upvotes: 4
Views: 537
Reputation: 5309
As @sbabbi pointed out, there's no builtin language support currently, so it won't come for free, you have to do something yourself. In c++14, with minimum efforts:
#include <type_traits>
// specialize this trait to register T
template<class T>
struct decompose;
template<class T, class F>
void magical_decompose(T&& t, F&& f)
{
decompose<std::decay_t<T>>::apply(t, f);
}
template <typename ...T>
void bar(T ...t) {
}
template <typename T>
void foo(T t) {
magical_decompose(t, [](auto&&... ts)
{
bar(static_cast<decltype(ts)>(ts)...);
});
}
struct MyPod {
int i;
double d;
float f;
};
// register MyPod
template<>
struct decompose<MyPod>
{
// works for both const & non-const
template<class T, class F>
static void apply(T& t, F& f)
{
f(t.i, t.d, t.f);
}
};
int main() {
MyPod myPod = {1,2,3};
foo(myPod);
}
This is quite generic, just specialize decompose
for your interested type T
, and it can chime in gracefully.
Upvotes: 0
Reputation: 1993
If you are willing to explicitly describe the structure members you might come close to what you wanted.
#include <iostream>
#include <tuple>
#include <functional>
using namespace std;
struct test
{
int i = 121;
double j = 234.0;
string k = "Some k";
};
struct anotherStruct
{
double t = 121.8;
};
struct undescribedStruct
{
string t = "Some undescribed";
};
tuple<int&, double&, string&> struct_as_tuple(test& t)
{
return tie( t.i, t.j, t.k);
}
tuple<double&> struct_as_tuple(anotherStruct& t)
{
return tie( t.t );
}
//make_indices && Co thanks to sigidagi
//see http://cpptruths.blogspot.de/2012/06/perfect-forwarding-of-parameter-groups.html
template<unsigned...> struct index_tuple{};
template<unsigned I, typename IndexTuple, typename... Types>
struct make_indices_impl;
template<unsigned I, unsigned... Indices, typename T, typename... Types>
struct make_indices_impl<I, index_tuple<Indices...>, T, Types...>
{
typedef typename
make_indices_impl<I + 1,
index_tuple<Indices..., I>,
Types...>::type type;
};
template<unsigned I, unsigned... Indices>
struct make_indices_impl<I, index_tuple<Indices...> >
{
typedef index_tuple<Indices...> type;
};
template<typename... Types>
struct make_indices
: make_indices_impl<0, index_tuple<>, Types...>
{};
void bar()
{
std::cout << endl;
}
template <typename T, typename... Args>
void bar(T&& t, Args&&... args)
{
std::cout << "T: [" << t << "] ";
return bar(forward<Args>(args)...);
}
template <unsigned... Indices, class... Args>
void foo_forward_call_impl(index_tuple<Indices...>,
std::tuple<Args...> tuple )
{
return bar(std::get<Indices>(tuple)...);
}
template<class... Args>
void foo_forward_call(std::tuple<Args...> tuple )
{
typedef typename make_indices<Args...>::type Indices;
return foo_forward_call_impl(Indices(), tuple);
}
template <typename T>
void foo(T&& t)
{
return foo_forward_call( struct_as_tuple(t) );
}
int main()
{
test t1;
foo(t1);
anotherStruct t2;
foo(t2);
undescribedStruct t3;
//will error foo(t3);
// your code goes here
return 0;
}
You basically have to provide a tuple construction from struct members for each supported type (see struct_as_tuple).
foo then generates the tuple out of the passed in type and passes it to a tuple unrolling implementation.
It's not what you probably would like to have but it is the closest to it I can imagine at this time...
Upvotes: 1
Reputation: 11191
What you are looking for is called reflection.
There is currently no builtin support in the language for that. There is a working group in the C++ commitee, and some libraries which use macros or other tricks to emulate it in some ways. The simplest solution I can think of uses macro, and its implemented in Boost.Fusion with the BOOST_FUSION_ADAPT_STRUCT macro.
Upvotes: 3
Reputation: 179582
It is possible to determine if a structure contains certain member names (using SFINAE tricks), and to determine their order via offsetof
. For example, if you knew that the member names were all exactly one letter long, you could use these tricks to enumerate the structure members (though the resulting code would not be maintainable in the least).
However, to the best of my knowledge is not possible to simply enumerate the element names without knowing anything about them. Boost.MPL (the Metaprogramming Library), arguably one of the most sophisticated uses of metaprogramming, does not offer such a facility, with the authors noting in an academic paper that this isn't possible:
Unfortunately, this arrangement is not susceptible to the compile-time type introspection power that C++ gives us: there's no way to find out what the names of the members are, and even if we assume that they're named according to some convention as above, there's no way to know how many members there are.
(The last sentence is true for a structure with arbitrarily many members, but by limiting the number of members to e.g. 20, you can solve the problem for a fixed naming scheme using SFINAE).
Upvotes: 2