Home > Software design >  conditional execution path on static class trait
conditional execution path on static class trait

Time:03-04

I have problems finding an adequate solution in the attempt to implement an new API alternative to an existing API, which I still want to support for backwards-compatibility;

let this be my old API:

typedef int[3] Node; 

template <class T>
struct convert{
  static std::pair<bool, T> decode(Node node);
}

//and a call to this construct here
template<typename T>
T decode_for_me(Node node) {
    return convert<T>::decode(node).second;
}

//with a specialization, for example a custom type
struct custom_type {int a; int b;};

template<>
struct convert<custom_type>{
    static std::pair<bool, custom_type> decode(Node node) {
       custom_type c; c.a=node[0]; c.b=node[1]; return std::make_pair(true, c); };
};

int main() {
   Node node = {1,2};
   decode_for_me<custom_type>(node);
   return 0;
}

now is would like to change a convert::decode to a little bit more slim signature and refresh the API for further types implemented:

class alt_type {
    int x; int y; int z;
    alt_type(int x, int y, int z) : x(x), y(y), z(z) {};
}

template<>
struct convert<alt_type>{
    static alt_type decode_new_api(Node node) {return {node[0], node[1], node[2]};};
}

how would i now go about to implement a conditional switch on the class-layout/trait of convert<T> to select the correct call-path, replacing decode_for_me<T>() as:

template<typename T>
T decode_for_me(Node node) {
    if constexpr ( "has_trait<convert<T>, "decode">" ) //<<<<
        return convert<T>::decode(node).second;
    if constexpr ( "has_trait<convert<T>, "decode_new_api">" ) // <<<<
        return convert<T>::decode_new_api(node);
    throw std::runtime_error();
}

I researched some similar questions and answers dealing with this in SFINAE varying from template constructs over use of decltype and declval. However, all those deal with member functions, while here I am interested in static evaluation. Also I could get none of them to work for me.

Thanks for any help!

CodePudding user response:

I don't know a way to solve this problem with a single type-traits that receive the name of the method (decode or decode_new_api) as argument.

But, if you accept different tests for different methods, a possible solution is a couple of declared (no need to define) functions to test "declare"

template <typename>
std::false_type has_decode (long);

template <typename T>
auto has_decode (int)
  -> decltype( T::decode(std::declval<Node>()), std::true_type{});

and a couple for "declare_new_api"

template <typename>
std::false_type has_decode_new_api (long);

template <typename T>
auto has_decode_new_api (int)
  -> decltype( T::decode_new_api(std::declval<Node>()), std::true_type{});

Now your decode_for_me() become

template <typename T>
T decode_for_me(Node node) {
    if constexpr ( decltype(has_decode<convert<T>>(0))::value )
        return convert<T>::decode(node).second;
    if constexpr ( decltype(has_decode_new_api<convert<T>>(0))::value )
        return convert<T>::decode_new_api(node);
    throw std::runtime_error("no decode");
}
  • Related