Related
Unfortunately this code doesn't work:
#include <iostream>
#include <type_traits>
#include <utility>
#include <tuple>
template<typename FirstArg, typename ... Args>
requires (sizeof ... (Args) == 0 || (std::is_convertible_v<Args ..., FirstArg>))
constexpr void multi_max( FirstArg firstArg, Args const &... args )
{
using namespace std;
FirstArg &max = firstArg;
auto findMax = [&]<size_t ... Is>( index_sequence<Is ...> iseq, tuple<Args ...> tArgs )
{
((max = get<Is>( tArgs ) > max ? get<Is>( tArgs ) : max), ...);
};
findMax( make_index_sequence<sizeof ... (Args)>(), make_tuple( args ... ) );
}
int main()
{
multi_max( 1 );
//multi_max( 1, 2, 3 );
}
The commented section doesn't fulfill the right half of the requires-constraint. Why ? And if I remove the first constraint the compilers complains about wrong unpacking of the args into a tuple.
Looking at the relevant portion of the compiler error:
note: because substituted constraint expression is ill-formed: too many template arguments for variable template 'is_convertible_v'
You're using is_convertible_v<int, int, int>. What you want is all tests of individual arguments to be true, which you can do with a fold expression:
requires (sizeof ... (Args) == 0 || (std::is_convertible_v<Args, FirstArg> && ...))
However, for a pack of 0 elements, the fold expression will produce true for &&, so the first part is now redundant:
requires (std::is_convertible_v<Args, FirstArg> && ...)
Cleaning up to use the concept convertible_to instead of the old type trait:
requires (std::convertible_to<Args, FirstArg> && ...)
Now things are looking a little suspiciously convenient. We can actually move this to the template parameter and get rid of the requires entirely:
template<typename FirstArg, std::convertible_to<FirstArg> ... Args>
This placeholder will shove FirstArg in as the second argument and place the given type as the first, so a given T would test std::convertible_to<T, FirstArg>.
I am new to c++ metaprogramming. I tried to look at other answers, but I was not able to find one that could suit my problem.
Or simply I was not able to apply it to my case.
Here I will post a simplified version of the code, to highlight the main features which I would like to obtain.
What I would like to achieve, is the construction of a std::tuple of dimension N (N known at compile-time),
whose components type is given by a template class, MyType, depending on two parameters M and N.
M is fixed, while the type of the tuple component i is actually MyType<M,i>, for i=0,...,N.
Since I have to define recursively a tuple of these types, I have considered the DefineType template.
// general definition
template<Integer M, Integer N>
struct DefineType
{
using rest = typename DefineType<M, N-1>::type;
using type = decltype(std::tuple_cat(std::declval< std::tuple< MyType<M,N>>>(),
std::declval<rest>() ));
};
// specialization for N=0
template<Integer M>
struct DefineType<M,0>
{
using type = typename std::tuple< MyType<M,0> >;
};
This should produce the following types:
DefineType< M, N=0 >: std::tuple< MyType< M,0 > > ;
DefineType< M, N=1 >: std::tuple< MyType< M,0 >, MyType< M,1 > > ;
DefineType< M, N=2 >: std::tuple< MyType< M,0 >, MyType< M,1 > , MyType< M,2 > > ;
and so on, up to a general N.
Then I would like also to initialize a tuple of this kind, based on something which I call param of type Param. For doing this,
I write a code of this kind:
// general definition
template<Integer M, Integer N>
typename DefineType<M,N>::type MyClass(Param param)
{
return std::tuple_cat(std::tuple<MyType<M,N>>(MyType<M,N>(param)),
MyClass<M,N-1>(param) ) ;
}
// specialization for N=0
template<Integer M>
typename DefineType<M,0>::type MyClass(Param param)
{
return std::tuple<MyType<M, 0>>(MyType<M, 0>(param));
}
Finally in the main:
int main()
{
// M and N given
const auto myobject=MyClass<M,N>(param);
}
The code is not compiling, complaining that I am initializing too many times DefineType<M,N>. Basically N does not reach the base-case, with N=0. I do not get why...So for sure the recursive type definition is wrong. But, in addition to this, maybe there are other errors that I do not see. I hope you can help me in understanding how to do this. I apologize, but meta programming is very new (and difficult) to me.
Thank you.
Given the definitions
template<Integer M, Integer N>
typename DefineType<M,N>::type MyClass(Param param)
{
return std::tuple_cat(std::tuple<MyType<M,N>>(MyType<M,N>(param)),
MyClass<M,N-1>(param) ) ;
}
template<Integer M>
typename DefineType<M,0>::type MyClass(Param param)
{
return std::tuple<MyType<M, 0>>(MyType<M, 0>(param));
}
what you have is two overloaded distinct function templates. The second is not a "partial specialization" of the first because there is no such thing a function template partial specialization, only class template specializations. (And so the call MyClass<M,N-1>(param) can't possibly match the second template, even if it had been previously declared, since the second one only accepts one template argument, meaning the first template is infinitely recursive.)
One solution could be to use a helper class template:
namespace MyClass_detail {
template<Integer M, Integer N>
struct helper {
static typename DefineType<M,N>::type build(const Param& param)
{
return std::tuple_cat(
std::tuple<MyType<M,N>>(MyType<M,N>(param)),
MyClass<M,N-1>(param));
}
};
template<Integer M>
struct helper<M, 0> {
static typename DefineType<M,0>::type build(const Param& param)
{
return std::tuple<MyType<M, 0>>(MyType<M, 0>(param));
}
};
}
template<Integer M, Integer N>
typename DefineType<M,N>::type MyClass(Param param)
{
return MyClass_detail::helper<M,N>::build(Param);
}
Though I would recommend taking advantage of std::make_integer_sequence. (This is a C++14 feature, and I see your question is tagged C++11. If you can't use C++14 or later, a search should turn up some replacement implementations for make_integer_sequence and related tools that can be used in C++11.)
#include <utility>
#include <tuple>
namespace MyClass_detail {
template<Integer M, Integer N, Integer ...Inds>
auto MyClass_helper(const Param ¶m, std::integer_sequence<Integer, Inds...>)
{
return std::make_tuple(MyType<M, N-Inds>(param)...);
}
}
template<Integer M, Integer N>
auto MyClass(Param param)
{
return MyClass_detail::MyClass_helper<M,N>(
param, std::make_integer_sequence<Integer, N+1>{});
}
// And if DefineType is wanted for other uses:
template<Integer M, Integer N>
using DefineType = decltype(MyClass<M,N>(std::declval<Param>()));
See the full working demo on coliru.
I see two problems in your code.
(1) you say that you want that
DefineType< M, N=2 > is std::tuple< MyType< M,0 >, MyType< M,1 > , MyType< M,2 > >
but writing
using type = decltype(std::tuple_cat(std::declval< std::tuple< MyType<M,N>>>(),
std::declval<rest>() ));
inside DefineType, you get the opposite order; you obtain that
DefineType< M, N=2 > is std::tuple<MyType<M, 2>, MyType<M, 1> , MyType<M, 0>>
If you want the order from zero to N, you have to define, in DefineType, before the rest and then the N element; I mean
using type = decltype(std::tuple_cat(
std::declval<rest>(),
std::declval<std::tuple<MyType<M,N>>>() ));
(2) The recursion for MyClass() function doesn't works because in your recursive version call the same MyClass() ever with two template parameters
template<Integer M, Integer N>
typename DefineType<M,N>::type MyClass(Param param)
{
return std::tuple_cat(std::tuple<MyType<M,N>>(MyType<M,N>(param)),
MyClass<M,N-1>(param) ) ;
} // you call the second parameter .........^^^
// also when N is 1 (and N-1 is 0)
so the base-case (defined with only one template parameter) never matches.
Unfortunately partial template specialization doesn't works for template functions, so you can use partial template specialization of structs (see aschepler's answer) or, if you prefer, SFINAE to enable/disable the two versions of MyClass() according the value of N.
I propose the following solution
// specialization for N == 0
template <Integer M, Integer N>
typename std::enable_if<(N == 0), typename DefineType<M,0>::type>::type
MyClass(Param param)
{ return std::tuple<MyType<M, 0>>(MyType<M, 0>(param)); }
// general definition
template <Integer M, Integer N>
typename std::enable_if<(N > 0u), typename DefineType<M,N>::type>::type
MyClass(Param param)
{
return std::tuple_cat(
MyClass<M,N-1>(param),
std::tuple<MyType<M,N>>(MyType<M,N>(param)) );
}
Observe that now the ground case (N == 0) has two template parameter but is enabled only when N is zero. The other case in enabled only when N > 0.
Observe also that you have to write before the ground case version because it is used by the recursive version.
Observe also that I've switched the order of rest/actual type.
If you can use C++14, so std::make_index_sequence/std::index_sequence, I strongly suggest to avoid recursion and to follow the aschepler's suggestion.
You can also avoid recursion for the DefineType itself using specialization as follows
template <Integer, Integer N, typename = std::make_index_sequence<N+1u>>
struct DefineType;
template <Integer M, Integer N, std::size_t ... Is>
struct DefineType<M, N, std::index_sequence<Is...>>
{ using type = std::tuple<MyType<M, Is>...>; };
The following is a full compiling C++14 example
#include <tuple>
#include <type_traits>
using Integer = std::size_t;
using Param = int;
template <Integer M, Integer N>
struct MyType
{ MyType (Param) {} };
template <Integer, Integer N, typename = std::make_index_sequence<N+1u>>
struct DefineType;
template <Integer M, Integer N, std::size_t ... Is>
struct DefineType<M, N, std::index_sequence<Is...>>
{ using type = std::tuple<MyType<M, Is>...>; };
template <Integer M, Integer N>
std::enable_if_t<(N == 0), typename DefineType<M,0>::type>
MyClass(Param param)
{ return std::tuple<MyType<M, 0>>(MyType<M, 0>(param)); }
// general definition
template <Integer M, Integer N>
std::enable_if_t<(N > 0u), typename DefineType<M,N>::type>
MyClass(Param param)
{
return std::tuple_cat(
MyClass<M,N-1>(param),
std::tuple<MyType<M,N>>(MyType<M,N>(param)) );
}
int main ()
{
using t0 = typename DefineType<42u, 0u>::type;
using u0 = std::tuple<MyType<42u, 0u>>;
using t1 = typename DefineType<42u, 1u>::type;
using u1 = std::tuple<MyType<42u, 0u>, MyType<42u, 1u>>;
using t2 = typename DefineType<42u, 2u>::type;
using u2 = std::tuple<MyType<42u, 0u>, MyType<42u, 1u>, MyType<42u, 2u>>;
static_assert( std::is_same<t0, u0>::value, "!" );
static_assert( std::is_same<t1, u1>::value, "!" );
static_assert( std::is_same<t2, u2>::value, "!" );
auto const myobject = MyClass<42u, 2u>(12);
}
I would like to create a function that takes a variable number of template arguments. Later with these arguments the function should pass their position like this:
template<typename R, typename Args...>
R myFunction(Data &data, void *function) {
auto f = (R (*)(Args...))function;
return f(read<Args1>(data, 1), read<Args2>(data, 2), ...);// <-- This is the problem
}
The given code is of course not compilable. Is there any way to fix it? Is there a way to do it without variadic templates without too much code duplication?
Yes, that is possible:
// we need a compile-time helper to generate indices
template< std::size_t... Ns >
struct indices
{
typedef indices< Ns..., sizeof...( Ns ) > next;
};
template< std::size_t N >
struct make_indices
{
typedef typename make_indices< N - 1 >::type::next type;
};
template<>
struct make_indices< 0 >
{
typedef indices<> type;
};
With these helpers, you need one forwarder for your function like this:
template<typename R, typename... Args, std::size_t... Ns>
R myFunctionImpl(void *Data, void *function, indices<Ns...> ) {
auto f = (R (*)(Args...))function;
return f(read<Args>(Data, Ns + 1)...);// +1 because indices is zero-based
}
template<typename R, typename... Args>
R myFunction(void *Data, void *function) {
return myFunctionImpl< R, Args... >( Data, function, typename make_indices<sizeof...(Args)>::type() );
}
EDIT: How does it work? First, we determine the size of the argument pack Args through sizeof.... make_indices<N>::type then expands into indices<0,1,2,...,N-1>. It is given as an additional parameter to the implementation function (from the forwarder who just creates a dummy instance), hence argument deduction kicks in on the implementation function's side and puts the generated indices into the argument pack Ns.
The implementation function now has two argument packs with the same size, namely Args and Ns. When expanded through the ellipsis ..., the ellipsis expands the whole expression that it's applied to and it expands all parameter packs in parallel! In the above example that expression is read<Args>(Data, Ns+1), which nicely expands into the OPs pseudo-code.
EDIT: I use curry below, but have been informed this is instead partial application.
I've been trying to figure out how one would write a curry function in C++, and i actually figured it out!
#include <stdio.h>
#include <functional>
template< class Ret, class Arg1, class ...Args >
auto curry( Ret f(Arg1,Args...), Arg1 arg )
-> std::function< Ret(Args...) >
{
return [=]( Args ...args ) { return f( arg, args... ); };
}
And i wrote a version for lambdas, too.
template< class Ret, class Arg1, class ...Args >
auto curry( const std::function<Ret(Arg1,Args...)>& f, Arg1 arg )
-> std::function< Ret(Args...) >
{
return [=]( Args ...args ) { return f( arg, args... ); };
}
The tests:
int f( int x, int y )
{
return x + y;
}
int main()
{
auto f5 = curry( f, 5 );
auto g2 = curry( std::function<int(int,int)>([](int x, int y){ return x*y; }), 2 );
printf("%d\n",f5(3));
printf("%d\n",g2(3));
}
Yuck! The line initializing g2 is so large that i might as well have curried it manually.
auto g2 = [](int y){ return 2*y; };
Much shorter. But since the intent is to have a really generic and convenient curry function, could i either (1) write a better function or (2) somehow my lambda to implicitly construct an std::function? I fear the current version violates the rule of least surprise when f is not a free function. Especially annoying is how no make_function or similar-type function that i know of seems to exist. Really, my ideal solution would just be a call to std::bind, but i'm not sure how to use it with variadic templates.
PS: No boost, please, but i'll settle if nothing else.
EDIT: I already know about std::bind. I wouldn't be writing this function if std::bind did exactly what i wanted with the best syntax. This should be more of a special case where it only binds the first element.
As i said, my ideal solution should use bind, but if i wanted to use that, i'd use that.
Your curry function is just a scaled down inefficient subcase of std::bind (std::bind1st and bind2nd should not be used anymore now that we have std::result_of)
Your two lines read in fact
auto f5 = std::bind(f, 5, _1);
auto g2 = std::bind(std::multiplies<int>(), 2, _1);
after having used namespace std::placeholders. This carefully avoids the boxing into std::function and allows the compiler to inline more easily the result at the call site.
For functions of two arguments, hacking something like
auto bind1st(F&& f, T&& t)
-> decltype(std::bind(std::forward<F>(f), std::forward<T>(t), _1))
{
return std::bind(std::forward<F>(f), std::forward<T>(t), _1)
}
may work, but it is difficult to generalize to the variadic case (for which you'd end up rewriting a lot of the logic in std::bind).
Also currying is not partial application. Currying has "signature"
((a, b) -> c) -> (a -> b -> c)
ie. it is the action to transform a function taking two arguments into a function returning a function. It has an inverse uncurry performing the reverse operation (for mathematicians: curry and uncurry are isomorphisms, and define an adjunction). This inverse is very cumbersome to write in C++ (hint: use std::result_of).
This is a way to have currying in C++ and may or may not be relevant after the recent edits to the OP.
Due to overloading it is very problematic to inspect a functor and detect its arity. What is possible however is that given a functor f and an argument a, we can check if f(a) is a valid expression. If it isn't, we can store a and given a following argument b we can check if f(a, b) is a valid expression, and so on. To wit:
#include <utility>
#include <tuple>
/* Two SFINAE utilities */
template<typename>
struct void_ { using type = void; };
template<typename T>
using Void = typename void_<T>::type;
// std::result_of doesn't play well with SFINAE so we deliberately avoid it
// and roll our own
// For the sake of simplicity this result_of does not compute the same type
// as std::result_of (e.g. pointer to members)
template<typename Sig, typename Sfinae = void>
struct result_of {};
template<typename Functor, typename... Args>
struct result_of<
Functor(Args...)
, Void<decltype( std::declval<Functor>()(std::declval<Args>()...) )>
> {
using type = decltype( std::declval<Functor>()(std::declval<Args>()...) );
};
template<typename Functor, typename... Args>
using ResultOf = typename result_of<Sig>::type;
template<typename Functor, typename... Args>
class curry_type {
using tuple_type = std::tuple<Args...>;
public:
curry_type(Functor functor, tuple_type args)
: functor(std::forward<Functor>(functor))
, args(std::move(args))
{}
// Same policy as the wrappers from std::bind & others:
// the functor inherits the cv-qualifiers from the wrapper
// you might want to improve on that and inherit ref-qualifiers, too
template<typename Arg>
ResultOf<Functor&(Args..., Arg)>
operator()(Arg&& arg)
{
return invoke(functor, std::tuple_cat(std::move(args), std::forward_as_tuple(std::forward<Arg>(arg))));
}
// Implementation omitted for brevity -- same as above in any case
template<typename Arg>
ResultOf<Functor const&(Args..., Arg)>
operator()(Arg&& arg) const;
// Additional cv-qualified overloads omitted for brevity
// Fallback: keep calm and curry on
// the last ellipsis (...) means that this is a C-style vararg function
// this is a trick to make this overload (and others like it) least
// preferred when it comes to overload resolution
// the Rest pack is here to make for better diagnostics if a user erroenously
// attempts e.g. curry(f)(2, 3) instead of perhaps curry(f)(2)(3)
// note that it is possible to provide the same functionality without this hack
// (which I have no idea is actually permitted, all things considered)
// but requires further facilities (e.g. an is_callable trait)
template<typename Arg, typename... Rest>
curry_type<Functor, Args..., Arg>
operator()(Arg&& arg, Rest const&..., ...)
{
static_assert( sizeof...(Rest) == 0
, "Wrong usage: only pass up to one argument to a curried functor" );
return { std::forward<Functor>(functor), std::tuple_cat(std::move(args), std::forward_as_tuple(std::forward<Arg>(arg))) };
}
// Again, additional overloads omitted
// This is actually not part of the currying functionality
// but is here so that curry(f)() is equivalent of f() iff
// f has a nullary overload
template<typename F = Functor>
ResultOf<F&(Args...)>
operator()()
{
// This check if for sanity -- if I got it right no user can trigger it
// It *is* possible to emit a nice warning if a user attempts
// e.g. curry(f)(4)() but requires further overloads and SFINAE --
// left as an exercise to the reader
static_assert( sizeof...(Args) == 0, "How did you do that?" );
return invoke(functor, std::move(args));
}
// Additional cv-qualified overloads for the nullary case omitted for brevity
private:
Functor functor;
mutable tuple_type args;
template<typename F, typename Tuple, int... Indices>
ResultOf<F(typename std::tuple_element<Indices, Tuple>::type...)>
static invoke(F&& f, Tuple&& tuple, indices<Indices...>)
{
using std::get;
return std::forward<F>(f)(get<Indices>(std::forward<Tuple>(tuple))...);
}
template<typename F, typename Tuple>
static auto invoke(F&& f, Tuple&& tuple)
-> decltype( invoke(std::declval<F>(), std::declval<Tuple>(), indices_for<Tuple>()) )
{
return invoke(std::forward<F>(f), std::forward<Tuple>(tuple), indices_for<Tuple>());
}
};
template<typename Functor>
curry_type<Functor> curry(Functor&& functor)
{ return { std::forward<Functor>(functor), {} }; }
The above code compiles using a snapshot of GCC 4.8 (barring copy-and-paste errors), provided that there is an indices type and an indices_for utility. This question and its answer demonstrates the need and implementation of such things, where seq plays the role of indices and gens can be used to implement a (more convenient) indices_for.
Great care is taken in the above when it comes to value category and lifetime of (possible) temporaries. curry (and its accompanying type, which is an implementation detail) is designed to be as lightweight as possible while still making it very, very safe to use. In particular, usage such as:
foo a;
bar b;
auto f = [](foo a, bar b, baz c, int) { return quux(a, b, c); };
auto curried = curry(f);
auto pass = curried(a);
auto some = pass(b);
auto parameters = some(baz {});
auto result = parameters(0);
does not copy f, a or b; nor does it result in dangling references to temporaries. This all still holds true even if auto is substituted with auto&& (assuming quux is sane, but that's beyond the control of curry). It's still possible to come up with different policies in that regard (e.g. systematically decaying).
Note that parameters (but not the functor) are passed with the same value category in the final call as when they're passed to the curried wrapper. Hence in
auto functor = curry([](foo f, int) {});
auto curried = functor(foo {});
auto r0 = curried(0);
auto r1 = curried(1);
this means that a moved-from foo is passed to the underlying functor when computing r1.
With some C++14 features, partial application that works on lambda's can be implemented in a pretty concise way.
template<typename _function, typename _val>
auto partial( _function foo, _val v )
{
return
[foo, v](auto... rest)
{
return foo(v, rest...);
};
}
template< typename _function, typename _val1, typename... _valrest >
auto partial( _function foo, _val1 val, _valrest... valr )
{
return
[foo,val,valr...](auto... frest)
{
return partial(partial(foo, val), valr...)(frest...);
};
}
// partial application on lambda
int p1 = partial([](int i, int j){ return i-j; }, 6)(2);
int p2 = partial([](int i, int j){ return i-j; }, 6, 2)();
A lot of the examples people provided and that i saw elsewhere used helper classes to do whatever they did. I realized this becomes trivial to write when you do that!
#include <utility> // for declval
#include <array>
#include <cstdio>
using namespace std;
template< class F, class Arg >
struct PartialApplication
{
F f;
Arg arg;
constexpr PartialApplication( F&& f, Arg&& arg )
: f(forward<F>(f)), arg(forward<Arg>(arg))
{
}
/*
* The return type of F only gets deduced based on the number of arguments
* supplied. PartialApplication otherwise has no idea whether f takes 1 or 10 args.
*/
template< class ... Args >
constexpr auto operator() ( Args&& ...args )
-> decltype( f(arg,declval<Args>()...) )
{
return f( arg, forward<Args>(args)... );
}
};
template< class F, class A >
constexpr PartialApplication<F,A> partial( F&& f, A&& a )
{
return PartialApplication<F,A>( forward<F>(f), forward<A>(a) );
}
/* Recursively apply for multiple arguments. */
template< class F, class A, class B >
constexpr auto partial( F&& f, A&& a, B&& b )
-> decltype( partial(partial(declval<F>(),declval<A>()),
declval<B>()) )
{
return partial( partial(forward<F>(f),forward<A>(a)), forward<B>(b) );
}
/* Allow n-ary application. */
template< class F, class A, class B, class ...C >
constexpr auto partial( F&& f, A&& a, B&& b, C&& ...c )
-> decltype( partial(partial(declval<F>(),declval<A>()),
declval<B>(),declval<C>()...) )
{
return partial( partial(forward<F>(f),forward<A>(a)),
forward<B>(b), forward<C>(c)... );
}
int times(int x,int y) { return x*y; }
int main()
{
printf( "5 * 2 = %d\n", partial(times,5)(2) );
printf( "5 * 2 = %d\n", partial(times,5,2)() );
}
I am working on "LINQ to Objects" library for C++11.
I would like to do smth like this:
// filtering elements by their value
arr.where( [](double d){ return d < 0; } )
// filtering elements by their value and position
arr.where( [](double d, int i){ return i%2==0; } )
I down want to write arr.where_i( ... ) - it's ugly.
So i need function/method overloading by lambda-type...
This is my solution:
template<typename F>
auto my_magic_func(F f) -> decltype(f(1))
{
return f(1);
}
template<typename F>
auto my_magic_func(F f, void * fake = NULL) -> decltype(f(2,3))
{
return f(2,3);
}
int main()
{
auto x1 = my_magic_func([](int a){ return a+100; });
auto x2 = my_magic_func([](int a, int b){ return a*b; });
// x1 == 1+100
// x2 == 2*3
}
Is it SFINAE solution?
What can you suggest me?
Maybe something variadic:
#include <utility>
template <typename F, typename ...Args>
decltype(f(std::declval<Args>()...) my_magic_func(F f, Args &&... args)
{
return f(std::forward<Args>(args)...);
}
Edit: You can also use typename std::result_of<F(Args...)>::type for the return type, which does the same thing.
You certainly want SFINAE in your solution. Generally speaking, the result would look something like:
template<
typename Functor
, typename std::enable_if<
special_test<Functor>::value
, int
>::type = 0
>
return_type
my_magic_func(Functor f);
template<
typename Functor
, typename std::enable_if<
!special_test<Functor>::value
, int
>::type = 0
>
return_type
my_magic_func(Functor f);
such that only one overload would be active at any one time -- all that remains now is carefully crafting that special_test to have the behaviour we want. This is a careful balancing act as you don't want the test to be too specific; otherwise we lose generality. Quite a shame when writing generic code. You haven't given too much information (e.g. are you strictly interested in support for lambdas? monomorphic functors? polymorphic functors?), but I will assume for now that we have access to a value_type alias which would correspond to double in your example.
As such, here's an example condition that will check that a given type is Callable (that's a Standard concept) with signature bool(value_type); i.e. that it's a predicate of sorts:
template<typename Functor, typename ValueType>
struct is_unary_predicate {
typedef char (&accepted)[1];
typedef char (&refused)[2];
void consume(bool);
template<
typename X
, typename Y
, typename = decltype( consume(std::declval<X>()(std::declval<Y>())) )
>
accepted
test(X&&, Y&&);
refused test(...);
static constexpr bool value =
sizeof test(std::declval<Functor>(), std::declval<ValueType>())
== sizeof(accepted);
};
Personally I have an is_callable<F, Signature> trait so that I would only need to write something like template<typename Functor, typename ValueType> using is_unary_predicate = is_callable<Functor, bool(ValueType)>; (and similarly I could have an is_binary_predicate alias instead of letting the second overload of my_magic_func be a catch-all). Perhaps you'd want to use a similar trait for future uses of SFINAE (although it may be somewhat painful to write without variadic templates).