I require constexpr if in some part of my templated codebase, but since it is not available in C++11, I decided to come up with my own version of a simpler constexpr if-then-else.
Below is my implementation of constexpr if-then-else. I am not entirely sure if it is correct, and was unable to find any relevant content anywhere which suitably explains it. It would be really helpful if someone could verify this, and/or possibly point out alternative implementations.
template <typename T, typename F>
constexpr F static_ite(std::false_type, T &&, F &&f) { return f; }
template <typename T, typename F>
constexpr T static_ite(std::true_type, T &&t, F &&) { return t; }
template <bool cond, typename T, typename F>
constexpr auto static_ite(T &&t, F &&f)
-> decltype(static_ite(std::integral_constant<bool, cond>{}, std::forward<T>(t), std::forward<F>(f)))
{
return static_ite(std::integral_constant<bool, cond>{}, std::forward<T>(t), std::forward<F>(f));
}
I intend to use it as a generic template. Any help would be appreciated.
template <typename T, typename F>
constexpr typename std::decay<F>::type static_ite(std::false_type, T &&, F &&f) { return std::forward<F>(f); }
and similar for other branch. References can be passed through explicitly with std ref or pointers.
I find a more generic dispatch to be also useful:
template<std::size_t N, class...Ts>
nth_type<N,typename std::decay<Ts>::type...>
dispatch_nth(index_t<N>, Ts&&...ts);
(write obvious helpers).
This lets you work on more than 2 branches.
All of these become insanely more awesome with auto lambda paramerers; while that is c++14 it was implemented in most early c++1y early implementations.
I'm going to assume you want the function to return a reference to whatever you give it; if you instead want a copy, refer to Yakk's answer.
The return types of the first two overloads should be rvalue references, and you should std::forward when you return from them.
The long decltype could be shortened to typename std::conditional<cond, T &&, F &&>::type.
Everything else looks good to me.
The issue with this interface is that you are evaluating both sides of the conditional, so both sides need to be valid regardless of the value of the predicate. Because of this, it is more usual to pass in lambdas one of which will be invoked within the function. In addition, you may wish to pass in extra arguments so that when an expression is type-dependent you can ensure the type is only evaluated conditionally (though to use this effectively you may need C++14 generic lambdas).
Altogether, something like this:
template <bool cond, typename T, typename F, class... Args>
constexpr auto static_ite(T &&t, F &&f, Args&&... args)
-> typename std::result_of<typename std::conditional<cond, T, F>::type&&(Args&&...)>::type
{
return std::forward<typename std::conditional<cond, T, F>::type>(
std::get<cond ? 0 : 1>(std::make_tuple(std::ref(t), std::ref(f)))(
std::forward<Args>(args)...);
}
Note that this uses the std::tuple trick to bring everything within one function body; you can use your method of std::true_type / std::false_type overloading if you prefer.
Related
Background: I have a function that I wrote that takes some callable object and a tuple, and unpacks the tuple into the a parameter pack that can be passed to the function. You can probably imagine that this involves a bit of template recursion. However, most of my colleagues aren't native speakers of C++, so they understandably have trouble with the code, as well as the compilation errors they get when the function is called incorrectly.
If I could just generate a pack of size_t from 0 to the size of the tuple - 1, I could use the following one-liner:
template <class Function, class... TupleArgs>
auto UnpackTuple(Function fn, std::tuple<TupleArgs>&& t)
-> decltype(fn(std::declval<TupleArgs>()...)) {
return fn(std::get<{my size_t pack}>(std::forward(t))...);
}
Is there anything I can substitute for "my size_t pack" to make this work? I know I could hack something together if I new that each type in TupleArgs was unique, but that's a very specific case that isn't useful to me.
Seems like you are looking for std::apply, which will do exactly what you want to do with UnpackTuple.
That being said, if you need to do this manually for some odd reason (you probably shouldn't, unless you want to do something entirely different from calling a function),
std::make_index_sequence does almost what you want (except that your size_t sequence is wrapped in a type, so you need an additional level of indirection). You can see how it is used in the example implementation of std::apply at cppreference.com:1)
template <class F, class Tuple, std::size_t... I>
constexpr decltype(auto) apply_impl(F&& f, Tuple&& t, std::index_sequence<I...>)
{
return std::invoke(std::forward<F>(f), std::get<I>(std::forward<Tuple>(t))...);
}
template <class F, class Tuple>
constexpr decltype(auto) apply(F&& f, Tuple&& t)
{
return apply_impl(
std::forward<F>(f),
std::forward<Tuple>(t),
std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<Tuple>>>{}
);
}
Since you mentioned that you are still on C++14, you'll need to replace std::invoke with a normal function call (keep the std::forward, however, in case someone is mad enough to implement a operator() &&)
1) (CC-BY-SA 3.0)
Using std::function, we can get the type of an argument using the argument_type, second_argument_type etc. typedefs, but I can't see a way to do the same thing with lambdas. Is it possible? (I'm using VS2010)
Say I want something like the following in my deserialization system used to read an object and pass it to a setter function:
template<typename F>
static void forward(F f)
{
// Create an object of the type of the first
// parameter to the function object F
typedef typename F::argument_type T;
T t;
//...do something with 't' here (deserialize in my case)
// Forward the object to the function
f(t);
}
It can be used like this and everything works fine:
std::function<void(int)> f = [](int i) -> void { setValue(i); };
forward(f);
But it will not work directly with lambdas:
forward([](int i) -> void { setValue(i); });
//error C2039: 'argument_type' : is not a
//member of '`anonymous-namespace'::<lambda1>'
Is there a way to access the parameter types in a way that will work for both lambdas and std::function objects? Maybe a way to get the std::function type of a lambda first, and then the argument_type from that?
Following on from the answer below, a version that works with lambdas and std::function is:
template<typename T, typename F>
static void forward(F f)
{
T t;
//...do something with 't' here (deserialize in my case)
f(t);
}
forward<int>([](int i) -> void { setValue(i); });
Since int is repeated here I was hoping to get rid of it - not so bad for int but more annoying for long-named types in a couple of namespaces. C'est la vie!
It's not desirable in the general case. (Note that it's quite easy for std::function<T(A)> to specify what e.g. argument_type is: it's just A! It's available in the type definition.)
It would be possible to require each and every function object type to specify its argument types, and in turn mandate that the closure types generated from lambda expression do so. In fact, pre-C++0x features like adaptable functors would only work for such types.
However, we're moving from that with C++0x and for good reasons. The simplest of which is simply overloading: a functor type with a templated operator() (a.k.a a polymorphic functor) simply takes all kind of arguments; so what should argument_type be? Another reason is that generic code (usually) attempts to specify the least constraints on the types and objects it operates on in order to more easily be (re)used.
In other words, generic code is not really interested that given Functor f, typename Functor::argument be int. It's much more interesting to know that f(0) is an acceptable expression. For this C++0x gives tools such as decltype and std::declval (conveniently packaging the two inside std::result_of).
The way I see it you have two choices: require that all functors passed to your template use a C++03-style convention of specifying an argument_type and the like; use the technique below; or redesign. I'd recommend the last option but it's your call since I don't know what your codebase looks like or what your requirements are.
For a monomorphic functor type (i.e. no overloading), it is possible to inspect the operator() member. This works for the closure types of lambda expressions.
So we declare these helpers
template<typename F, typename Ret, typename A, typename... Rest>
A
helper(Ret (F::*)(A, Rest...));
template<typename F, typename Ret, typename A, typename... Rest>
A
helper(Ret (F::*)(A, Rest...) const);
// volatile or lvalue/rvalue *this not required for lambdas (phew)
that accept a pointer to member function taking at least one argument. And now:
template<typename F>
struct first_argument {
typedef decltype( helper(&F::operator()) ) type;
};
[ an elaborate trait could successively query the lvalue-rvalue/const/volatile overloads and expose the first argument if it's the same for all overloads, or use std::common_type.]
#Luc's answer is great but I just came across a case where I also needed to deal with function pointers:
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>()));
This can be used on both functors and function pointers:
void function(float);
struct functor {
void operator() (int);
};
int main() {
std::cout << std::is_same<first_argument<functor>, int>::value
<< ", "
<< std::is_same<first_argument<decltype(&function)>, int>::value
<< std::endl;
return 0;
}
Let's say we have a class template like this:
template<typename F>
class A
{
public:
template<typename... Args>
A(F f, Args... args)
{ /* Do something... */ }
};
And now I want to use it in some way like this one:
A<int(int)> a(::close, 1);
Now the question: is there any way to omit the <int(int)> because a compiler can know this information for the ::close? There is no need to save the "design" of the template.
As for concrete task, I need to design a template of a class. Objects of this class could take a function and parameters for this function at construction time and call this function later.
No, you (currently) cannot. The standard way of doing this is by creating "make_like" function (such as make_pair, make_optional ...):
template<typename F, typename... Args>
A<std::decay_t<F>> make_A (F &&f, Args&&... args) {
return {std::forward<F>(f), std::forward<Args>(args)...};
}
C++17 will introduce template argument deduction for class which will allow you to do exactly what you want (see also Barry's answer below).
Thanks to the adoption of template parameter deduction for constructors, in C++17, you'll be able to just write:
A a(::close, 1);
Before that, you'll just need to write a factory to do the deduction for you:
template <class F, class... Args>
A<std::decay_t<F>> make_a(F&& f, Args&&... args) {
return {std::forward<F>(f), std::forward<Args>(args)...};
}
auto a = make_a(::close, 1);
This is a little verbose, but at least you don't need to worry about efficiency - there will be no copies made here thanks to RVO.
You cannot omit the arguments of a template class, unless they are defaulted. What you can do is have a maker function which deduces the argument and forwards this argument to the template class, returning an object of the appropriate instantiation.
template<typename F, typename... Args>
A<F> make_A(F f, Args&&... args) {
return A<F>(f, std::forward<Args>(args)...);
}
Why does the following compile under C++11? (I know it won't link.) I would expect the std::enable_if test to fail since 1() is not a function type.
#include <type_traits>
template <typename Func, typename... Args>
typename std::enable_if<std::is_function<Func(Args...)>::value>::type
delegate(Func, Args...);
int main(void) {
delegate(1); // << Why does this line compile?
return 0;
}
Func is int, Args is empty, so Func(Args...) is int(), i.e., "function of () returning int".
Anything that is_function returns true for can't be the type of a by-value function parameter, so it's not obvious what you want to do.
I was trying to get delegate to only be callable when Func is a
function (preferably function pointer) that can be applied to Args...
Use expression SFINAE for that.
template <typename Func, typename... Args>
auto delegate(Func f, Args... args) -> decltype(f(args...), void());
Depending on what you actually want to do, you may want to std::move f and args.
The code you have written will always yield true. You probably meant std::is_function<Func>...
Though not sure, it seems like you do not need enable_if at all, and you'd better of with a simple
template <class R, class... ARGS>
R delegate2(R (*fun)(ARGS...), ARGS...);
However, if I am wrong and enable_if is a key to success in your case, here is how you can do this:
#include <type_traits>
template <typename Func, typename... Args>
typename std::enable_if<std::is_function<std::remove_pointer_t<Func>>::value>::type
delegate(Func, Args...);
void check(int);
int main(void) {
delegate(check, 10); // << good line compiles
delegate(10); // << this bad line does not
return 0;
}
Based on this comment:
I was trying to get delegate to only be callable when Func is a function (preferably function pointer) that can be applied to Args...
you're using the wrong type trait. To check if Func is callable with Args..., you need to construct an expression that would actually call an instance of Func with those arguments. For that, there's std::result_of_t (in C++14, it becomes SFINAE friendly):
template <typename Func, typename... Args,
class R = std::result_of_t<Func(Args...)>>
R delegate(Func, Args...);
Or, in C++11, just write that out with decltype and declval:
template <typename Func, typename... Args,
class R = std::declval<Func>()(std::declval<Args>()...)>
R delegate(Func, Args...);
Using std::function, we can get the type of an argument using the argument_type, second_argument_type etc. typedefs, but I can't see a way to do the same thing with lambdas. Is it possible? (I'm using VS2010)
Say I want something like the following in my deserialization system used to read an object and pass it to a setter function:
template<typename F>
static void forward(F f)
{
// Create an object of the type of the first
// parameter to the function object F
typedef typename F::argument_type T;
T t;
//...do something with 't' here (deserialize in my case)
// Forward the object to the function
f(t);
}
It can be used like this and everything works fine:
std::function<void(int)> f = [](int i) -> void { setValue(i); };
forward(f);
But it will not work directly with lambdas:
forward([](int i) -> void { setValue(i); });
//error C2039: 'argument_type' : is not a
//member of '`anonymous-namespace'::<lambda1>'
Is there a way to access the parameter types in a way that will work for both lambdas and std::function objects? Maybe a way to get the std::function type of a lambda first, and then the argument_type from that?
Following on from the answer below, a version that works with lambdas and std::function is:
template<typename T, typename F>
static void forward(F f)
{
T t;
//...do something with 't' here (deserialize in my case)
f(t);
}
forward<int>([](int i) -> void { setValue(i); });
Since int is repeated here I was hoping to get rid of it - not so bad for int but more annoying for long-named types in a couple of namespaces. C'est la vie!
It's not desirable in the general case. (Note that it's quite easy for std::function<T(A)> to specify what e.g. argument_type is: it's just A! It's available in the type definition.)
It would be possible to require each and every function object type to specify its argument types, and in turn mandate that the closure types generated from lambda expression do so. In fact, pre-C++0x features like adaptable functors would only work for such types.
However, we're moving from that with C++0x and for good reasons. The simplest of which is simply overloading: a functor type with a templated operator() (a.k.a a polymorphic functor) simply takes all kind of arguments; so what should argument_type be? Another reason is that generic code (usually) attempts to specify the least constraints on the types and objects it operates on in order to more easily be (re)used.
In other words, generic code is not really interested that given Functor f, typename Functor::argument be int. It's much more interesting to know that f(0) is an acceptable expression. For this C++0x gives tools such as decltype and std::declval (conveniently packaging the two inside std::result_of).
The way I see it you have two choices: require that all functors passed to your template use a C++03-style convention of specifying an argument_type and the like; use the technique below; or redesign. I'd recommend the last option but it's your call since I don't know what your codebase looks like or what your requirements are.
For a monomorphic functor type (i.e. no overloading), it is possible to inspect the operator() member. This works for the closure types of lambda expressions.
So we declare these helpers
template<typename F, typename Ret, typename A, typename... Rest>
A
helper(Ret (F::*)(A, Rest...));
template<typename F, typename Ret, typename A, typename... Rest>
A
helper(Ret (F::*)(A, Rest...) const);
// volatile or lvalue/rvalue *this not required for lambdas (phew)
that accept a pointer to member function taking at least one argument. And now:
template<typename F>
struct first_argument {
typedef decltype( helper(&F::operator()) ) type;
};
[ an elaborate trait could successively query the lvalue-rvalue/const/volatile overloads and expose the first argument if it's the same for all overloads, or use std::common_type.]
#Luc's answer is great but I just came across a case where I also needed to deal with function pointers:
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>()));
This can be used on both functors and function pointers:
void function(float);
struct functor {
void operator() (int);
};
int main() {
std::cout << std::is_same<first_argument<functor>, int>::value
<< ", "
<< std::is_same<first_argument<decltype(&function)>, int>::value
<< std::endl;
return 0;
}