Given POD structures of the general form
struct case_0 { const char *foo; };
struct case_1i { const char *foo; int v0; };
struct case_1d { const char *foo; double v0; };
struct case_2ii { const char *foo; int v0; int v1; };
struct case_2id { const char *foo; int v0; double v1; };
// etc
is it possible to dispatch to (template) members of a function overload set based on the presence or absence of the v0, v1, etc data members -- ideally, without any dependence on the specific type of these members -- and if so, how? Concretely, given
void
process(const case_0& c)
{
do_stuff_with(c.foo);
}
template <typename case_1> void
process(const case_1& c)
{
do_stuff_with(c.foo, c.v0);
}
template <typename case_2> void
process(const case_2& c)
{
do_stuff_with(c.foo, c.v0, c.v1);
}
I would like each overload to be selected for all case_* structures that have all the v-members that are used within its body, and -- equally important -- don't have any v-members that are not used within its body.
This program must be 100% self-contained, so no Boost, please. C++11 features are okay.
You need to write a set of traits such as has_v0 and has_v1 (which I'm sure has been demonstrated many times on SO) then constrain your overloads using them:
template <typename case_0,
typename = typename std::enable_if<!has_v0<case_0>::value>::type,
typename = typename std::enable_if<!has_v1<case_0>::value>::type
>
void
process(const case_0& c)
{
do_stuff_with(c.foo);
}
template <typename case_1,
typename = typename std::enable_if<has_v0<case_1>::value>::type,
typename = typename std::enable_if<!has_v1<case_1>::value>::type
>
void
process(const case_1& c)
{
do_stuff_with(c.foo, c.v0);
}
template <typename case_2,
typename = typename std::enable_if<has_v0<case_2>::value>::type,
typename = typename std::enable_if<has_v1<case_2>::value>::type
>
void
process(const case_2& c)
{
do_stuff_with(c.foo, c.v0, c.v1);
}
You can simplify the constraints with something like
template<typename Cond>
using Require = typename std::enable_if<Cond::value>::type;
e.g.
template <typename case_2,
typename = Require<has_v0<case_2>>,
typename = Require<has_v1<case_2>>
>
void
process(const case_2& c)
{
do_stuff_with(c.foo, c.v0, c.v1);
}
One solution is provided by @Jonathan Wakely which employs the use of has_XXX meta-functions.
Here is another solution, but it requires you to change your full-fledge structs definitions to mere typedefs of std::tuple<>.
struct case_0 { const char *foo; };
struct case_1i { const char *foo; int v0; };
struct case_1d { const char *foo; double v0; };
struct case_2ii { const char *foo; int v0; int v1; };
struct case_2id { const char *foo; int v0; double v1; };
typedef std::tuple<const char*> case_0;
typedef std::tuple<const char*,int> case_1i;
typedef std::tuple<const char*,double> case_1d;
typedef std::tuple<const char*,int,int> case_2ii;
typedef std::tuple<const char*,int,double> case_2id;
template<typename...Args>
auto foo(std::tuple<Args...> & tpl) -> decltype(std::get<0>(tpl))&
{
return std::get<0>(tpl);
}
template<typename...Args>
auto v0(std::tuple<Args...> & tpl) -> decltype(std::get<1>(tpl))&
{
return std::get<1>(tpl);
}
template<typename...Args>
auto v1(std::tuple<Args...> & tpl) -> decltype(std::get<2>(tpl))&
{
return std::get<2>(tpl);
}
case_1i obj; //full-fledge struct
obj.foo = "hello";
obj.v0 = 100;
case_1i obj; //typedef struct
foo(obj) = "hello";
v0(obj) = 100;
Once you accept this design change, the solution to your original problem becomes pretty much straight-forward as follows:
template<size_t...>
struct seq{};
template<size_t M, size_t ...N>
struct genseq : genseq<M-1,M-1, N...> {};
template<size_t ...N>
struct genseq<0,N...>
{
typedef seq<N...> type;
};
template <typename ...Args, size_t ...N>
void call_do_stuff_with(std::tuple<Args...> & tpl, seq<N...>)
{
do_stuff_with(std::get<N>(tpl)...);
}
template <typename ...Args>
void process(std::tuple<Args...> & tpl)
{
const size_t N = sizeof ...(Args);
call_do_stuff_with(tpl, typename genseq<N>::type());
}
Do let me know if that is acceptable. If that is not acceptable, I will delete my answer (if you feel so).
Live demo!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With