Generically call member function on each element of a tuple

时光总嘲笑我的痴心妄想 提交于 2019-12-06 08:20:37

问题


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);
}

回答1:


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(); 
    } 
};


来源:https://stackoverflow.com/questions/28642087/generically-call-member-function-on-each-element-of-a-tuple

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!