Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I deal with undesired template instantiation for a function that isn't called?

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.

like image 495
Prismatic Avatar asked Nov 24 '25 01:11

Prismatic


2 Answers

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);
}
like image 76
Jarod42 Avatar answered Nov 25 '25 20:11

Jarod42


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 :-)

like image 37
Angew is no longer proud of SO Avatar answered Nov 25 '25 19:11

Angew is no longer proud of SO



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!