Skip to content

Instantly share code, notes, and snippets.

@zakarumych
Last active August 29, 2015 14:15
Show Gist options
  • Select an option

  • Save zakarumych/532a3cb0cb924b42f591 to your computer and use it in GitHub Desktop.

Select an option

Save zakarumych/532a3cb0cb924b42f591 to your computer and use it in GitHub Desktop.
switch by argument type / inspired by Rust match
#include <boost/variant.hpp>
#include "type_match.h"
int main(int argc, char** argv) {
boost::variant<int, const char*> x(42);
x.apply_visitor(match(
[](const char* value) {
printf("%s\n", value); // won't be called cause x is int
},
[](int value) {
printf("%d\n", value); // will be called with value = 42
}
));
x = "qwerty";
x.apply_visitor(match(
[](const char* value) {
printf("%s\n", value); // will be called with value = "qwerty"
},
[](...) {
// this is called for any type
// but example won't compile without this block
// match must have at least one function suitable for each variant type
}
));
}
#pragma once
#include <type_traits>
#include <boost/preprocessor.hpp>
// Undefine at file end
#define IS_CALLABLE_ONE(N) is_callable<F##N, U>::value
#define AND_IS_NOT_CALLABLE_ONE(z, N, _) && !IS_CALLABLE_ONE(N)
#define AND_IS_NOT_CALLABLE_ALL(N) BOOST_PP_REPEAT(N, AND_IS_NOT_CALLABLE_ONE,_)
#define AND_IS_NOT_CALLABLE_ALL_Z(z, N) BOOST_PP_REPEAT_ ## z(N, AND_IS_NOT_CALLABLE_ONE,_)
#define ENABLE_OVERLOAD(N) typename std::enable_if<(IS_CALLABLE_ONE(N) AND_IS_NOT_CALLABLE_ALL(N))>::type* = 0
#define ENABLE_OVERLOAD_Z(z, N) typename std::enable_if<(IS_CALLABLE_ONE(N) AND_IS_NOT_CALLABLE_ALL_Z(z, N))>::type* = 0
#define DECAY_T_F(z, N, _) typedef typename std::decay<T##N>::type F##N;
#define CONSTRUCT_F(z, N, _) BOOST_PP_COMMA_IF(N) f##N(std::forward<T##N>(t##N))
#define OPERATOR(z, N, _) template<typename U> void operator()(U&& u, ENABLE_OVERLOAD_Z(z, N)) { f##N(std::forward<U>(u)); }
#define LIST_F(z, N, _) F##N f##N;
#define LIST_FARGS(z, N, _) BOOST_PP_COMMA_IF(N) std::forward<T##N>(t##N)
#define DEFINE_TYPE_MATCH_N(z, N, _)\
template<BOOST_PP_ENUM_PARAMS_Z(z, N, typename T)>\
class type_match_x##N {\
public:\
BOOST_PP_REPEAT_ ## z(N, DECAY_T_F, _);\
typedef void result_type;\
type_match_x##N(BOOST_PP_ENUM_BINARY_PARAMS_Z(z, N, T, &&t))\
: BOOST_PP_REPEAT_ ## z(N, CONSTRUCT_F, _) {}\
BOOST_PP_REPEAT_ ## z(N, OPERATOR, _)\
private:\
BOOST_PP_REPEAT_ ## z(N, LIST_F, _);\
};\
template<BOOST_PP_ENUM_PARAMS_Z(z, N, typename T)>\
type_match_x##N<BOOST_PP_ENUM_PARAMS_Z(z, N, T)>\
match(BOOST_PP_ENUM_BINARY_PARAMS_Z(z, N, T, &&t)) {\
return type_match_x##N<BOOST_PP_ENUM_PARAMS_Z(z, N, T)>(BOOST_PP_REPEAT_ ## z(N, LIST_FARGS, _));\
}
template<typename F, typename A>
struct is_callable {
typedef char yes_t;
struct no_t { yes_t x[2]; };
template<typename T>
static yes_t test(decltype(std::declval<F>()(std::declval<T>()))*);
template<typename T>
static no_t test(...);
enum { value = sizeof(test<A>(nullptr)) == sizeof(yes_t) };
};
BOOST_PP_REPEAT_FROM_TO(1, 20, DEFINE_TYPE_MATCH_N, _);
#undef IS_CALLABLE_ONE
#undef AND_IS_NOT_CALLABLE_ONE
#undef AND_IS_NOT_CALLABLE_ALL
#undef AND_IS_NOT_CALLABLE_ALL_Z
#undef ENABLE_OVERLOAD
#undef ENABLE_OVERLOAD_Z
#undef DECAY_T_F
#undef CONSTRUCT_F
#undef OPERATOR
#undef LIST_F
#undef LIST_FARGS
#undef DEFINE_TYPE_MATCH_N
@zakarumych
Copy link
Author

Can be simplified with variadic templates

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment