在可变参数模板的参数中解构POD结构

时间:2014-09-15 18:41:13

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

我想迭代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)。我不知道是否有可能,但我希望它是可能的。有没有人知道如何才能做到这一点?

4 个答案:

答案 0 :(得分:3)

您要找的是反射

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

答案 1 :(得分:2)

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

但是,据我所知,不可能简单地枚举元素名称而不了解它们。 Boost.MPL(元编程库),可以说是元编程最复杂的用途之一,并没有提供这样的设施,作者在an academic paper中注意到这是不可能的:

  

不幸的是,这种安排不容易受到C ++给我们的编译时类型内省功能的影响:   没有办法知道成员的名字是什么,即使我们假设他们根据他们的名字命名   对于上述某些惯例,没有办法知道有多少成员。

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

答案 2 :(得分: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成员提供元组构造(请参阅struct_as_tuple)。

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

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

答案 3 :(得分: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);
}

这是非常通用的,只为您感兴趣的类型decompose专门设置T,它可以优雅地发出声音。