I'm working on a project which involves providing an interface for users to find optima of functions of arbitrary numbers of arguments. Internally, all the mechanism is built around std::tuples of the argument types. I want to provide users the ability to call my optimization routines, though, on functions written in the "usual" style (such as f1 in the example), rather than having to write their functions to be optimized as functions of std::tuple instantiations (such as f2 in the example).
As part of this mechanism, I have written an apply function which unpacks a tuple into the arguments of a given function and calls it.
I have also created a pair of function templates, one forwarding to the other with a lambda wrapper, providing the interface to the optimization routine. A simplified version appears below as tuple_array_map. The intention was to provide SFINAE for selection between the two, depending on whether the function type is callable with a tuple argument, or callable with the unpacked tuple members as arguments. I use dummy template parameters with SFINAE-triggering default arguments for this purpose.
This scheme works perfectly under g++ 4.7 and higher and compiling with -std=c++11 -pedantic -Wall -Wextra -Werror produces no warnings or errors.
However, when trying to compile under clang 5.1 with -std=c++11 (sorry, I'm not a big clang user and I don't know if there's a more appropriate set of options), I get the following output for my example code:
clang_fail.cpp:91:5: error: call to 'tuple_array_map' is ambiguous
tuple_array_map(f2, tuples);
^~~~~~~~~~~~~~~
clang_fail.cpp:59:6: note: candidate function [with Fn = double (*)(const
std::__1::tuple<double> &), TupleArr =
std::__1::array<std::__1::tuple<double>, 5>, $2 = double]
void tuple_array_map(Fn f, const TupleArr& arr)
^
clang_fail.cpp:69:6: note: candidate function [with Fn = double (*)(const
std::__1::tuple<double> &), TupleArr =
std::__1::array<std::__1::tuple<double>, 5>, $2 = double, $3 = void]
void tuple_array_map(Fn f, const TupleArr& arr)
^
clang_fail.cpp:71:5: error: call to 'tuple_array_map' is ambiguous
tuple_array_map([&](const typename TupleArr::value_type& t) {
^~~~~~~~~~~~~~~
clang_fail.cpp:90:5: note: in instantiation of function template specialization
'tuple_array_map<double (*)(double),
std::__1::array<std::__1::tuple<double>, 5>, double, void>' requested here
tuple_array_map(f1, tuples);
^
clang_fail.cpp:59:6: note: candidate function [with Fn = <lambda at
clang_fail.cpp:71:21>, TupleArr = std::__1::array<std::__1::tuple<double>,
5>, $2 = double]
void tuple_array_map(Fn f, const TupleArr& arr)
^
clang_fail.cpp:69:6: note: candidate function [with Fn = <lambda at
clang_fail.cpp:71:21>, TupleArr = std::__1::array<std::__1::tuple<double>,
5>, $2 = double, $3 = void]
void tuple_array_map(Fn f, const TupleArr& arr)
^
The really puzzling part is that it appears to deduce a double return from a call expression that should SFINAE out, unless I've missed something from the standard regarding either template default arguments or SFINAE itself.
Example follows---it's as minimal as I could get it while still triggering the same behavior:
#include <tuple>
#include <array>
#include <utility>
#include <type_traits>
double f1(double x)
{
return x * 2;
}
double f2(const std::tuple<double>& x)
{
return std::get<0>(x) * 2;
}
template<std::size_t N>
struct apply_impl {
template<class F, class Tuple, class... TParams>
static auto apply(F&& fn, Tuple&& t, TParams&&... args)
-> decltype(
apply_impl<N - 1>::apply(
std::forward<F>(fn), std::forward<Tuple>(t),
std::get<N - 1>(std::forward<Tuple>(t)),
std::forward<TParams>(args)...
))
{
return apply_impl<N - 1>::apply(
std::forward<F>(fn), std::forward<Tuple>(t),
std::get<N - 1>(std::forward<Tuple>(t)),
std::forward<TParams>(args)...
);
}
};
template<>
struct apply_impl<0> {
template<class F, class Tuple, class... TParams>
static auto apply(F&& fn, Tuple&&, TParams&&... args)
-> decltype(std::forward<F>(fn)(std::forward<TParams>(args)...))
{
return std::forward<F>(fn)(std::forward<TParams>(args)...);
}
};
template<class F, class Tuple>
auto apply(F&& fn, Tuple&& t)
-> decltype(apply_impl<
std::tuple_size<typename std::decay<Tuple>::type>::value
>::apply(std::forward<F>(fn), std::forward<Tuple>(t)))
{
return apply_impl<
std::tuple_size<typename std::decay<Tuple>::type>::value
>::apply(std::forward<F>(fn), std::forward<Tuple>(t));
}
template<class Fn, class TupleArr,
class = decltype(std::declval<Fn>()(
std::declval<typename TupleArr::value_type>()))>
void tuple_array_map(Fn f, const TupleArr& arr)
{
for (auto i = 0; i < arr.size(); ++i)
static_cast<void>(f(arr[i]));
}
template<class Fn, class TupleArr,
class = decltype(apply(std::declval<Fn>(),
std::declval<typename TupleArr::value_type>())),
class = void>
void tuple_array_map(Fn f, const TupleArr& arr)
{
tuple_array_map([&](const typename TupleArr::value_type& t) {
return apply(f, t);
}, arr);
}
int main()
{
std::array<std::tuple<double>, 5> tuples = {
std::make_tuple(1),
std::make_tuple(2),
std::make_tuple(3),
std::make_tuple(4),
std::make_tuple(5)
};
// "apply" unpacks a tuple into arguments to a function
apply(f1, tuples[0]);
// this call produces an ambiguity one level down under clang
tuple_array_map(f1, tuples);
// this call directly produces an ambiguity under clang
tuple_array_map(f2, tuples);
}
The ambiguity when compiling with libc++ is due to the lack of the standard-mandated explicit specifier on std::tuple's converting constructor (Constructor #2 at cppreference). Consequently, double is implicitly convertible to std::tuple<double> (See this example program) so both of your tuple_apply_map functions are viable.
As a workaround, I suggest creating a needs_apply trait and using that to constrain your tuple_apply_map templates (I'll use tag dispatching):
template<class Fn, class TupleArr>
struct needs_apply {
template <class F=Fn>
static auto test(int) ->
decltype(std::declval<F>()(*std::declval<TupleArr>().begin()), std::false_type{});
static auto test(...) -> std::true_type;
using type = decltype(test(0));
};
template<class Fn, class TupleArr>
void tuple_array_map(Fn f, const TupleArr& arr, std::false_type)
{
for (auto&& i : arr)
static_cast<void>(f(i));
}
template<class Fn, class TupleArr>
void tuple_array_map(Fn f, const TupleArr& arr, std::true_type)
{
tuple_array_map([&](const typename TupleArr::value_type& t) {
return apply(f, t);
}, arr, std::false_type{});
}
template<class Fn, class TupleArr>
void tuple_array_map(Fn&& f, TupleArr&& arr) {
tuple_array_map(std::forward<Fn>(f), std::forward<TupleArr>(arr),
typename needs_apply<Fn,TupleArr>::type{});
}
This works correctly with libc++ and with libstdc++ and even compiling with g++.
According to this answer by Howard Hinnant, this non-conformance of the std::tuple constructor is an extension implemented in libc++ as an experiment.
See also Library Working Group active issue 2051 and the paper N3680 written by Daniel Krügler to address the issue.
Related
I'm trying to port some code written for GCC (8.2) to be compilable by Clang:
#include <tuple>
struct Q{};
using TUP = std::tuple<Q>;
template<typename Fn>
inline
void feh(Fn&, const std::tuple<>*)
{}
template<typename Fn, typename H>
inline
void feh(Fn& fn, const std::tuple<H>*)
{
fn(H{});
}
template<typename Fn, typename H, typename... R>
inline
void feh(Fn& fn, const std::tuple<H, R...>*)
{
fn(H{});
using Rest = const std::tuple<R...>*;
feh<Fn, R...>(fn, static_cast<Rest>(nullptr));
}
template<typename Tuple, typename Fn>
inline
void fe(Fn& fn, const Tuple * tpl = nullptr)
{
feh(fn, tpl);
}
int main()
{
auto r = [] (Q const&) {};
TUP tup;
fe<TUP>(r, &tup);
}
GCC 8.2 (and 12.1) compiles the code just fine. However, Clang 11.0.0 (and 14.0.0) complains that the call from fe to feh is ambiguous between void feh(Fn& fn, const std::tuple<H>*) [with Fn = (lambda at <source>:38:14), H = Q] and void feh(Fn& fn, const std::tuple<H, R...>*) [with Fn = (lambda at <source>:38:14), H = Q, R = <>].
https://godbolt.org/z/5E9M6a5c6
Which compiler is right?
How can I write this code so both compilers accept it?
Both if constexpr and fold expressions would work in C++17, but this is a library header included by many projects, and not all of them are compiled with C++17. I need a solution which works in C++11.
Which compiler is right?
Clang is wrong in rejecting the code because the first overload candidate feh(Fn& fn, const std::tuple<H>*) should be preferred over the other candidate feh(Fn& fn, const std::tuple<H, R...>*) since the former is more specialized than the latter.
In other words, the version without the pack is considered more specialized and hence should be preferred if it matches the call.
This is because, basically(roughly) for one function template to be considered more specialized than the other, the latter should be able to accept all the template arguments that the former can accept but not vice-versa.
Now, in your given example the overload feh(Fn& fn, const std::tuple<H, R...>*) can accept(or work with) all template arguments which the former feh(Fn& fn, const std::tuple<H>*) can accept but the reverse is not true. Hence the former is more specialized than the latter. For more technical details of this process, refer to What is the partial ordering procedure in template deduction or from [temp.deduct.partial]/10 which states:
Function template F is at least as specialized as function template G if, for each pair of types used to determine the ordering, the type from F is at least as specialized as the type from G. F is more specialized than G if F is at least as specialized as G and G is not at least as specialized as F.
(emphasis mine)
clang++ is correct because both functions matches equally good. I'm unsure which compiler that is correct, but...
A C++11 solution could be to just add the requirement that the Rest part must contain at least one type and that is easily done by just adding R1. That would mean that the rest of your code could be left unchanged:
template<typename Fn, typename H, typename R1, typename... R>
inline
void feh(Fn& fn, const std::tuple<H, R1, R...>*)
{
fn(H{});
using Rest = const std::tuple<R1, R...>*;
feh<Fn, R1, R...>(fn, static_cast<Rest>(nullptr));
}
A C++17 solution would be to remove the other feh overloads and use a fold expression:
template <typename Fn, typename... H>
inline void feh(Fn& fn, const std::tuple<H...>*) {
(..., fn(H{}));
}
This is a unary left fold over the comma operator which "unfolded" becomes:
(((fn(H1{}), fn(H2{})), ...), fn(Hn{}))
By far the simplest solution is an if constexpr :
template<typename Fn, typename H, typename... R>
inline
void feh(Fn& fn, const std::tuple<H, R...>*)
{
fn(H{});
if constexpr (sizeof...(R) > 0) {
using Rest = const std::tuple<R...>*;
feh<Fn, R...>(fn, static_cast<Rest>(nullptr));
}
}
and just remove the problematic overload.
How can I write this code so both compilers accept it?
You can write your code without recursion
template<typename Fn, typename ... Ts>
void fe(Fn& fn, const std::tuple<Ts...>* = nullptr)
{
// Trick to simulate fold expression of c++17
const int dummy[] = {0, (static_cast<void>(fn(Ts{})), 0)...};
static_cast<void>(dummy); // Avoid warning about unused variable
}
Which would become, in C++17
template<typename Fn, typename ... Ts>
void fe(Fn& fn, const std::tuple<Ts...>* = nullptr)
{
(static_cast<void>(fn(Ts{})), ...);
// static_cast is here to handle evil overloaded operator comma (for type returned by Fn)
// might be omitted if you know you are not in that pathological case
}
[Demo](fe(r, &tup);)
I'm writing my own std::async analogue (has to work back to Intel13/gcc 4.4 STL), and this works fine:
template <typename Func, typename ...Args>
struct return_value {
template <typename T>
using decayed = typename std::decay<T>::type;
using type = typename std::result_of<decayed<Func>(decayed<Args>...)>::type;
};
template <typename Func, typename ...Args>
typename return_value<Func,Args...>::type async(Func &&func, Args&&... args) {
return func(args...);
}
void run(int a, double b) {
printf("a: %i b: %f\n", a, b);
}
int main() {
async(run, 1, 3.14);
}
But if I add an overload for run:
void run() {
printf("no args\n");
}
Then it can't properly resolve:
<source>: In function 'int main()':
<source>:27:23: error: no matching function for call to 'async(<unresolved overloaded function type>, int, double)'
async(run, 1, 3.14);
^
<source>:14:43: note: candidate: 'template<class Func, class ... Args> typename return_value<Func, Args>::type async(Func&&, Args&& ...)'
typename return_value<Func,Args...>::type async(Func &&func, Args&&... args) {
^~~~~
<source>:14:43: note: template argument deduction/substitution failed:
<source>:27:23: note: couldn't deduce template parameter 'Func'
async(run, 1, 3.14);
^
Compiler returned: 1
How can I take a function as a template parameter and properly deduce the overload given the arguments?
I personally don't see a way to disambiguate overloads unless you know the return type. You could assume return type void most common and to this then: (I am simplifying your example for brevity)
template <class F, class... Args>
auto async(F f, Args... args)
{
return f(args...);
}
template <class... Args>
auto async(void (*f)(Args...), Args... args)
{
return f(args...);
}
void run();
void run(int, double);
auto test()
{
async(run); // calls run();
async(run, 1, 2.); // calls run(int, double);
}
This does seem kind of fishy and confusing to the user. Why does it work when the function passed returns void and it doesn't if it returns int? So I don't recommend it.
So really the only thing you could do is let it in the hands of the user to figure it out.
So some solutions for the caller of your function:
The good (and ugly) old way: use cast to disambiguate the overload:
async(static_cast<int(*)(int, double)>(run), 1, 2.);
I personally don't like this approach at all. I don't like the verbosity of it and most of all I don't like that I have to be explicit about something that should really be implicit.
The lambda way
async([] { return run(1, 2.); });
I like this. It's not half bad. Still a little bit verbose, but way way better than other alternatives.
The macro way
Yes, macros, in C++. Without further ado, there it is (perfect forwarding omitted for brevity):
#define OVERLOAD(foo) [] (auto... args) { return foo(args...); }
async(OVERLOAD(run), 1, 2.);
I am not going to comment on this one. I leave each and every one of you to judge this macro.
Consider the following snippet:
#include <type_traits>
#include <string>
template<typename T>
std::string stringify (const T& value)
{ return "dummy"; }
template<typename T>
class foo_class
{
public:
template<typename Converter = std::string(&)(const T&),
class = typename std::enable_if<std::is_convertible<
typename std::result_of<Converter(const T&)>::type,
std::string>
::value>::type>
foo_class(const T &value, const Converter &converter = stringify<T>) {}
};
int main(int,const char*[])
{
int X = 7;
foo_class<int> x(X);
foo_class<int> y(X, stringify<int>);
}
The first constructor compiles just fine, however the second one fails with the following error under Clang 3.6.2:
candidate template ignored: substitution failure [with Converter = std::__cxx11::basic_string<char> (const int &)]: function cannot return function type 'std::__cxx11::basic_string<char> (const int &)'
foo_class(const T &value, const Converter &converter = stringify<T>) {}
I dug down and found one way to fix it, by changing const Converter & to just Converter in the parameter list, although this might not be the desired behavior in some cases.
The error is caused by something in the std::enable_if<..> clause. I can remove it and the code compiles (and runs) just fine.
I am primarily interested in the question "why" - why does it work in the first case (when the function is selected as a default parameter), but not in the second case, when it is selected explicitly.
As a secondary question, what would be considered the best way to deal with the issue? I mean, I have one workaround, but would prefer to stick to the "const reference by default" policy for arguments that are not functions.
That's not how you are supposed to use result_of.
It should always be used with references, where the kind of the reference designates the value category of the corresponding expression in the call whose result you want to know.
So if you are calling converter as a const lvalue, then you'd do result_of<const Converter &(const T&)>. A few more examples:
// forward the value category of the function object
template<class F>
auto f(F&& f) -> std::result_of_t<F&&()> { return std::forward<F>(f)(); }
// always call as an lvalue, accept both lvalue and rvalue function object
template<class F>
auto f(F&& f) -> std::result_of_t<F&()> { return f(); }
// Accept everything by value, but call with all lvalues
template<class F, class... Args>
auto f(F f, Args... args) -> std::result_of_t<F&(Args&...)> { return f(args...); }
// Accept everything by value, and move them all
template<class F, class... Args>
auto f(F f, Args... args) -> std::result_of_t<F&&(Args&&...)> { return std::move(f)(std::move(args)...); }
In your second snippet, you have
Converter = std::string(const T&) // without reference
2 possibles fixes/workarounds:
Use std::decay_t
template<typename T>
class foo_class
{
public:
template<typename Converter = std::string(&)(const T&),
std::enable_if_t<
std::is_convertible<
std::result_of_t<std::decay_t<Converter>(const T&)>,
std::string>::value>* = nullptr>
foo_class(const T &value, const Converter &converter = stringify<T>) {}
};
Demo
or use
foo_class<int> y(X, &stringfy<int>);
Given a function f(x, y, z) we can bind x to 0, getting a function g(y, z) == f(0, y, z). We can continue doing this and get h() = f(0, 1, 2).
In C++ syntax that would be
#include <functional>
#include <iostream>
void foo(int a, long b, short c)
{
std::cout << a << b << c << std::endl;
}
int main()
{
std::function<void(int, long, short)> bar1 = foo;
std::function<void(long, short)> bar2 = std::bind(bar1, 0, std::placeholders::_1, std::placeholders::_2);
std::function<void(short)> bar3 = std::bind(bar2, 1, std::placeholders::_1);
std::function<void()> bar4 = std::bind(bar3, 2);
bar4(); // prints "012"
return 0;
}
So far so good.
Now say that I want to do the same -- bind the first argument of a function, get the new function back and repeat this process until all arguments are binded -- but generalize it to work not only with a function of 3 arguments as in the C++ example above, but with a function with unknown* number of arguments.
* In C++ there is such thing as variadic arguments and in C++11 there are variadic templates. I'm referring to variadic templates here.
Basically, what I want to be able to do, is to write a function that accepts any std::function and recursively binds the first argument to some value until all arguments are binded and the function can be called.
For the simplicity, let's assume that std::function represents a function taking any integral arguments and returning void.
This code can be considerate to be a generalization of the previous code
#include <functional>
#include <iostream>
// terminating case of recursion
void apply(std::function<void()> fun, int i)
{
fun();
}
template<class Head, class... Tail>
void apply(std::function<void(Head, Tail...)> f, int i)
{
std::function<void(Tail...)> g = std::bind(f, i);
apply<Tail...>(g, ++i);
}
void foo(int a, long b, short c)
{
std::cout << a << b << c << std::endl;
}
int main()
{
std::function<void(int, long, short)> bar1 = foo;
apply<int, long, short>(bar1, 0);
return 0;
}
This code is great. It is exactly what I want. It doesn't compile.
main.cpp: In instantiation of 'void apply(std::function<void(Head, Tail ...)>, int) [with Head = int; Tail = {long int, short int}]':
main.cpp:24:40: required from here
main.cpp:12:56: error: conversion from 'std::_Bind_helper<false, std::function<void(int, long int, short int)>&, int&>::type {aka std::_Bind<std::function<void(int, long int, short int)>(int)>}' to non-scalar type 'std::function<void(long int, short int)>' requested
std::function<void(Tail...)> g = std::bind(f, i);
^
The issue is that you can't just leave out std::placeholders in std::bind call like that. They are required, and number of placeholders in std::bind should match the number of non-binded arguments in the function.
If we change line
std::function<void(Tail...)> g = std::bind(f, i);
to
std::function<void(Tail...)> g = std::bind(f, i, std::placeholders::_1, std::placeholders::_2);
we see that it successfully passes through the first apply() call, but gets stuck on the second pass, because during the second pass g needs only one placeholder, while we still have two of them in the std::bind.
main.cpp: In instantiation of 'void apply(std::function<void(Head, Tail ...)>, int) [with Head = long int; Tail = {short int}]':
main.cpp:13:30: required from 'void apply(std::function<void(Head, Tail ...)>, int) [with Head = int; Tail = {long int, short int}]'
main.cpp:24:40: required from here
main.cpp:12:102: error: conversion from 'std::_Bind_helper<false, std::function<void(long int, short int)>&, int&, const std::_Placeholder<1>&, const std::_Placeholder<2>&>::type {aka std::_Bind<std::function<void(long int, short int)>(int, std::_Placeholder<1>, std::_Placeholder<2>)>}' to non-scalar type 'std::function<void(short int)>' requested
std::function<void(Tail...)> g = std::bind(f, i, std::placeholders::_1, std::placeholders::_2);
^
There is a way to solve that using regular non-variadic templates, but it introduces a limit on how many arguments std::function can have. For example, this code works only if std::function has 3 or less arguments
(replace apply functions in the previous code on these)
// terminating case
void apply(std::function<void()> fun, int i)
{
fun();
}
template<class T0>
void apply(std::function<void(T0)> f, int i)
{
std::function<void()> g = std::bind(f, i);
apply(g, ++i);
}
template<class T0, class T1>
void apply(std::function<void(T0, T1)> f, int i)
{
std::function<void(T1)> g = std::bind(f, i, std::placeholders::_1);
apply<T1>(g, ++i);
}
template<class T0, class T1, class T2>
void apply(std::function<void(T0, T1, T2)> f, int i)
{
std::function<void(T1, T2)> g = std::bind(f, i, std::placeholders::_1, std::placeholders::_2);
apply<T1, T2>(g, ++i);
}
But the issue with that code is that I would have to define a new apply function to support std::function with 4 arguments, then the same with 5 arguments, 6 and so on. Not to mention that my goal was to not have any hard-coded limit on the number of arguments. So this is not acceptable. I don't want it to have a limit.
I need to find a way to make the variadic template code (the second code snippet) to work.
If only std::bind didn't require to specify placeholders -- everything would work, but as std::bind currently works, we need to find some way to specify the right number of placeholders.
It might be useful to know that we can find the right number of placeholders to specify with C++11's sizeof...
sizeof...(Tail)
but I couldn't get anything worthwhile out of this fact.
First, stop using bind unless you absolutely need to.
// terminating case of recursion
void apply(std::function<void()> fun, int i) {
fun();
}
// recursive case:
template<class Head, class... Tail>
void apply(std::function<void(Head, Tail...)> f, int i) {
// create a one-shot lambda that binds the first argument to `i`:
auto g = [&](Tail&&...tail) // by universal ref trick, bit fancy
{ return std::move(f)(std::move(i), std::forward<Tail>(tail)...);};
// recurse:
apply<Tail...>(g, ++i);
}
next, only type erase if you have to:
// `std::resukt_of` has a design flaw. `invoke` fixes it:
template<class Sig,class=void>struct invoke{};
template<class Sig>using invoke_t=typename invoke<Sig>::type;
// converts any type to void. Useful for sfinae, and may be in C++17:
template<class>struct voider{using type=void;};
template<class T>using void_t=typename voider<T>::type;
// implementation of invoke, returns type of calling instance of F
// with Args...
template<class F,class...Args>
struct invoke<F(Args...),
void_t<decltype(std::declval<F>()(std::declval<Args>()...))>
>{
using type=decltype(std::declval<F>()(std::declval<Args>()...));
};
// tells you if F(Args...) is a valid expression:
template<class Sig,class=void>struct can_invoke:std::false_type{};
template<class Sig>
struct can_invoke<Sig,void_t<invoke_t<Sig>>>
:std::true_type{};
now we have some machinery, a base case:
// if f() is a valid expression, terminate:
template<class F, class T, class I,
class=std::enable_if_t<can_invoke<F()>{}>
>
auto apply(F&& f, T&& t, I&&i)->invoke_t<F()>
{
return std::forward<F>(f)();
}
which says "if we can be invoked, just invoke f.
Next, the recursive case. It relies on C++14 return type deduction:
// if not, build lambda that binds first arg to t, then recurses
// with i(t):
template<class F, class T, class I,
class=std::enable_if_t<!can_invoke<F()>{}, int>>
>
auto apply(F&& f, T&& t, I&&i)
{
// variardic auto lambda, C++14 feature, with sfinae support
// only valid to call once, which is fine, and cannot leave local
// scope:
auto g=[&](auto&&...ts) // takes any number of params
-> invoke_t< F( T, decltype(ts)... ) > // sfinae
{
return std::forward<F>(f)(std::forward<T>(t), decltype(ts)(ts)...);
};
// recurse:
return apply(std::move(g), i(t), std::forward<I>(i));
}
If you want increment, pass [](auto&&x){return x+1;} as 3rd arg.
If you want no change, pass [](auto&&x){return x;} as 3rd arg.
None of this code has been compiled, so there may be typos. I am also worried about the recursion of apply with C++14 return type deduction, that gets tricky sometimes.
If you really have to use bind, you can define your own placeholder types by specializing std::is_placeholder:
template<int N>
struct my_placeholder { static my_placeholder ph; };
template<int N>
my_placeholder<N> my_placeholder<N>::ph;
namespace std {
template<int N>
struct is_placeholder<::my_placeholder<N>> : std::integral_constant<int, N> { };
}
The reason this is useful is that it allows you to then map an integer to a placeholder at compile time, which you can use with the integer_sequence trick:
void apply(std::function<void()> fun, int i)
{
fun();
}
template<class T, class... Ts>
void apply(std::function<void(T, Ts...)> f, int i);
template<class T, class... Ts, int... Is>
void apply(std::function<void(T, Ts...)> f, int i, std::integer_sequence<int, Is...>)
{
std::function<void(Ts...)> g = std::bind(f, i, my_placeholder<Is + 1>::ph...);
apply(g, ++i);
}
template<class T, class... Ts>
void apply(std::function<void(T, Ts...)> f, int i) {
apply(f, i, std::make_integer_sequence<int, sizeof...(Ts)>());
}
Demo. make_integer_sequence and friends are C++14, but can be implemented easily in C++11.
If you're prepared to drop std::bind (which really was a bit of a hacky workaround for pre-C++11 partial applications in my view) this can be quite concisely written:
#include <functional>
#include <iostream>
// End recursion if no more arguments
void apply(std::function<void()> f, int) {
f();
}
template <typename Head, typename ...Tail>
void apply(std::function<void(Head, Tail...)> f, int i=0) {
auto g = [=](Tail&& ...args){
f(i, std::forward<Tail>(args)...);
};
apply(std::function<void(Tail...)>{g}, ++i);
}
void foo(int a, int b, int c, int d) {
std::cout << a << b << c << d << "\n";
}
int main() {
auto f = std::function<void(int,int,int,int)>(foo);
apply(f);
}
Tested working with clang 3.4 and g++ 4.8.2 in C++11 mode. Also on ideone.
You don't need to use std::bind recursively to call some function with a tuple of parameters which values can be evaluated using parameter index:
#include <functional>
#include <utility>
template <typename... Types, std::size_t... indexes, typename Functor>
void apply(std::function<void(Types...)> f, std::index_sequence<indexes...>, Functor&& functor)
{
f(static_cast<Types>(std::forward<Functor>(functor)(indexes))...);
}
template <typename... Types, typename Functor>
void apply(std::function<void(Types...)> f, Functor&& functor)
{
apply(f, std::make_index_sequence<sizeof...(Types)>{}, std::forward<Functor>(functor));
}
Example of use:
void foo(int a, long b, short c)
{
std::cout << a << b << c << std::endl;
}
// ...
std::function<void(int, long, short)> bar = foo;
apply(bar, [](std::size_t index){ return (int)index; });
Live demo
As #T.C. noted in his answer std::make_index_sequence is a C++14 feature but it can be implemented in C++11.
I'd like to hide a std::tuple in my class 'Record' and provide an operator[] on it to access elements of the tuple. The naive code that does not compile is this:
#include <tuple>
template <typename... Fields>
class Record {
private:
std::tuple<Fields...> list;
public:
Record() {}
auto operator[](std::size_t n)
-> decltype(std::get<1u>(list)) {
return std::get<n>(list);
}
};
int main() {
Record<int, double> r;
r[0];
return 0;
}
g++ 4.6 says:
x.cc:13:32: error: no matching function for call to ‘get(std::tuple<int, double>&)’
x.cc:13:32: note: candidates are:
/usr/include/c++/4.6/utility:133:5: note: template<unsigned int _Int, class _Tp1, class _Tp2> typename std::tuple_element<_Int, std::pair<_Tp1, _Tp2> >::type& std::get(std::pair<_Tp1, _Tp2>&)
/usr/include/c++/4.6/utility:138:5: note: template<unsigned int _Int, class _Tp1, class _Tp2> const typename std::tuple_element<_Int, std::pair<_Tp1, _Tp2> >::type& std::get(const std::pair<_Tp1, _Tp2>&)
/usr/include/c++/4.6/tuple:531:5: note: template<unsigned int __i, class ... _Elements> typename std::__add_ref<typename std::tuple_element<__i, std::tuple<_Elements ...> >::type>::type std::get(std::tuple<_Elements ...>&)
/usr/include/c++/4.6/tuple:538:5: note: template<unsigned int __i, class ... _Elements> typename std::__add_c_ref<typename std::tuple_element<__i, std::tuple<_Elements ...> >::type>::type std::get(const std::tuple<_Elements ...>&)
Basically I'd like to call Record::operator[] just like on an array. is this possible?
The argument to get is a compile time constant. You cannot use a
runtime variable for this and you cannot have a single function that
returns the tuple members as your return type is going to be
wrong. What you can do is to abuse non-type argument deduction:
#include <tuple>
template<typename... Args>
struct Foo {
std::tuple<Args...> t;
template<typename T, std::size_t i>
auto operator[](T (&)[i]) -> decltype(std::get<i>(t)) {
return std::get<i>(t);
}
// also a const version
};
int main()
{
Foo<int, double> f;
int b[1];
f[b];
return 0;
}
This is so horrible, that I would never use it and it won't make much sense to users. I would just forward get through a template member.
I'll try to explain why I think why this is really evil: The return type of a function depends only on compile time facts (this changes slightly for virtual member functions). Let's just assume that non-type argument deduction were possible for some cases (the function call arguments are constexpr) or that we could build something that hides it reasonably well, your users wouldn't realize that their return type just changed and implicit conversion would do nasty things to them. Making this explicit safes some of the trouble.
The error message seems to be misleading, as the problem with your code is pretty much clear:
auto operator[](std::size_t n)
-> decltype(std::get<1u>(list)) {
return std::get<n>(list);
}
The template argument n to std::get must be a constant expression, but in your code above n is not a constant expression.
No.
It is not possible to use a parameter bound at runtime (such as a function parameter) to act as template parameter, because such need be bound at compile-time.
But let's imagine for a second that it was:
Record<Apple, Orange> fruitBasket;
Then we would have:
decltype(fruitBasket[0]) equals Apple
decltype(fruitBasket[1]) equals Orange
is there not something here that bothers you ?
In C++, a function signature is defined by the types of its arguments (and optionally the values of its template parameters). The return type is not considered and does not participate (for better or worse) in the overload resolution.
Therefore, the function you are attempting to build simply does not make sense.
Now, you have two alternatives:
require that all arguments inherit or be convertible to a common type, and return that type (which allows you to propose a non-template function)
embrace templates and require your users to provide specifically the index of the type they wish to use
I do not (and cannot) which alternative is preferable in your particular situation, this is a design choice you will have to make.
Finally, I will remark that you may be reasoning at a too low level. Will your users really need to access each field independently ? If they don't, you could provide facilities to apply functions (visitors ?) to each element in turn, for example.
I think Xeo had code which did this.
Here is my attempt which somewhat works. The problem is that [] is not a reference.
template<typename T, std::size_t N = std::tuple_size<T>::value - 1>
struct foo {
static inline auto bar(std::size_t n, const T& list)
-> decltype(((n != N) ? foo<T, N-1>::bar(n, list) : std::get<N>(list))) {
return ((n != N) ? foo<T, N-1>::bar(n, list) : std::get<N>(list));
}
};
template<typename T>
struct foo<T, 0> {
static inline auto bar(std::size_t n, const T& list)
-> decltype(std::get<0>(list)) {
return std::get<0>(list);
}
};
template <typename... Fields>
class Record {
private:
std::tuple<Fields...> list;
public:
Record() {
std::get<0>(list) = 5;
}
inline auto operator[](std::size_t n)
-> decltype(foo<decltype(list)>::bar(n, list)) {
return foo<decltype(list)>::bar(n, list);
}
};
int main() {
Record<int, double> r;
std::cout << r[0];
return 0;
}
As n is a template parameter, it should be known in compile time, but you want to pass it as a parameter in run-time.
Also, gcc 4.5.2 isn't happy due to this fact:
g++ 1.cpp -std=c++0x
1.cpp: In member function 'decltype (get<1u>(((Record<Fields>*)0)->Record<Fields>::list)) Record<Fields>::operator[](size_t)':
1.cpp:14:25: error: 'n' cannot appear in a constant-expression
If you're fine with a compile-time constant and still want to have the nice operator[] syntax, this is an interesting workaround:
#include <tuple>
template<unsigned I>
struct static_index{
static unsigned const value = I;
};
template <typename... Fields>
class Record {
private:
typedef std::tuple<Fields...> tuple_t;
tuple_t list;
public:
Record() {}
template<unsigned I>
auto operator[](static_index<I>)
-> typename std::tuple_element<
I, tuple_t>::type&
{
return std::get<I>(list);
}
};
namespace idx{
const static_index<0> _0 = {};
const static_index<1> _1 = {};
const static_index<2> _2 = {};
const static_index<3> _3 = {};
const static_index<4> _4 = {};
}
int main() {
Record<int, double> r;
r[idx::_0];
return 0;
}
Live example on Ideone. Though I'd personally just advise to do this:
// member template
template<unsigned I>
auto get()
-> typename std::tuple_element<
I, tuple_t>::type&
{
return std::get<I>(list);
}
// free function
template<unsigned I, class... Fields>
auto get(Record<Fields...>& r)
-> decltype(r.template get<I>())
{
return r.template get<I>();
}
Live example on Ideone.