在可变模板中拆解传递的POD结构体参数

4

我想遍历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成员函数)。 - SwiftMango
2
@texasbruce。不需要运行时反射,因为类型在编译时已知。 - Arne
C++1z中的编译时反射? - PaperBirdMaster
4个回答

3
你要找的东西被称为“反射”。 目前该语言没有内置支持。C++委员会中有一个工作组,一些库使用宏或其他技巧在某些方面模拟它。我能想到的最简单的解决方案使用宏,在Boost.Fusion中用BOOST_FUSION_ADAPT_STRUCT宏实现。

2
可以使用 SFINAE 技巧确定一个结构体是否包含某些成员名,并通过 `offsetof` 确定它们的顺序。例如,如果您知道成员名称都是一个字母长,您可以使用这些技巧枚举结构体成员(尽管所得到的代码将不可维护)。
然而,据我所知,仅仅通过了解结构体元素的名称而不知道任何有关它们的信息是不可能的。 Boost.MPL(元编程库)可以说是最复杂的元编程之一,但它并没有提供这样的功能,在 一篇学术论文 中,作者指出这是不可能的:
“不幸的是,这种排列方式不能受到 C++ 给我们的编译时类型内省能力的影响:没有办法找出成员的名称,即使我们假设它们按照上述某种约定命名,也无法知道有多少成员。”
最后一句话对于具有任意成员的结构是正确的,但通过将成员数量限制为例如20,您可以使用SFINAE解决固定命名方案的问题。

谢谢,我想我会等到它成为C++的一部分。这不是强制性的,但它会简化很多代码。 - Arne

1
如果您愿意明确描述结构成员,您可能会接近您想要的内容。
#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根据传入的类型生成元组,并将其传递给元组展开实现。
这可能不是你想要的,但在此时此刻,这是我能想到的最接近它的方法...

是的,这就是重点。我想要明确地不为每个结构体编写特殊代码,但还是谢谢你的回答,很好。 - Arne
好的。挺好的...不过我不会等到他们把你想要的东西放入标准中;-)。 - ds27680

0
正如@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);
}

这是相当通用的,只需为您感兴趣的类型 T 专门化 decompose,它就可以优雅地发挥作用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接