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");
}