4

Consider the following struct:

struct Test {
   char a;
   short b;
   int c;
   long long d;
   void transformTest() {
      // Pseudo
      foreach datamember (regardless of type) of Test
          call someTransform(datamember)
   }
};

We could also pass a lambda, function pointer, functor, whatever into the transformTest(), that's not my concern as of right now.

What's the best way to do this?

4

4 回答 4

3

最好的方法是明确地做到这一点:

someTransform(a);
someTransform(b);
someTransform(c);
someTransform(d);

当然,您需要适当数量的someTransform().

如果你真的,真的不喜欢那样,总会有Boost Fusion。有了它,您可以将您的值放在图书馆理解的结构中,然后可以迭代。对于简单的用例,这不值得做。

于 2013-10-07T12:26:21.467 回答
3

听起来像是Boost Fusion及其for_each()功能与BOOST_FUSION_ADAPT_STRUCT结合的案例。这东西可以创造一些奇迹!这是一个有关如何执行此操作的示例:

#include <boost/fusion/include/algorithm.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <iostream>

using namespace boost::fusion;

struct Print
{
    template <typename T>
    void operator()( T && t ) const
    {

        std::cout << t << ' ';
    }
};

struct Test {
    char a;
    short b;
    int c;
    long long d;

    void printTest() const;
};

BOOST_FUSION_ADAPT_STRUCT(
    Test,
    (char, a)
    (short, b)
    (int, c)
    (long long, d)
    )

void Test::printTest() const
{
    for_each ( *this, Print() );
}

int main()
{
    const auto t = Test();
    t.printTest();
}
于 2013-10-07T13:12:11.837 回答
1

While Boost.Fusion is a good solution, I thought I'd add that in C++11 you could use std::tuple like this:

    template <unsigned ... indices>
    struct sequence
    {
        typedef sequence type;
    };

    template <unsigned size, unsigned ... indices>
    struct static_range : static_range<size-1,size-1,indices...> {};

    template <unsigned ... indices>
    struct static_range<0, indices...> : sequence<indices...> {};

    template <class Function, class Tuple, unsigned ... indices>
    auto transform_impl(const Tuple & t, Function f, sequence<indices...>) ->
        std::tuple<decltype(f(std::get<indices>(t)))...>
    {
         return std::make_tuple(f(std::get<indices>(t))...);
    }

    template <class Function, class Tuple>
    auto transform_tuple(const Tuple & t, Function f) ->
        decltype(transform_impl(t, f, static_range<std::tuple_size<Tuple>::value>()))
    {
        return transform_impl(t, f, static_range<std::tuple_size<Tuple>::value>());
    }

The sequence/static_range classes have been invaluable in my code in expanding classes (not just std::tuples) by indices so that I can std::get them.

Other than that, I think the code is fairly straightforward, it should be noted however that with this method the order in which f is invoked on each tuple element is undefined.

Usage would look like:

    std::tuple<char, short, int, long long> t;
    struct addone
    { template <class T> auto operator()(T t) -> decltype(t+1) {return t + 1;}};
    auto t2 = transform_tuple(t, addone());

The resulting tuple will not have the same types as the input tuple due to integral promotion, each will have type typename std::common_type<T,int>::type.

于 2013-10-07T13:50:45.860 回答
1

第 1 步:将数据包装在tuple. 可能是临时的。

第 2 步:将您的可调用对象包装在仿函数中。

第 3 步:编写 a tuple_foreach,将仿函数应用于 a 的每个元素tuple

对于第 1 步,我建议将数据保留在原处,并仅使用 astd::tie创建tuple引用。

对于第 2 步,一个简单的完美转发函子如下所示:

#define RETURNS(X) ->decltype(X) { return (X); }
struct foo_functor {
  template<typename... Args>
  auto operator()(Args&&... args) const
    RETURNS( foo( std::forward<Args>(args)... ) )
};

它表示调用的重写函数集foo,并将其包装到单个对象中,该对象自动调度对 . 的适当重载的任何调用foo

对于第 3 步,这并不难。只需使用索引技巧在 a 的每个元素上运行代码tuple

void do_in_order() {}
template<typename Lambda, typename... Lambdas>
void do_in_order( Lambda&& closure, Lambdas&&... closures ) {
  std::forward<Lambda>(closure)();
  do_in_order( std::forward<Lambdas>(closures)... );
}
template<unsigned... Is>
struct seq { typedef seq<Is> type; }
template<unsigned Max, unsigned... Is>
struct make_seq:make_seq<Max-1, Max-1, Is...> {};
template<unsigned... Is>
struct make_seq<0,Is...>:seq<Is...> {};

template<typename Tuple, typename Functor, unsigned... Is>
void foreach_tuple_helper( seq<Is...>, Tuple&& t, Functor&& f ) {
  do_in_order(
    [&]{ std::forward<Functor>(f)(std::get<Is>(std::forward<Tuple>(t))); }...
  );
}
template<typename Tuple, typename Functor>
void foreach_tuple( Tuple&& t, Functor&& f ) {
  foreach_tuple_helper( make_seq<std::tuple_size< typename std::decay<Tuple>::type >::value>(), std::forward<Tuple>(t), std::forward<Functor>(f) );
}

do_in_order我上次检查时在 clang 中不起作用,但是在你的编译器上工作的等效索引技巧应该不难用谷歌搜索。

于 2013-10-07T18:01:21.610 回答