Skip to content

Instantly share code, notes, and snippets.

@zeroxia
Created March 24, 2023 02:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zeroxia/902017ee03e60ef52e076bceefc4591e to your computer and use it in GitHub Desktop.
Save zeroxia/902017ee03e60ef52e076bceefc4591e to your computer and use it in GitHub Desktop.
Wrap a callable object according to its functional signature, and return a corresponding std::function<void()>.
#include <type_traits>
#include <iostream>
#include <functional>
/*
* Type dispatching example.
* Stolen from: https://stackoverflow.com/questions/87372/check-if-a-class-has-a-member-function-of-a-given-signature
* Live demo:
* https://godbolt.org/z/1cxjG4Th9
*/
namespace t1 {
// Primary template with a static assertion for a meaningful error message if it ever gets instantiated.
template<typename Type, typename Signature>
struct IsFunctor {
static_assert(
std::integral_constant<Signature, false>::value,
"Second template parameter needs to be of function type.");
};
// Specialization that does the checking.
template <typename C, typename Ret, typename... Args>
struct IsFunctor<C, Ret(Args...)> {
private:
template <typename T>
static constexpr auto check(int)
-> typename
std::is_same<
decltype( std::declval<T>()(std::declval<Args>()... ) ),
Ret
>::type;
template<typename>
static constexpr std::false_type check(...);
using type = decltype(check<C>(0));
public:
static constexpr bool value = type::value;
};
struct TypeA {
int operator()(int, double) { return 1; }
};
struct TypeB {
float operator()(int, double) { return -3.14F; }
};
struct Dispatcher {
template <typename T>
static
std::enable_if_t<
IsFunctor<std::remove_reference_t<T>, int(int, double)>::value,
std::function<void()>
>
wrap(T&& t) {
return [callable = std::move(t)] () mutable {
auto ret = callable(3, 3.14);
std::cout << "wrapped int(int, double): ret = " << ret << std::endl;
};
}
template <typename T>
static
std::enable_if_t<
IsFunctor<std::remove_reference_t<T>, float(int, double)>::value,
std::function<void()>
>
wrap(T&& t) {
return [callable = std::move(t)] () mutable {
auto ret = callable(3, 3.14);
std::cout << "wrapped float(int, double): ret = " << ret << std::endl;
};
}
};
int FunC(int a, double b) {
return static_cast<int>(a * b);
}
void test() {
std::cout << "check TypeA:\n";
auto f = Dispatcher::wrap(TypeA{});
f();
TypeA A;
f = Dispatcher::wrap(A);
f();
std::cout << "check TypeB:\n";
f = Dispatcher::wrap(TypeB{});
f();
TypeB B;
f = Dispatcher::wrap(B);
f();
std::cout << "check FunC:\n";
f = Dispatcher::wrap(&FunC);
f();
f = Dispatcher::wrap(FunC);
f();
}
} // namespace t1
int main() {
t1::test();
}
@zeroxia
Copy link
Author

zeroxia commented Mar 24, 2023

The code requires C++14, but can be changed to support C++11 as follows:

  1. do not use lambda capture initializers
  2. do not use std::enable_if_t, use typename std::enable_if<...>::type
  3. do not use std::remove_reference_t, use typename std::remove_reference<...>::type

https://godbolt.org/z/G4a4osYvM

Revised C++14 version using std::forward<T> instead of std::move:
https://godbolt.org/z/7xWPT8T87

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