///////////////////////////////////////////////
// reference implementation for swallowing
// return values in a 'function<void(...)>'
// and thus accept a wider range of callables
//
// most of this is boiler-plate code to mimic
// the implementation in libc++ w.r.t. the
// SFINAE trickery
//
// note that 'invoke_test' is actually
// implemented to facilitate reuse by
// 'maybe_swallow', keeping it DRY.
//
// -- Xeo
///////////////////////////////////////////////
#include <type_traits>
#include <utility>
#include <iostream>
// naive implementations
struct not_a_type{};
auto invoke_test(...)
-> not_a_type;
// only functor and function pointer support for reference
template<class F, class... As>
auto invoke_test(F&& f, As&&... as)
-> decltype(std::forward<F>(f)(std::forward<As>(as)...))
{ // actually implemented for maybe_swallow
return std::forward<F>(f)(std::forward<As>(as)...);
};
template<class F, class... As>
struct invokable{
typedef decltype(
invoke_test(std::declval<F>(), std::declval<As>()...)
) type;
static bool const value =
!std::is_same<type, not_a_type>::value;
};
template<class F, class... As>
struct invoke_of{
typedef typename invokable<F, As...>::type type;
};
template<class R1, class R2>
struct convertible_check
: std::is_convertible<R1, R2> {};
template<class R1>
struct convertible_check<R1, void>{
static bool const value = true;
};
// workaround for missing partial function specialization
template<class T> struct type{};
template<class R, class F, class... As>
auto maybe_swallow(type<R>, F&& f, As&&... as)
-> typename invoke_of<F, As...>::type
{
return invoke_test(std::forward<F>(f), std::forward<As>(as)...);
}
template<class F, class... As>
void maybe_swallow(type<void>, F&& f, As&&... as)
{
(void)invoke_test(std::forward<F>(f), std::forward<As>(as)...);
}
template<class Sig>
class function;
// lazy me supports only function pointer
template<class R, class... As>
class function<R(As...)>
{
template<class> struct dump;
template<class F, bool = invokable<F&, As...>::value>
struct callable;
template<class F>
struct callable<F, true>{
static bool const value =
convertible_check<typename invoke_of<F&, As...>::type, R>::value;
};
template<class F>
struct callable<F, false>{ static bool const value = false; };
template<class F>
struct enable_if_callable
: std::enable_if<callable<F>::value> {};
public:
template<class Fptr>
function(Fptr f, typename enable_if_callable<Fptr>::type* = 0);
template<class... FAs>
R operator()(FAs&&... fas){
return _f(_f_holder, std::forward<FAs>(fas)...);
}
private:
typedef void (*void_fptr)();
typedef R (*call_func)(void_fptr, As...);
call_func _f;
void_fptr _f_holder;
template<class Fptr>
static R call_fptr(void_fptr fh, As... as){
//dump<Fptr>();
return maybe_swallow(type<R>(), (Fptr)fh, as...);
}
};
template<class R, class... As>
template<class Fptr>
function<R(As...)>::function(Fptr f,
typename enable_if_callable<Fptr>::type*)
: _f(&call_fptr<Fptr>)
, _f_holder((void_fptr)f)
{
}
int foo(int i){ std::cout << i << "\n"; return i; }
void bar(int i){ (void)foo(i); }
void baz(function<void(int)> f){ f(42); }
void baz(function<void(int,int)>){}
void quux(function<void(int)>){}
void quux(function<int(int)>){}
int main(){
function<void(int)> f1(foo), f2(bar);
f1(42);
f2(42);
baz(foo);
baz(bar);
//quux(foo);
// comment in for ambiguous overload error
// since function<void(...)> now accepts any
// callable as long as the parameters work
// could be "explained" that it fits with the
// rule that one can't overload on return type
// alone. :)
}