I am trying to define two overload for a template function.
First overload is generated if functor passed as an argument has no parameter otherwise second overload is generated.
I started to implement like this :
template <typename R, typename... Types>
constexpr size_t argumentCount(R (*f)(Types...))
{
return sizeof...(Types);
}
template <typename Function>
typename std::enable_if<argumentCount<Function>() == 0, int>::value = 0 > callFunc(Function fu)
{
fu();
}
template <typename Function, typename... Params>
typename std::enable_if<argumentCount<Function>() == 0, int>::value = 0 > callFunc(Function fu, Params... params)
{
fu(std::forward<decltype(params)>(params)...);
}
This does not compile for several reasons including parse error.What I want to do is,
callFunc([](){}); will call overload 1 while callFunc([](int value1,int value2){}); will call overload 2.
How can I achieve this ?
You can use the detection idiom with std::void_t and std::declval to detect such features.
template <typename FuncT>
using invocable_without_args_t = std::void_t<decltype(std::declval<FuncT>()())>;
More information is available here; this post helped me a lot to understand how this works.
If you can add a level of indirection... what about using tag-dispatching instead of SFINAE?
I mean, something as follows
#include <iostream>
#include <type_traits>
template <typename F>
void callFunc_helper (F fu, std::true_type)
{
std::cout << "zero version" << std::endl;
fu();
}
template <typename F, typename... Prs>
void callFunc_helper (F fu, std::false_type, Prs && ... params)
{
std::cout << "non zero version" << std::endl;
fu(std::forward<Prs>(params)...);
}
template <typename F, typename... Prs>
void callFunc (F fu, Prs && ... params)
{ callFunc_helper(fu, std::integral_constant<bool, 0u == sizeof...(Prs)>{},
std::forward<Prs>(params)...); }
int main ()
{
callFunc([]{});
callFunc([](int, int){}, 0, 1);
}
Obviously you can use std::integral_constant<bool, 0u == argumentCount<Function>()>{} if you really want to check the number of the functional arguments instead of the number of the following parameters (but why?).
Here is your version with a fixed syntax:
template <typename R, typename... Types>
constexpr size_t argumentCount(R (*f)(Types...))
{
return sizeof...(Types);
}
template <typename Function>
typename std::enable_if<argumentCount<Function>() == 0, int> callFunc(Function fu)
{
fu();
}
template <typename Function, typename... Params>
typename std::enable_if<argumentCount<Function>() == 0, int> callFunc(Function fu, Params... params)
{
fu(std::forward<decltype(params)>(params)...);
}
In this particular example you do not need an extra overload, Types... can be empty.
template <typename Function, typename... Params>
auto callFunc(Function fu, Params... params)
{
fu(std::forward<decltype(params)>(params)...);
}
works perfectly well.
If you need two template functions, just write them:
#include <iostream>
template <class R>
void callFunc(R (*f)()) {
std::cout << "Called no-arg template\n";
f();
}
template <class R, class T, class... Types, class... Params>
void callFunc(R (*f)(T, Types...), Params... params) {
std::cout << "Called multi-arg template\n";
f(params...);
}
void g() {
std::cout << "Called g\n";
}
void h(int) {
std::cout << "Called h\n";
}
int main() {
callFunc(g);
callFunc(h, 3);
return 0;
}
Output:
[temp]$ clang++ -std=c++11 test.cpp
[temp]$ ./a.out
Called no-arg template
Called g
Called multi-arg template
Called h
[temp]$
Related
I implemented a Visit function (on a variant) that checks that the currently active type in the variant matches the function signature (more precisely the first argument). Based on this nice answer.
For example
#include <variant>
#include <string>
#include <iostream>
template<typename Ret, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(*) (Arg, Rest...));
template<typename Ret, typename F, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(F::*) (Arg, Rest...));
template<typename Ret, typename F, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(F::*) (Arg, Rest...) const);
template <typename F>
decltype(first_argument_helper(&F::operator())) first_argument_helper(F);
template <typename T>
using first_argument = decltype(first_argument_helper(std::declval<T>()));
std::variant<int, std::string> data="abc";
template <typename V>
void Visit(V v){
using Arg1 = typename std::remove_const_t<std::remove_reference_t<first_argument<V>>>;//... TMP magic to get 1st argument of visitor + remove cvr, see Q 43526647
if (! std::holds_alternative<Arg1>(data)) {
std::cerr<< "alternative mismatch\n";
return;
}
v(std::get<Arg1>(data));
}
int main(){
Visit([](const int& i){std::cout << i << "\n"; });
Visit([](const std::string& s){std::cout << s << "\n"; });
// Visit([](auto& x){}); ugly kabooom
}
This works, but it explodes with a user unfriendly compile time error when users passes a generic (e.g. [](auto&){}) lambda. Is there a way to detect this and give nice static_assert() about it?
Would also be nice if it worked with function templates as well, not just with lambdas.
Note that I do not know what possible lambdas do, so I can not do some clever stuff with Dummy types since lambdas may invoke arbitrary functions on types.
In other words I can not try to call lambda in 2 std::void_t tests on int and std::string and if it works assume it is generic because they might try to call .BlaLol() on int and string.
Is there a way to detect this and give nice static_assert about it?
I suppose you can use SFINAE over operator() type.
Follows an example
#include <type_traits>
template <typename T>
constexpr auto foo (T const &)
-> decltype( &T::operator(), bool{} )
{ return true; }
constexpr bool foo (...)
{ return false; }
int main()
{
auto l1 = [](int){ return 0; };
auto l2 = [](auto){ return 0; };
static_assert( foo(l1), "!" );
static_assert( ! foo(l2), "!" );
}
Instead of a bool, you can return std::true_type (from foo() first version) or std::false_type (from second version) if you want to use it through decltype().
Would also be nice if it worked with function templates as well, not just with lambdas.
I don't think it's possible in a so simple way: a lambda (also a generic lambda) is an object; a template function isn't an object but a set of objects. You can pass an object to a function, not a set of objects.
But the preceding solution should works also for classes/structs with operator()s: when there is a single, non template, operator(), you should get 1 from foo(); otherwise (no operator(), more than one operator(), template operator()), foo() should return 0.
Yet another simpler option:
#include <type_traits>
...
template <typename V>
void Visit(V v) {
class Auto {};
static_assert(!std::is_invocable<V, Auto&>::value);
static_assert(!std::is_invocable<V, Auto*>::value);
...
}
The Auto class is just an invented type impossible to occur in the V parameters. If V accepts Auto as an argument it must be a generic.
I tested in coliru and I can confirm the solution covers these cases:
Visit([](auto x){}); // nice static assert
Visit([](auto *x){}); // nice static assert
Visit([](auto &x){}); // nice static assert
Visit([](auto &&x){}); // nice static assert
I'm not sure if that would cover all the possible lambdas that you don't know which are :)
#include <variant>
#include <string>
#include <iostream>
template <class U, typename T = void>
struct can_be_checked : public std::false_type {};
template <typename U>
struct can_be_checked<U, std::enable_if_t< std::is_function<U>::value > > : public std::true_type{};
template <typename U>
struct can_be_checked<U, std::void_t<decltype(&U::operator())>> : public std::true_type{};
template<typename Ret, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(*) (Arg, Rest...));
template<typename Ret, typename F, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(F::*) (Arg, Rest...));
template<typename Ret, typename F, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(F::*) (Arg, Rest...) const);
template <typename F>
decltype(first_argument_helper(&F::operator())) first_argument_helper(F);
template <typename T>
using first_argument = decltype(first_argument_helper(std::declval<T>()));
std::variant<int, std::string> data="abc";
template <typename V>
void Visit(V v){
if constexpr ( can_be_checked<std::remove_pointer_t<decltype(v)>>::value )
{
using Arg1 = typename std::remove_const_t<std::remove_reference_t<first_argument<V>>>;//... TMP magic to get 1st argument of visitor + remove cvr, see Q 43526647
if (! std::holds_alternative<Arg1>(data))
{
std::cerr<< "alternative mismatch\n";
return;
}
v(std::get<Arg1>(data));
}
else
{
std::cout << "it's a template / auto lambda " << std::endl;
}
}
template <class T>
void foo(const T& t)
{
std::cout <<t << " foo \n";
}
void fooi(const int& t)
{
std::cout <<t << " fooi " << std::endl;
}
int main(){
Visit([](const int& i){std::cout << i << std::endl; });
Visit([](const std::string& s){std::cout << s << std::endl; });
Visit([](auto& x){std::cout <<x << std::endl;}); // it's a template / auto lambda*/
Visit(foo<int>);
Visit<decltype(fooi)>(fooi);
Visit(fooi);
// Visit(foo); // => fail ugly
}
I don't know if it's you want, but you can, with that static_assert if an auto lambda is passed as parameter.
I think it's not possible to do the same for template function, but not sure.
Background
I'm trying to write some template functions for a template-only unit test library, specifically for Qt.
Problem
In this library, I have a variadic template that receives a variable amount of objects and functors (Qt5 Signals actually), always paired next to each other, as in QObject, signal, etc... then desirably followed by a variable amount of signal arguments.
Desired Solution
// implementation.h
template <typename T, typename U, typename... Sargs, typename... Fargs>
void test_signal_daisy_chain(T* t, void(T::*t_signal)(Fargs...),
U* u, void(U::*u_signal)(Fargs...),
Sargs... sargs,
Fargs... fargs) {...}
// client.cpp
test_signal_daisy_chain(object, &Object::signal1,
object, &Object::signal2,
object, &Object::signal3,
1, 2, 3); // where the signals are defined as void(Object::*)(int, int, int)
Where Fargs... corresponds to both the parameters in t_signal and u_signal as well as the arguments to pass to this function for testing, and Sargs... corresponds to a variable amount of QObject and signal member functions (void(T::*)(Fargs...)) to emit for the express purpose of testing.
Unsurprisingly I get "no matching function" due to "template argument deduction/substitution failed", and my ClangCodeModel plugin warns me that 6 arguments were expected, where 8 were given.
Working (ugly) solution
// implementation.h
template <typename... Fargs>
struct wrapper
{
template <typename T, typename U, typename... Sargs>
void test_signal_daisy_chain(Fargs... fargs,
T* t, void(T::*t_signal)(Fargs...),
U* u, void(U::*u_signal)(Fargs...),
Sargs... sargs) {...}
// client.cpp
wrapper<int, int, int>::test_signal_daisy_chain(1, 2, 3,
object, &Object::signal1,
object, &Object::signal2,
object, &Object::signal3);
I'm not content with having to explicitly define the variable function arguments at both the beginning of the function call and in the wrapper template type parameters. In fact, I was initially surprised that the could not be deduced simply by the fact that they were to match the variable arguments of the functors. I'm open to using wrapper functions as opposed to wrapper classes, as I already have a detail namespace set up which I'm willing to get messy for in order to provide a clean and user-friendly API.
Note: signal arguments can be anywhere from primitives to user-defined types to POD structs to template classes, all of variable length.
Edit 1: c++11 is a hard requirement so you can leave >c++11 features in your answer as long as they have some c++11 workaround, i.e. auto... is easy to fix, auto myFunction = []() constexpr {...}; much less so. If using if constexpr instead of a recursive template <std::size_t> helper function saves space and provides for a more succinct, complete, and future-proof answer, then please opt for whichever standard you deem best.
The simplest approach is to pack the parameters into a tuple at the beginning, and pass the tuple to test_signal_daisy_chain_impl:
template < typename... Fargs,
typename T, typename... Sargs>
void test_signal_daisy_chain_impl(const std::tuple<Fargs...> & fargs,
T* t, void(T::*t_signal)(Fargs...),
Sargs &&... sargs)
{
// apply unpacks the tuple
std::apply([&](auto ...params)
{
(t->*t_signal)(params...);
}, fargs);
// Although packed into the tuple, the elements in
// the tuple were not removed from the parameter list,
// so we have to ignore a tail of the size of Fargs.
if constexpr (sizeof...(Sargs) > sizeof...(Fargs))
test_signal_daisy_chain_impl(fargs, std::forward<Sargs>(sargs)...);
}
// Get a tuple out of the last I parameters
template <std::size_t I, typename Ret, typename T, typename... Qargs>
Ret get_last_n(T && t, Qargs && ...qargs)
{
static_assert(I <= sizeof...(Qargs) + 1,
"Not enough parameters to pass to the signal function");
if constexpr(sizeof...(Qargs)+1 == I)
return {std::forward<T>(t), std::forward<Qargs>(qargs)...};
else
return get_last_n<I, Ret>(std::forward<Qargs>(qargs)...);
}
template <typename T, typename... Fargs,
typename... Qargs>
void test_signal_daisy_chain(T* t, void(T::*t_signal)(Fargs...),
Qargs&&... qargs)
{
static_assert((sizeof...(Qargs) - sizeof...(Fargs)) % 2 == 0,
"Expecting even number of parameters for object-signal pairs");
if constexpr ((sizeof...(Qargs) - sizeof...(Fargs)) % 2 == 0) {
auto fargs = get_last_n<sizeof...(Fargs), std::tuple<Fargs...>>(
std::forward<Qargs>(qargs)...);
test_signal_daisy_chain_impl(fargs, t, t_signal,
std::forward<Qargs>(qargs)...);
}
}
And the usage:
class Object {
public:
void print_vec(const std::vector<int> & vec)
{
for (auto elem: vec) std::cout << elem << ", ";
}
void signal1(const std::vector<int> & vec)
{
std::cout << "signal1(";
print_vec(vec);
std::cout << ")\n";
}
void signal2(const std::vector<int> & vec)
{
std::cout << "signal2(";
print_vec(vec);
std::cout << ")\n";
}
void signal_int1(int a, int b)
{ std::cout << "signal_int1(" << a << ", " << b << ")\n"; }
void signal_int2(int a, int b)
{ std::cout << "signal_int2(" << a << ", " << b << ")\n"; }
void signal_int3(int a, int b)
{ std::cout << "signal_int3(" << a << ", " << b << ")\n"; }
};
int main()
{
Object object;
test_signal_daisy_chain(&object, &Object::signal1,
&object, &Object::signal2 ,
std::vector{1,2,3});
test_signal_daisy_chain(&object, &Object::signal_int1,
&object, &Object::signal_int2 ,
&object, &Object::signal_int3,
1,2);
}
Edit 1
Since C++11 is a hard constraint, there is a much uglier solution, based on the same principles. Things like std::apply and std::make_index_sequence have to be implemented. Overloading is used instead of if constexpr(....) :
template <std::size_t ...I>
struct indexes
{
using type = indexes;
};
template<std::size_t N, std::size_t ...I>
struct make_indexes
{
using type_aux = typename std::conditional<
(N == sizeof...(I)),
indexes<I...>,
make_indexes<N, I..., sizeof...(I)>>::type;
using type = typename type_aux::type;
};
template <typename Tuple, typename T, typename Method, std::size_t... I>
void apply_method_impl(
Method t_signal, T* t, const Tuple& tup, indexes<I...>)
{
return (t->*t_signal)(std::get<I>(tup)...);
}
template <typename Tuple, typename T, typename Method>
void apply_method(const Tuple & tup, T* t, Method t_signal)
{
apply_method_impl(
t_signal, t, tup,
typename make_indexes<
std::tuple_size<Tuple>::value>::type{});
}
template < typename... Fargs, typename... Sargs>
typename std::enable_if<(sizeof...(Fargs) == sizeof...(Sargs)), void>::type
test_signal_daisy_chain_impl(const std::tuple<Fargs...> & ,
Sargs &&...)
{}
template < typename... Fargs,
typename T, typename... Sargs>
void test_signal_daisy_chain_impl(const std::tuple<Fargs...> & fargs,
T* t, void(T::*t_signal)(Fargs...),
Sargs &&... sargs)
{
apply_method(fargs, t, t_signal);
// Although packed into the tuple, the elements in
// the tuple were not removed from the parameter list,
// so we have to ignore a tail of the size of Fargs.
test_signal_daisy_chain_impl(fargs, std::forward<Sargs>(sargs)...);
}
// Get a tuple out of the last I parameters
template <std::size_t I, typename Ret, typename T, typename... Qargs>
typename std::enable_if<sizeof...(Qargs)+1 == I, Ret>::type
get_last_n(T && t, Qargs && ...qargs)
{
return Ret{std::forward<T>(t), std::forward<Qargs>(qargs)...};
}
template <std::size_t I, typename Ret, typename T, typename... Qargs>
typename std::enable_if<sizeof...(Qargs)+1 != I, Ret>::type
get_last_n(T && , Qargs && ...qargs)
{
static_assert(I <= sizeof...(Qargs) + 1, "Not enough parameters to pass to the singal function");
return get_last_n<I, Ret>(std::forward<Qargs>(qargs)...);
}
template <typename T, typename... Fargs,
typename... Qargs>
void test_signal_daisy_chain(T* t, void(T::*t_signal)(Fargs...),
Qargs&&... qargs)
{
static_assert((sizeof...(Qargs) - sizeof...(Fargs)) % 2 == 0,
"Expecting even number of parameters for object-signal pairs");
auto fargs = get_last_n<sizeof...(Fargs), std::tuple<Fargs...>>(
std::forward<Qargs>(qargs)...);
test_signal_daisy_chain_impl(fargs, t, t_signal,
std::forward<Qargs>(qargs)...);
}
Edit 2
It is possible to avoid runtime recursion by storing all parameters in a tuple. The following test_signal_daisy_chain_flat() does exactly that, while retaining the same interface as test_signal_daisy_chain():
template <typename Fargs, typename Pairs, std::size_t ...I>
void apply_pairs(Fargs && fargs, Pairs && pairs, const indexes<I...> &)
{
int dummy[] = {
(apply_method(std::forward<Fargs>(fargs),
std::get<I*2>(pairs),
std::get<I*2+1>(pairs)),
0)...
};
(void)dummy;
}
template <typename T, typename... Fargs,
typename... Qargs>
void test_signal_daisy_chain_flat(T* t, void(T::*t_signal)(Fargs...),
Qargs&&... qargs)
{
static_assert((sizeof...(Qargs) - sizeof...(Fargs)) % 2 == 0,
"Expecting even number of parameters for object-signal pairs");
auto fargs = get_last_n<sizeof...(Fargs), std::tuple<Fargs...>>(
std::forward<Qargs>(qargs)...);
std::tuple<T*, void(T::*)(Fargs...), const Qargs&...> pairs{
t, t_signal, qargs...};
apply_pairs(fargs, pairs,
typename make_indexes<(sizeof...(Qargs) - sizeof...(Fargs))/2>
::type{});
}
Caveats:
Not asserting that parameter pairs match. The compiler simply fails to compile (possibly deep in recursion).
The types of the parameters passed to the function are deduced from the signature of the first function, regardless of the types of the trailing parameters - the trailing parameters are converted to the required types.
All functions are required to have the same signature.
template<class T>
struct tag_t { using type=T; };
template<class Tag>
using type_t = typename Tag::type;
template<class T>
using no_deduction = type_t<tag_t<T>>;
template <typename T, typename U, typename... Sargs, typename... Fargs>
void test_signal_daisy_chain(
T* t, void(T::*t_signal)(Sargs...),
U* u, void(U::*u_signal)(Fargs...),
no_deduction<Sargs>... sargs,
no_deduction<Fargs>... fargs)
I assume the Fargs... in t_signal was a typo, and was supposed to be Sargs.
If not, you are in trouble. There is no rule that "earlier deduction beats later deduction".
One thing you can do in c++14 is to have a function returning a function object:
template <typename T, typename U, typename... Fargs>
auto test_signal_daisy_chain(
T* t, void(T::*t_signal)(Fargs...),
U* u, void(U::*u_signal)(Fargs...),
no_deduction<Fargs>... fargs
) {
return [=](auto...sargs) {
// ...
};
}
Then use looks like:
A a; B b;
test_signal_daisy_chain( &a, &A::foo, &b, &B::bar, 1 )('a', 'b', 'c');
doing this in c++11 is possible with a manually written function object.
Consider this code:
#include <iostream>
#include <type_traits>
template <std::size_t N> void bar() { std::cout << "bar<" << N << ">() called.\n"; }
template <std::size_t N> void hit() { std::cout << "hit<" << N << ">() called.\n"; }
template <typename T> struct evaluate : std::bool_constant<std::is_integral_v<T>> {
static constexpr std::size_t size = sizeof(T); // Simplified for illustration only.
};
void foo() { }
template <typename T, typename... Args>
std::enable_if_t<!evaluate<T>::value> foo (const T&, const Args&...);
template <typename T, typename... Args>
std::enable_if_t<evaluate<T>::value> foo (const T&, const Args&... args) {
bar<evaluate<T>::size>();
// Do whatever.
foo(args...);
}
template <typename T, typename... Args>
std::enable_if_t<!evaluate<T>::value> foo (const T&, const Args&... args) {
hit<evaluate<T>::size>();
// Do whatever, but different from the previous foo overload.
foo(args...);
}
int main() {
foo (5, "hello", true);
}
Output:
bar<4>() called.
hit<6>() called.
bar<1>() called.
How to rewrite the above so that evaluate<T> needs only be computed once instead of twice with each foo iteration?
You maybe like this one:
template <std::size_t N> void bar() { std::cout << "bar<" << N << ">() called.\n"; }
template <std::size_t N> void hit() { std::cout << "hit<" << N << ">() called.\n"; }
template <typename T>
struct evaluate : std::bool_constant<std::is_integral_v<T>>
{
static constexpr std::size_t size = sizeof(T); // Simplified for illustration only.
};
void foo() { }
template <typename T, typename... Args>
void foo( const T&, const Args&... args)
{
using X = evaluate<T>;
if constexpr ( X::value )
{
bar<X::size>();
}
else
{
hit<X::size>();
}
foo( args... );
}
int main() {
foo (5, "hello", true);
}
It "calls" only once evaluate<T>, which is not important but maybe easier to read. That all the template code is only used during instantiation makes it only a matter of taste.
As you mention c++17 you can use constexpr if to get rid of SFINAE at all in your example. This makes it also possible to reuse common lines of code in both variants of foo which is quite nice. The executable will not be much different you can believe, but the maintainability is much better I think!
Ok, I thought evaluate was computed twice. But how would you make it appear only once (without using macros)? We are supposed to avoid repetition in code anyways
You can try to save it as a additional template parameter with default value
Something as
template <typename T, typename... Args, typename E = evaluate<T>>
std::enable_if_t<!E::value> foo (const T&, const Args&...);
template <typename T, typename... Args, typename E = evaluate<T>>
std::enable_if_t<E::value> foo (const T&, const Args&... args)
{
bar<E::size>();
// Do whatever.
foo(args...);
}
template <typename T, typename ... Args, typename E>
std::enable_if_t<!E::value> foo (const T&, const Args&... args)
{
hit<E::size>();
// Do whatever, but different from the previous foo overload.
foo(args...);
}
I'd like to define functions for integral types, string and other types.
I can write:
template<typename T, typename = std::enable_if<std::is_integral<T>::value>::type>
void foo();
template<typename T, typename = std::enable_if<std::is_same<std::string>::value>::type>
void foo();
But how I can define function that will be called in other cases (if T not integral type and not std::string)?
I'm pretty sure that writing something like the line below becomes quite annoying and error-prone when you want to write up to N sfinae'd versions of foo:
std::enable_if<!std::is_integral<T>::value && !std::is_same<T, std::string>::value>::type
To avoid it, you can use the choice trick (a simple way to exploit overload resolution actually).
It follows a minimal, working example:
#include <iostream>
#include <utility>
#include <string>
template<int N> struct Choice: Choice<N-1> {};
template<> struct Choice<0> {};
template<typename T, typename... Args>
std::enable_if_t<std::is_integral<T>::value>
bar(Choice<2>, Args&&...) { std::cout << "integral" << std::endl; }
template<typename T, typename... Args>
std::enable_if_t<std::is_same<T, std::string>::value>
bar(Choice<1>, Args&&...) { std::cout << "string" << std::endl; }
template<typename T, typename... Args>
void bar(Choice<0>, Args&&...) { std::cout << "whatever" << std::endl; }
template<typename T, typename... Args>
void foo(Args&&... args) { bar<T>(Choice<100>{}, std::forward<Args>(args)...); }
int main() {
foo<bool>("foo");
foo<std::string>(42);
foo<void>(.0, "bar");
}
It handles nicely also the arguments that are directly forwarded to the right function once it has been picked up from the set.
The basic idea is that it tries to use all the versions of your function in the order you specified, from N to 0. This has also the advantage that you can set a priority level to a function when two of them match the template parameter T with their sfinae expressions.
The sfinae expressions enable or disable the i-th choice and you can easily define a fallback by using the Choice<0> tag (that is far easier to write than std::enable_if<!std::is_integral<T>::value && !std::is_same<T, std::string>::value>::type).
The drawback (even though I wouldn't consider it a drawback) is that it requires an extra function that simply forwards the arguments to the chain by appending them to the Choice<N> tag.
See it up and running on Coliru.
Your examples do not compile at all for many reasons. See proper SFINAE in the code below. This is only one of many possible ways to do it.
You can just negate all special conditions simultaneously. For example:
template<typename T, std::enable_if_t<std::is_integral<T>::value>* = nullptr>
void foo() { std::cout << "Integral\n"; }
template<typename T, std::enable_if_t<std::is_same<T, std::string>::value>* = nullptr>
void foo() { std::cout << "Str\n"; }
template<typename T, std::enable_if_t<!std::is_integral<T>::value && !std::is_same<T, std::string>::value>* = nullptr>
void foo() { std::cout << "Something else\n"; }
int main(void)
{
foo<int>();
foo<std::string>();
foo<float>();
return 0;
}
prints:
Integral
Str
Something else
Note that you may get automatic overload resolution if your functions take template-dependent arguments. SFINAE will look a bit different in this case:
template<typename T>
typename std::enable_if<std::is_integral<T>::value>::type foo(const T&) { std::cout << "Integral\n"; }
template<typename T>
typename std::enable_if<std::is_same<T, std::string>::value>::type foo(const T&) { std::cout << "Str\n"; }
template<typename T>
typename std::enable_if<!std::is_integral<T>::value && !std::is_same<T, std::string>::value>::type
foo(const T&) { std::cout << "Something else\n"; }
Usage:
foo(1);
foo(std::string("fdsf"));
foo(1.1f);
Finally, in the last case std::string overload may be on-template function:
template<typename T>
typename std::enable_if<std::is_integral<T>::value>::type foo(const T&) { std::cout << "Integral\n"; }
template<typename T>
typename std::enable_if<!std::is_integral<T>::value && !std::is_same<T, std::string>::value>::type
foo(const T&) { std::cout << "Something else\n"; }
void foo(const std::string&) { std::cout << "Str\n"; }
#Jarod42 was right.
I chose the wrong way to use SFINAE.
Actually, I had to use std::enable_if in return type declaration. And in order to define 'default' function (for all other types) I just need to define function with the same name and with ... as input parameters.
template<typename T>
std::enable_if_t<std::is_integral<T>::value>
foo() { std::cout << "integral"; }
template<typename T>
std::enable_if_t<std::is_same<T, std::string>::value>
foo() { std::cout << "string"; }
template<typename T>
void foo(...) { std::cout << "other"; }
I have a template class that must perform some operation before calling a function whose parameters and return type are generic.
This is the method:
template <typename ReturnType, typename ...Args>
ReturnType function (Args ...args) {
// prepare for call
// ...
ReturnType rv = makeCall(args...); // [1]
// dismiss the call
// ...
return rv;
}
Of course it's compiling correctly when ReturnType is not void.
When I use it in this context:
function<void>(firstArg, secondArg);
The compiler responds with
error: return-statement with a value, in function returning 'void' [-fpermissive]
pointing to the line marked with [1].
Is there any solution other than passing -fpermissive to the compiler?
I would prefer to have a unique method, because I possible solution I found is to instantiate different versions using enable_if and is_same.
Thank you in advance.
-- Update --
This is a complete example. I should have said that our functions are indeed class methods.
#include <type_traits>
#include <iostream>
class Caller {
public:
Caller() {}
template <typename ReturnType, typename ...Arguments>
ReturnType call(Arguments ... args) {
prepare();
ReturnType rv = callImpl<ReturnType>(args...);
done();
return rv;
}
private:
void prepare() {
std::cout << "Prepare\n";
}
void done() {
std::cout << "Done\n";
}
template <typename ReturnType, typename ...Arguments>
typename std::enable_if<std::is_same<ReturnType, void>::value, ReturnType>::type callImpl ( Arguments ... args) {
std::cout << "Calling with void\n";
return;
}
template <typename ReturnType, typename ...Arguments>
typename std::enable_if<std::is_same<ReturnType, bool>::value, ReturnType>::type callImpl (Arguments ... args) {
std::cout << "Calling with bool\n";
return true;
}
template <typename ReturnType, typename ...Arguments>
typename std::enable_if<std::is_same<ReturnType, int>::value, ReturnType>::type callImpl (Arguments ... args) {
std::cout << "Calling with int\n";
return 42;
}
};
int main(int argc, char *argv[]) {
Caller c;
auto rbool = c.call<bool> (1,20);
std::cout << "Return: " << rbool << "\n";
auto rint = c.call<int> (1,20);
std::cout << "Return: " << rint << "\n";
// the next line fails compilation. compile with --std=c++11
c.call<void>("abababa");
return 0;
}
-- Update --
Not a big issue: Use std::bind(&Caller::callImpl<ReturnType>, this, args).
Here's my attempt at a general C++11-compliant solution that you can easily reuse.
Let's start by creating a simple type trait that converts void to an empty struct. This doesn't introduce any code repetition.
struct nothing { };
template <typename T>
struct void_to_nothing
{
using type = T;
};
template <>
struct void_to_nothing<void>
{
using type = nothing;
};
template <typename T>
using void_to_nothing_t = typename void_to_nothing<T>::type;
We also need a way to call an arbitrary function converting an eventual void return type to nothing:
template <typename TReturn>
struct helper
{
template <typename TF, typename... Ts>
TReturn operator()(TF&& f, Ts&&... xs) const
{
return std::forward<TF>(f)(std::forward<Ts>(xs)...);
}
};
template <>
struct helper<void>
{
template <typename TF, typename... Ts>
nothing operator()(TF&& f, Ts&&... xs) const
{
std::forward<TF>(f)(std::forward<Ts>(xs)...);
return nothing{};
}
};
template <typename TF, typename... Ts>
auto with_void_to_nothing(TF&& f, Ts&&... xs)
-> void_to_nothing_t<
decltype(std::forward<TF>(f)(std::forward<Ts>(xs)...))>
{
using return_type =
decltype(std::forward<TF>(f)(std::forward<Ts>(xs)...));
return helper<return_type>{}(std::forward<TF>(f), std::forward<Ts>(xs)...);
}
Usage:
template <typename ReturnType, typename ...Args>
void_to_nothing_t<ReturnType> function (Args ...args) {
// prepare for call
// ...
auto rv = with_void_to_nothing(makeCall, args...); // [1]
// dismiss the call
// ...
return rv;
}
live wandbox example
There's a proposal by Matt Calabrese called "Regular Void" that would solve this issue. You can find it here: "P0146R1".
Depending on what you wish to accomplish in the lines
// dismiss the call
you might be able to use:
template <typename ReturnType, typename ...Args>
ReturnType function (Args ...args) {
// prepare for call
// ...
CallDismisser c;
return makeCall(args...); // [1]
}
That would work as long as the destructor of CallDismisser can do everything you need to do.
struct nothing {};
template<class Sig>
using returns_void = std::is_same< std::result_of_t<Sig>, void >;
template<class Sig>
using enable_void_wrap = std::enable_if_t< returns_void<Sig>{}, nothing >;
template<class Sig>
using disable_void_wrap = std::enable_if_t< !returns_void<Sig>{}, std::result_of_t<Sig> >;
template<class F>
auto wrapped_invoker( F&& f ) {
return overload(
[&](auto&&...args)->enable_void_wrap<F(decltype(args)...)> {
std::forward<F>(f)(decltype(args)(args)...);
return {};
},
[&](auto&&...args)->disable_void_wrap<F(decltype(args)...)> {
return std::forward<F>(f)(decltype(args)(args)...);
}
);
}
so wrapped_invoker takes a function object, and makes it return nothing instead of void.
Next, holder:
template<class T>
struct holder {
T t;
T&& get()&& { return std::forward<T>(t); }
};
template<>
struct holder<void> {
template<class T>
holder(T&&) {} // discard
void get()&& {}
};
holder lets you hold the return value and convert back to void if needed. You must create holder<T> using {} to get reference lifetime extension to work properly. Adding a ctor to holder<T> will break it.
holder<void> silently discards anything passed to it.
template <typename ReturnType, typename ...Args>
ReturnType function (Args ...args) {
// prepare for call
// ...
holder<ReturnType> rv{ wrapped_invoker(makeCall)(args...) };
// dismiss the call
// ...
return std::move(rv).get();
}
Now, holder<ReturnType> holds either nothing or the return value of makeCall(args...).
If it holds nothing, rv.get() returns void, and it is legal to return void to a function where ReturnValue is void.
Basically we are doing two tricks. First, we are preventing makeCall from returning void, and second if we are returning void we are discarding the return value of makeCall conditionally.
overload isn't written here, but it is a function that takes 1 or more function objects (such as lambdas) and returns their overload set. There is a proposal for std::overload, and a myriad of examples on stackoverflow itself.
Here is some:
Overloaded lambdas in C++ and differences between clang and gcc
C++11 “overloaded lambda” with variadic template and variable capture
The problem seems to be with //Dismiss the call.
This code shouldn't exist. That's what we have RAII for. The following code does work, even with ReturnType = void.
template <typename ReturnType, typename ...Arguments>
ReturnType call(Arguments ... args) {
Context cx;
return callImpl<ReturnType>(args...);
}
Context::Context() { std::cout << "prepare\n"; }
Context::~Context() { std::cout << "done\n"; }