0

In C++, is it possible to map an overloaded function over a heterogenous tuple? For example:

double f(dobule);
size_t f(std::string);

auto t = std::make_tuple(3.14, "a string");
// should be the same as std::make_tuple(f(std::get<0>(t)), f(std::get<1>(t)));
map(f, make_tuple(3.14, "a string")); // type std::tuple<double, size_t>

I can write a map function which maps the same overloaded instance of f to each tuple element (code below), but I don't see how to defer the overloading resolution of f from the call to map to the invocation of f inside map. Has anyone figured out how to do this?

Here's my code to map a single instance of an overloaded function over a tuple (where seq and gens are taken from this answer https://stackoverflow.com/a/7858971/431282):

// basically C++14's integer sequence
template<int ...>
struct seq { };

template<int N, int ...S>
struct gens : gens<N-1, N-1, S...> { };

template<int ...S>
struct gens<0, S...> {
  typedef seq<S...> type;
};

template<typename R, typename A, typename ...B, int ...S> auto
map_1(R (*f)(A), std::tuple<B...> &&t, seq<S...>)
  -> decltype(std::make_tuple(f(std::get<S>(t))...))
{
  return std::make_tuple(f(std::get<S>(t))...);
}

template<typename R, typename A, typename ...B> auto
map(R (*f)(A), std::tuple<B...> &&t)
  -> decltype(map_1(f, std::forward<std::tuple<B...>>(t), typename gens<sizeof...(B)>::type()))
{
  return map_1(f, std::forward<std::tuple<B...>>(t), typename gens<sizeof...(B)>::type());
}
Community
  • 1
  • 1
Cotton Seed
  • 363
  • 3
  • 13
  • I figured out a partial solution: use a callable object with overloaded `operator()` and change the `map` signature to `template ... map(F f, ...)`. Still, it would be nice to capture an overloaded function in a scope as a callable object! – Cotton Seed May 01 '14 at 19:07
  • boost has this already, doesn't stdlib? – Mooing Duck May 01 '14 at 19:16

1 Answers1

2

The issue is that it can't be done with function pointers alone, since you want to resolve the function overload when it's bound to the argument. You can't get a function pointer to function without performing overload resolution. The key is to provide a function object that performs the overload on call, as opposed to trying to get a function pointer at the start.

For this, I would declare the primary function as

template<typename Func, typename ...B> auto
map(Func&& f, std::tuple<B...> &&t)
  -> decltype(map_1(std::forward<Func>(f), std::forward<std::tuple<B...>>(t), typename gens<sizeof...(B)>::type()))
{
  return map_1(std::forward<Func>(f), std::forward<std::tuple<B...>>(t), typename gens<sizeof...(B)>::type());
}

And then defined map_1 in a similar fashion.

Then you can make a function object wrapper

struct Wrapper
{
   template<typename T>
   auto operator()(T&& t) const -> decltype( f(std::forward<T>(t)) )
   {
       return f(std::forward<T>(t));
   }
};

and call it with map(Wrapper(), make_tuple(3.14, "a string"))

Edit: if you have C++14, you can do the following (thanks @MooingDuck for inspiration)

#define BINDOVERLOADED(X) [](auto&& t) { return X(std::forward<decltype(t)>(t)); }
auto x = map(BINDOVERLOADED(f), make_tuple(3.14, "a string"));

See http://coliru.stacked-crooked.com/a/439f958827de8cf2

Dave S
  • 20,507
  • 3
  • 48
  • 68
  • I think I would actually wrap `Wrapper` in a macro for `f`. – Mooing Duck May 01 '14 at 19:18
  • `#define BINDOVERLOADED(X) struct{templateauto operator()(T&&t)const->decltype(X(std::forward(t)){return X(std::forward(t));}}()` and then it's `map(BINDOVERLOADED(to_string),my_tuple);` – Mooing Duck May 01 '14 at 19:19
  • @MookingDuck: That doesn't appear to work for 2 reasons. 1) I can't declare a struct as part of an expression and 2) I can't declare a member template function of a local struct. Not sure if that's gcc or what the standard says at the moment http://ideone.com/XBFGMx – Dave S May 01 '14 at 19:23
  • `#define BINDOVERLOADED(X) [](auto&&t){return X(std::forward(t));}` for C++14? (amusingly, these consider _types_ to as overloaded functions as well, `BINDOVERLOADED(char)(42)` ) – Mooing Duck May 01 '14 at 19:25
  • @MooingDuck what is T? – Marc Glisse May 01 '14 at 19:27
  • 1
    I need to learn to C++14 :( Reading: http://scottmeyers.blogspot.com/2013/05/c14-lambdas-and-perfect-forwarding.html ....... – Mooing Duck May 01 '14 at 19:28
  • @MooingDuck: Edited with C++14 example – Dave S May 01 '14 at 19:34