对每个数据成员应用相同的函数 - 对异构类型进行转换

Apply the same function to each data member - Transform on heterogeneous types sort of thing

本文关键字:异构 类型 转换 函数 数据成员 应用      更新时间:2023-10-16

请考虑以下结构:

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

我们还可以将lambda,函数指针,functor,任何东西传递到transformTest()中,这不是我现在关心的问题。

最好的方法是什么?

最好的方法是明确地执行此操作:

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

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

如果你真的,

真的不喜欢这样,总会有Boost Fusion。 有了它,您可以将您的值放在一个库理解的结构中,然后可以迭代。 对于简单的用例,这是不值得的。

听起来像是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();
}

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

第 2 步:将可调用对象包装在函子中。

第 3 步:编写一个将函子应用于tuple的每个元素的tuple_foreach

对于第 1 步,我建议将数据保留在原处,只需使用 std::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 步,这并不难。 只需使用索引技巧在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 中不起作用,但适用于编译器的等效索引技巧应该不难谷歌。

虽然 Boost.Fusion 是一个很好的解决方案,但我想我会在 C++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它们。

除此之外,我认为代码相当简单,但是应该注意的是,使用此方法,在每个元组元素上调用f的顺序是未定义的。

用法如下所示:

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

由于积分提升,生成的元组将不具有与输入元组相同的类型,每个元组将具有类型 typename std::common_type<T,int>::type