I'm trying to write a function that would allow me to run the first valid expression from a list of expressions, some of which are potentially invalid. This would, for example, allow for uniform treatment of containers, some of which may have emplace_back, but some only emplace.
Here's my current implementation, using is_detected.
template <typename F1, typename... Fs>
class select {
template <typename... Args>
using expr_t = decltype(F1{}(std::declval<Args>()...));
template <typename... Args>
constexpr static bool ok1 = is_detected<expr_t,Args...>::value;
public:
template <typename... Args, std::enable_if_t<ok1<Args...>>* = nullptr>
inline decltype(auto) operator()(Args&&... args) {
return F1{}(std::forward<Args>(args)...);
}
template <typename... Args, std::enable_if_t<!ok1<Args...>>* = nullptr>
inline decltype(auto) operator()(Args&&... args) {
static_assert(sizeof...(Fs),"cannot select valid implementation");
return select<Fs...>{}(std::forward<Args>(args)...);
}
};
#define MEMFCN_SFINAE_WRAP(NAME,EXPR) \
struct NAME { \
template <typename X, typename... Args> \
inline auto operator()(X&& x, Args&&... args) \
-> decltype(EXPR) { return EXPR; } \
};
Here's a usage example:
#include <iostream>
#include <string>
#include <vector>
#include <map>
MEMFCN_SFINAE_WRAP(emplace_back, x.emplace_back(std::forward<Args>(args)...));
MEMFCN_SFINAE_WRAP(emplace, x.emplace(std::forward<Args>(args)...));
int main(int argc, char* argv[]) {
std::vector<std::pair<std::string,std::string>> v;
std::map<std::string,std::string> m;
select<emplace_back,emplace>{}(v,std::make_pair("hello","world"));
select<emplace_back,emplace>{}(m,std::make_pair("hello","world"));
}
My question is whether a more elegant implementation or abstraction is possible. I particularly dislike the fact that I have repeat EXPR twice, once for the return type deduction with decltype, and another time in the function's body.
Using this with member functions on containers is just an example.
The general idea is to use this construct to specify a list of default code snippets to attempt on a priori unknown arguments.
Another example would be to conditionally either print with std::cout << x;, or to print the type of x, or some default message.