I'm trying to implementing some code where I want to call certain templated methods based on type traits. Unfortunately, the methods are instantiated with types (that are never called with said types) that are incompatible with the function, which causes compilation errors. Ideally I could use the traits to prevent said uncalled methods from being instantiated but that's not the case, so I'm looking for a workaround or design pattern I can use.
I have a few simple POD structs. Each type has different members (their existence is indicated by corresponding traits)
struct ThingA
{
int a;
};
struct ThingAB
{
int a;
int b;
};
struct ThingABC
{
int a;
int b;
int c;
};
template <typename Thing>
struct ThingTraits
{
static const bool has_a=false;
static const bool has_b=false;
static const bool has_c=false;
};
Here's the template function; ThingA, ThingAB and ThingABC can be passed to it as either the Src or the Dst type
template<typename Src, typename Dst>
void assign(Src const &src, Dst &dst)
{
constexpr bool c_a = ThingTraits<Src>::has_a && ThingTraits<Dst>::has_a;
constexpr bool c_b = ThingTraits<Src>::has_b && ThingTraits<Dst>::has_b;
constexpr bool c_c = ThingTraits<Src>::has_c && ThingTraits<Dst>::has_c;
c_a ? copy_a(src,dst) : do_nothing();
c_b ? copy_b(src,dst) : do_nothing();
c_c ? copy_c(src,dst) : do_nothing();
}
The copy_a/b/c methods just copy the corresponding member:
template<typename Src, typename Dst>
void copy_a(Src const &src, Dst &dst)
{
dst.a = src.a;
}
template<typename Src, typename Dst>
void copy_b(Src const &src, Dst &dst)
{
dst.b = src.b;
}
template<typename Src, typename Dst>
void copy_c(Src const &src, Dst &dst)
{
dst.c = src.c;
}
When trying to compile this, I get errors because copy_a/b/c are instantiated for types where the required members don't exist (ie copy_b).
How can I implement something functionally equivalent since this way doesn't really work? I need ThingA,AB,ABC to stay as simple PODs with no additional members (size req is strict) and I don't want to determine which copy operations to call during run time.
You may have something like:
template<bool hasA>
struct copy_a_caller
{
template <typename Src, typename Dst>
void operator () (const Src& src, Dst& dst) const
{
dst.a = src.a;
}
};
template<>
struct copy_a_caller<false>
{
template <typename Src, typename Dst>
void operator () (const Src&, Dst&) const {}
};
template<typename Src, typename Dst>
void copy_a(Src const &src, Dst &dst)
{
copy_a_caller<ThingTraits<Src>::has_a && ThingTraits<Dst>::has_a>()(src, dst);
}
// similar thing for b and c
And then
template<typename Src, typename Dst>
void assign(Src const &src, Dst &dst)
{
copy_a(src, dst);
copy_b(src, dst);
copy_c(src, dst);
}
You can simply use the delegate-to-class trick & (partial) specialisation:
template<typename Src, typename Dst>
void assign(Src const &src, Dst &dst)
{
constexpr bool c_a = ThingTraits<Src>::has_a && ThingTraits<Dst>::has_a;
constexpr bool c_b = ThingTraits<Src>::has_b && ThingTraits<Dst>::has_b;
constexpr bool c_c = ThingTraits<Src>::has_c && ThingTraits<Dst>::has_c;
copy_a<c_a>::call(src, dst);
copy_b<c_b>::call(src, dst);
copy_c<c_c>::call(src, dst);
}
template <bool HasA>
struct copy_a;
template <>
struct copy_a<true>
{
template <class Src, class Dst>
static void call(Src const &src, Dst &dst)
{ src.a = dst.a; }
};
template <>
struct copy_a<false>
{
template <class Src, class Dst>
static void call(Src const &, Dst &)
{}
};
copy_b and copy_c are left as an excercise for the reader :-)
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