Generically call member function on each element of a tuple

c++, c++11, stdtuple, template-meta-programming, templates

Solution

You can't use `mem_fn` to create a wrapper that calls a member function on objects of heterogeneous type, as the wrapper created by `mem_fn` wraps a pointer to a particular member of a particular type.

The trick is to pass something with a templated function call operator that can accept any appropriate type and call the member function.

In C++14, pass a polymorphic lambda:

[](auto&& obj) -> decltype(auto) { return std::forward<decltype(obj)>(obj).get(); }

For C++11, you'd need to write the equivalent functor by hand:

struct call_get {
    template<class T>    
    auto operator()(T&& obj) const -> decltype(std::forward<T>(obj).get()) {
         return std::forward<T>(obj).get(); 
    } 
};

Problem

Step one: expand a tuple and pass elements to a function: I have a function which takes N parameters ``` void func(int, double, char); ``` and a tuple with the matching types ``` std::tuple<int, double, char> tuple; ``` As per this stackoverflow question, I am able to expand the tuple and call the function. Step two: expand a tuple and pass result of calling a member function on each of the elements to a function: Taking it a step further, my tuple contains multiple instances of a class template: ``` template<typename T> struct Foo; std::tuple<Foo<int>, Foo<double>, Foo<char>> ``` `Foo` has a member function, `Foo<T>::get()` which returns a value of type `T`. As per this stackoverflow answer, I have below working code which expands the tuple and calls `element.get()` on each element, ultimately passing the result to `func`. The unfortunate thing is that I've hard-coded the call to `element.get()`. Step three: make the specification of which member function to call on each of the tuple elements generic: (This is what I'm looking for help to do) Is it possible to make this generic? That is, to pass which member function to call to `apply`, and therefore have it as a generic utility? I thought perhaps I could use `std::mem_fn` to wrap a function (`std::mem_fn(&Foo::get)`) and pass the resulting object to `apply`, but that don't work because `Foo` is a class template: `error: ‘template<class T> struct Foo’ used without template parameters` Is there any way to make this generic? Working example below: ``` #include <iostream> #include <tuple> #include <utility> template<size_t...> struct Seq { }; template<size_t N, size_t... Sq> struct GenSeq : GenSeq<N - 1, N - 1, Sq...> { }; template<size_t... Sq> struct GenSeq<0, Sq...> { using type = Seq<Sq...>; }; ///////////////////////////////////// struct Invoker { template<typename Func, typename Tuple, size_t... Sq> static auto invoke(Func func, const Tuple& tuple, Seq<Sq...>) -> decltype(func(std::get<Sq>(tuple).get()...)) { // calls the .get() member on each object in the tuple // I would like to make this generic return func(std::get<Sq>(tuple).get()...); } template<typename Func, typename... Args> static auto apply(Func func, const std::tuple<Args...>& args) -> decltype(invoke(func, args, typename GenSeq<sizeof...(Args)>::type())) { return invoke(func, args, typename GenSeq<sizeof...(Args)>::type()); } }; template<typename Func, typename Tuple> inline auto apply(Func func, const Tuple& tuple) -> decltype(Invoker::apply(func, tuple)) { return Invoker::apply(func, tuple); } /////////////////////////////////////// template<typename T> struct Foo { T i; auto get() const -> decltype(i) { return i; } }; template<typename... Ts> struct Bar { Bar(Ts... ts) : tuple(std::make_tuple(Foo<Ts> { ts }...)) {} std::tuple<Foo<Ts>...> tuple; }; void func(int i, double d, char c) { std::cout << i << ", " << d << ", " << c << std::endl; } int main() { Bar<int, double, char> bar { 4, 1.23, 'a' }; apply(func, bar.tuple); } ```

Original source

Related problems