Apply the same function to each data item. Conversion on heterogeneous types.

Consider the following structure:

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, a pointer to a function, a functor, whatever in transformTest (), which does not concern me now.

What is the best way to do this?

+4
source share
4 answers

Step 1: Wrap Your Data In tuple. Maybe temporary.

Step 2: Wrap your call in a functor.

Step 3: Write a tuple_foreach, which applies the functor to each element of a tuple.

For step 1, I would advise leaving the data where it is and just using it std::tieto create links 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 . 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 last, , , , google.

+1

:

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

, someTransform().

, Boost Fusion. , , . .

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

Boost.Fusion - , , , ++ 11 std::tuple :

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

sequence/static_range ( std::tuple s) , std::get .

In addition, I believe that the code is quite simple, but it should be noted that with this method the order in which ftuple is called in each element is undefined.

Usage will look like this:

    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 the holistic progression, each of which will have a type typename std::common_type<T,int>::type.

+1
source

All Articles