Passing a function template as argument to another function template is always a bit tricky. Typically one has to resort to creating a lambda object that goes and calls the original function.
Example
template <typename It>
void f(It, It) {}
void g(std::vector<int>::iterator, std::vector<int>::iterator) {}
template <typename C, typename F>
void callA(C&& c, F callable) {
return callable(std::begin(c), std::end(c));
}
Problem
If I have a std::vector<int> c, I cannot just pass f along to callA because f is a template and not a function:
callA(c, f); // f has an unresolved overloaded function type and cannot be deduced
callA(c, std::distance); // same problem
callA(c, g); // works because g is a proper function, not a template
callA(c, [](auto a, auto b) {return f(a,b);}); // works again
Even if we help deduce the type of the callable:
template <typename C, template <typename> typename F,
typename T = std::decay_t<decltype(std::begin(std::declval<C>()))>>
auto callB(C&& c, F<T> callable) {
return callable(std::begin(c), std::end(c));
}
this fails to compile.
Question
Is there a way to force C++ to deduce the function's type directly without resorting to a lambda function or type-erasure like std::function? Effectively, (how) can I turn a function template into a first-class citizen? That is, have its type deduced.
I'm willing to go to some lengths when defining the higher-order function (see callB versus callA), but not when calling it.
You can change the parameter type to function pointer, which could be converted from function template (with template argument deduced). E.g.
template <typename C>
void callA(C&& c, void(*callable) (std::decay_t<decltype(std::begin(std::declval<C>()))>, std::decay_t<decltype(std::begin(std::declval<C>()))>)) {
return callable(std::begin(c), std::end(c));
}
Then
std::vector<int> c;
callA(c, f); // template argument It will be deduced as std::vector<int>::iterator for f
LIVE
Are you aware of BOOST_HOF_LIFT?
It allows you to lift overloaded and/or templated functions into objects with as easy syntax as auto my_max = BOOST_HOF_LIFT(std::max);.
You could change callA(c, std::distance) to callA(c, BOOST_HOF_LIFT(std::distance)).
Related
Passing a function template as argument to another function template is always a bit tricky. Typically one has to resort to creating a lambda object that goes and calls the original function.
Example
template <typename It>
void f(It, It) {}
void g(std::vector<int>::iterator, std::vector<int>::iterator) {}
template <typename C, typename F>
void callA(C&& c, F callable) {
return callable(std::begin(c), std::end(c));
}
Problem
If I have a std::vector<int> c, I cannot just pass f along to callA because f is a template and not a function:
callA(c, f); // f has an unresolved overloaded function type and cannot be deduced
callA(c, std::distance); // same problem
callA(c, g); // works because g is a proper function, not a template
callA(c, [](auto a, auto b) {return f(a,b);}); // works again
Even if we help deduce the type of the callable:
template <typename C, template <typename> typename F,
typename T = std::decay_t<decltype(std::begin(std::declval<C>()))>>
auto callB(C&& c, F<T> callable) {
return callable(std::begin(c), std::end(c));
}
this fails to compile.
Question
Is there a way to force C++ to deduce the function's type directly without resorting to a lambda function or type-erasure like std::function? Effectively, (how) can I turn a function template into a first-class citizen? That is, have its type deduced.
I'm willing to go to some lengths when defining the higher-order function (see callB versus callA), but not when calling it.
You can change the parameter type to function pointer, which could be converted from function template (with template argument deduced). E.g.
template <typename C>
void callA(C&& c, void(*callable) (std::decay_t<decltype(std::begin(std::declval<C>()))>, std::decay_t<decltype(std::begin(std::declval<C>()))>)) {
return callable(std::begin(c), std::end(c));
}
Then
std::vector<int> c;
callA(c, f); // template argument It will be deduced as std::vector<int>::iterator for f
LIVE
Are you aware of BOOST_HOF_LIFT?
It allows you to lift overloaded and/or templated functions into objects with as easy syntax as auto my_max = BOOST_HOF_LIFT(std::max);.
You could change callA(c, std::distance) to callA(c, BOOST_HOF_LIFT(std::distance)).
I'm writing a delegate library and stubled upon this problem: Let's say I have overloaded functions named foo like this:
int foo(double d);
double foo(int d);
How would I write my template argument list if I want to resolve which function is meant by specifying a signature as a template parameter. I basically want this syntax (yet it shall work with any signature):
Delegate d = make_delegate<&foo,int(double)>(); // "Delegate" is automatically deduced as Delegate<int(double)>
I managed to resolve it by using the following helper template, but it only works if I write the parameter types of the function signature manually. I struggle to forward the variadic parameter pack Args... (which is encoded in the Args_pack specialization) to the function signature.
template<typename... Args>
struct Args_pack {}
template<typename Signature>
struct Identify_signature_type;
template<typename Return, typename... Args>
struct Identify_signature_type<Return(Args...)> {
using T_return = Return;
using Args_pack = Args_pack<Args...>;
};
template<auto Signature> using Identify_signature = Identify_signature_type<decltype(Signature)>;
template<typename Signature, typename Identify_signature_type<Signature>::T_return Function(double /* important magic needed here */)>
auto make_delegate()
{...}
Delegate d = make_delegate<int(double), &foo>(); // Works. However, it would be nice if the template parameters could exchange places.
You can add a * to the signature to get the right function pointer type.
template<typename Signature, Signature* fptr>
auto make_delegate()
{...}
Consider the following code in C++14, following the posts here, here and here:
// Include
#include <tuple>
#include <iostream>
#include <type_traits>
// Temporary function queue declaration
template <class... F>
class temporary_function_queue;
// Apply function queue declaration
template <class... F>
constexpr temporary_function_queue<F&&...> apply_function_queue(F&&... f);
// Temporary function queue definition
template <class... F>
class temporary_function_queue final
{
// Types
private:
using const_lvalue_reference = const temporary_function_queue&;
using rvalue_reference = temporary_function_queue&&;
using temporary_type = temporary_function_queue<F&&...>;
using data_type = std::tuple<F&&...>;
// Lifecycle
private:
temporary_function_queue(rvalue_reference) = default;
temporary_function_queue(const_lvalue_reference) = delete;
temporary_function_queue operator=(rvalue_reference) = delete;
temporary_function_queue operator=(const_lvalue_reference) = delete;
explicit constexpr temporary_function_queue(F&&... f)
: _f{std::forward<F>(f)...}
{
}
// Temporary creator
public:
friend constexpr temporary_type apply_function_queue<>(F&&... f);
// Apply function queue declaration
public:
template <class... Args>
decltype(auto) operator()(Args&&... args) const&&
{
// Do I need to do std::forward on f0 too? If so, how?
return std::get<0>(_f)(std::forward<Args>(args)...);
}
// Data members
private:
data_type _f;
};
// Apply function queue definition
template <class... F>
constexpr temporary_function_queue<F&&...> apply_function_queue(F&&... f)
{
return temporary_function_queue<F&&...>(std::forward<F>(f)...);
}
/* Example of use
int main(int argc, char* argv[])
{
apply_function_queue(
[](auto i){std::cout<<0<<std::endl;},
[](auto i){std::cout<<1<<std::endl;}
)(0);
return 0;
}
*/
The goal is to produce the following call syntax:
apply_function_queue(f0, f1, f2)(a, b, c, d, e);
Where f0, f1, f2 are either function pointers, functors, lambdas..., and where a, b, c, d, e are arguments to be perfectly forwarded. This function should produce a temporary type, and then call the operator() of that temporary type, and this operator should do a perfect forwarding of fn (for now f0, it will be changed later) with the arguments a, b, c, d, e.... The temporary_function_queue should not be usable in any other context.
The problem is that I am a little lost with the forwarding, universal references and lvalue references... Is the code shown above safe? If not what example of use would lead to undefined behaviour? And in that case, how to make it safe, and efficient (ideally, I would like no runtime overhead on most compilers with -O3)?
Note: Dangling references are quite likely to happen with this approach.
auto make_f(); // return by value
auto&& q = apply_function_queue(make_f());
// q holds dangling rvalue reference
q(a, b, c); // whoops...
First some remarks about wording and deduction. Let:
template<class... T> void f(T&&... p) {}
Note: When this template is instantiated, there are two different packs here: T... and T&&....
Call it with an lvalue or type R and an rvalue of type Q:
R a;
f(a, Q{});
Now T... will be R&, Q but T&&... will be R&, Q&&.
Forwarding the pack p will result in the T&&... pack.
'decltype'(std::forward<T>(p)...) === T&&...
(Note: You can't actually apply decltype here - it's just for illustration.)
Therefore, I'll call the pack that is actually deduced (T...) the deduced types/pack and the result of adding rvalue references / forwarding (T&&...) the forwarded types/pack.
Applying && everywhere inside the class as well as in the return type of apply_function_queue is superfluous. (If you return temporary_function_queue<F&&...> from apply_function_queue there is no need for && inside temporary_function_queue. And if you apply && inside the class everywhere there is no need to return temporary_function_queue<F&&...>.)
You either instantiate the class template with the deduced pack and add && everywhere you want references our you instantiate the class template with the forwarding pack and don't add &&.
It is required to have the deduced types available in the class. (Because the friend declaration uses both F... and F&&....) So you'll want to remove && from the return type of apply_function_queue.
You'll need to change some declarations:
apply_function_queue
forward declaration:
template <class... F>
constexpr temporary_function_queue<F...> apply_function_queue(F&&... f);
definition:
template <class... F>
constexpr temporary_function_queue<F...> apply_function_queue(F&&... f)
{
return temporary_function_queue<F...>(std::forward<F>(f)...);
}
temporary_type
The class instance type is temporary_function_queue<F...> not temporary_function_queue<F&&...>!
using temporary_type = temporary_function_queue<F...>;
friend declaration
friend constexpr temporary_type apply_function_queue<F...>(F&&... f);
If you want perfect forwarding of rvalue function types in the function call operator you'll have to resort to manual casting / forwarding I think.
Inside decltype(auto) operator()(Args&&... args) const&& you'll find that
decltype(std::get<0>(_f)) === std::tuple_element_t<0u, data_type>&
which by reference collapsing rules is a lvalue reference. What you actually want, in order to forward the elements from the tuple is the tuple_element::type.
Thus you'd have to directly cast to the actual type in the tuple:
return static_cast<std::tuple_element_t<0u, data_type>>(
std::get<0>(_f))(std::forward<Args>(args)...);
or forward (which will have the same effect through reference collapsing):
return std::forward<std::tuple_element_t<0u, data_type>>(
std::get<0>(_f))(std::forward<Args>(args)...);
For example
template<class T, class U>
void f();
template<class T> using g = f<T, int>;
Or any similar idea for functions?
No. You cannot do that. You need to create a new function that calls f, forwarding all arguments and template arguments.
template<class T, class U>
void f();
template<class T>
void g() {
f<T, int>();
}
A C++14 alternative is a variable template of function pointer type:
template<typename T>
void (*g)() = &f<T, int>;
Although this approach ignores default arguments and probably has other quirks as well. I highly recommend the more verbose wrapping approach.
No, you can not do that as templated aliases are used to create aliases of types, not concrete data.
What you are trying to do is to create an alias/type from the address of a function as f<T, int> decays into a pointer to function.
You can however create a templated alias from the type of the function f.
template <typename T>
using g = typename std::add_pointer<decltype(f<T, int>)>::type;
int main() {
// Type of 'func' is 'void (*)()' with template arguments '{T1=double, T2=int}'.
g<double> func;
func = f<double, int>; // Point to function with same signature and template args.
func(); // Ok to call function.
}
I'm trying to create a delayable call object. Something along the lines of (pseudo-code):
template <class FN>
struct delayable_call
{
return-type-of-FN call(); // <-- I'd like to use result_of here.
template<class ArgTypes...>
delayable_call(FN* pFn, ArgTypes... args);
FN* fn;
args-saving-struct;
};
I tried using result_of::type for the return type of call, but get errors during instantiation of the template because apparently the argument types need to be specified separately.
Instantiation:
int foo(bool, double); // function prototype.
delayable_call<int(bool, double)> delayable_foo(foo, false, 3.14); // instantiation
The error messages and documentation I've read about result_of seem to indicate that the argument types must also be specified. So instead of result_of<FN>::type, I'd need to specify result_of<FN(bool, double)>::type. This does actually fix the compilation problem I'm having, but breaks the generality of the template.
So, how can I use result_of with a template parameter when the template parameter represents the function signature?
template <class FN> struct delayable_call;
template<class R, class...Args> delayable_call<R(Args...)>{
typedef R(*)(Args...) pFN;
replace your delayable_call with a specialization, and you will extrace both R and Args.... You need Args... anyhow to store the parameters.
However, a library-strength delayable call will end up using type erasure. The easiest way is a simple std::function<R()> where you shove a lambda into it:
int foo(double);
double x = 7;
std::function<int()> delayed_foo = [x]{ return foo(x); }
and capture by value unless you really, really mean it to capture by reference.
You could deduce R via:
template<typename Fn, typename... Args>
std::function< typename std::result_of<Fn(Args...)>::type()>
make_delayed_call( Fn&& fn, Args&&... args ) {
return [=]{ return fn(std::move(args)...); }
}
which should deduce your R from the callable object and the arguments. This captures everything by copy -- capture by move requires either more boilerplate, or C++14.