【问题标题】:deconstruct POD struct in arguments for variadic template在可变参数模板的参数中解构 POD 结构
【发布时间】:2014-11-09 08:55:57
【问题描述】:

我想遍历 POD 结构的成员,我能想象它可以工作的唯一方法是使用模板。但为此,我需要一些东西来解决这个问题:

template <typename ...T>
void bar(T ...t) {
    /* ... do something ... */
}

template <typename T>
void foo(T t) {
    bar(magical_decompose(t));
}

struct MyPod {
    int i;
    double d;
    float f;
};

int main() {
    MyPod myPod = {1,2,3};
    foo(myPod);
}

bar(magical_decompose(t)); 应该被模板转换成bar(t.i, t.d, t.f)。我不知道这是否可能,但我希望它是可能的。有谁知道我如何做到这一点?

【问题讨论】:

  • 它需要运行时反射。 C++ 是一种静态编译语言。对此没有“实际”支持。只需进行手动序列化(例如添加to_array_ptr成员函数)。
  • @texasbruce。不,它不需要运行时反射,因为类型在编译时是已知的。

标签: c++ c++11 variadic-templates


【解决方案1】:

您要查找的内容称为反射

目前该语言没有内置支持。 C++ 委员会中有一个working group,以及一些使用宏或其他技巧以某种方式模拟它的库。我能想到的最简单的解决方案是使用宏,它在Boost.Fusion 中使用 BOOST_FUSION_ADAPT_STRUCT 宏实现。

【讨论】:

    【解决方案2】:

    可以确定结构是否包含某些成员名称(使用 SFINAE 技巧),并通过offsetof 确定它们的顺序。例如,如果您知道成员名称都是一个字母长,则可以使用这些技巧来枚举结构成员(尽管生成的代码至少不可维护)。

    但是,据我所知,不可能在不了解元素名称的情况下简单地枚举元素名称。 Boost.MPL(元编程库)可以说是元编程最复杂的用途之一,但不提供这样的工具,作者在an academic paper 中指出这是不可能的:

    不幸的是,这种安排不受 C++ 赋予我们的编译时类型自省能力的影响: 没有办法知道成员的名字是什么,即使我们假设他们是根据 按照上面的一些约定,没有办法知道有多少成员。

    (最后一句对于具有任意多个成员的结构是正确的,但是通过将成员的数量限制为例如 20,您可以使用 SFINAE 解决固定命名方案的问题。

    【讨论】:

    • 谢谢,我想我只是等到它成为 C++ 的一部分。这不是强制性的,但它会简化很多代码。
    【解决方案3】:

    如果您愿意明确描述结构成员,您可能会接近您想要的。

    #include <iostream>
    #include <tuple>
    #include <functional>
    
    using namespace std;
    
    struct test
    {
        int i = 121;
        double j = 234.0;
        string k = "Some k";
    };
    
    struct anotherStruct
    {
        double t = 121.8;
    };
    
    struct undescribedStruct
    {
        string t = "Some undescribed";  
    };
    
    tuple<int&, double&, string&>   struct_as_tuple(test& t)
    {
        return tie( t.i, t.j, t.k);
    }
    
    tuple<double&>  struct_as_tuple(anotherStruct& t)
    {
        return tie( t.t );
    }
    
    //make_indices && Co thanks to sigidagi
    //see http://cpptruths.blogspot.de/2012/06/perfect-forwarding-of-parameter-groups.html 
    template<unsigned...> struct index_tuple{};
    
    template<unsigned I, typename IndexTuple, typename... Types>
    struct make_indices_impl;
    
    template<unsigned I, unsigned... Indices, typename T, typename... Types>
    struct make_indices_impl<I, index_tuple<Indices...>, T, Types...>
    {
      typedef typename
        make_indices_impl<I + 1, 
                          index_tuple<Indices..., I>, 
                          Types...>::type type;
    };
    
    template<unsigned I, unsigned... Indices>
    struct make_indices_impl<I, index_tuple<Indices...> >
    {
      typedef index_tuple<Indices...> type;
    };
    
    template<typename... Types>
    struct make_indices 
      : make_indices_impl<0, index_tuple<>, Types...>
    {};
    
    void bar()
    {
        std::cout << endl;
    }
    
    template <typename T, typename... Args>
    void bar(T&& t, Args&&... args) 
    {
        std::cout << "T: [" << t << "] ";
        return bar(forward<Args>(args)...);
    }
    
    template <unsigned... Indices, class... Args>
    void foo_forward_call_impl(index_tuple<Indices...>,
                               std::tuple<Args...> tuple )
    {
      return bar(std::get<Indices>(tuple)...);
    }
    
    template<class... Args>
    void foo_forward_call(std::tuple<Args...> tuple )
    {
       typedef typename make_indices<Args...>::type Indices;
       return foo_forward_call_impl(Indices(), tuple);
    }
    
    template <typename T>
    void foo(T&& t)
    {
        return foo_forward_call( struct_as_tuple(t) );  
    }
    
    int main() 
    {
        test t1;
        foo(t1);
    
        anotherStruct t2;
        foo(t2);
    
        undescribedStruct t3;
        //will error foo(t3);
    
        // your code goes here
        return 0;
    }
    

    您基本上必须为每种支持的类型提供结构成员的元组构造(请参阅 struct_as_tuple)。

    foo 然后从传入的类型生成元组并将其传递给元组展开实现。

    这可能不是你想要的,但它是我目前能想象的最接近它的......

    【讨论】:

    • 是的,这就是重点。我希望明确不要为每个结构编写特殊代码,但感谢您的回答,这很好。
    • 好的。很公平......我不会等到他们把你想要的东西放在标准中;-)。
    【解决方案4】:

    正如@sbabbi 指出的那样,目前没有内置语言支持,所以它不会免费提供,你必须自己做一些事情。在 c++14 中,用最少的努力:

    #include <type_traits>
    
    // specialize this trait to register T
    template<class T>
    struct decompose;
    
    template<class T, class F>
    void magical_decompose(T&& t, F&& f)
    {
        decompose<std::decay_t<T>>::apply(t, f);
    }
    
    template <typename ...T>
    void bar(T ...t) {
    }
    
    template <typename T>
    void foo(T t) {
        magical_decompose(t, [](auto&&... ts)
        {
            bar(static_cast<decltype(ts)>(ts)...);
        });
    }
    
    struct MyPod {
        int i;
        double d;
        float f;
    };
    
    // register MyPod
    template<>
    struct decompose<MyPod>
    {
        // works for both const & non-const
        template<class T, class F>
        static void apply(T& t, F& f)
        {
            f(t.i, t.d, t.f);
        }
    };
    
    int main() {
        MyPod myPod = {1,2,3};
        foo(myPod);
    }
    

    这很通用,只需将decompose 专门用于您感兴趣的类型T,它就可以优雅地插入。

    【讨论】:

      猜你喜欢
      • 2019-08-04
      • 2019-03-23
      • 2020-04-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多