【问题标题】:Building a list of types during compile time - no C++11在编译时构建类型列表 - 没有 C++11
【发布时间】:2013-10-28 08:38:26
【问题描述】:

我想做精确的this 来获取类型/类的列表。但我不能使用 C++11。有什么建议我可以将类型附加到模板列表吗?

编辑:一些我想做的代码:

#include <iostream>
#include <typeinfo>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/push_back.hpp>
#include <boost/mpl/for_each.hpp>

using namespace std;

class A {};
class B {};
class C {};

typedef boost::mpl::vector<> type1;
// supposed I'd like to have this as a #define macro so someone can call
// REGISTER_CLASS(A) and push the type into a list
typedef boost::mpl::push_back<type1, A> type2;
typedef boost::mpl::push_back<type2, B> type3;
typedef boost::mpl::push_back<type3, C> type4;

template <typename T> struct wrap {};

struct Print
{
      template <typename T> void operator()( wrap<T> t ) const
      {
            cout << typeid( T ).name() << endl;
      }
};

int main()
{
      // this doesn't compile because type4 is a sequence of sequence
      // supposed, I need to "flatten" this list so it's eqv to vector<A,B,C>
      boost::mpl::for_each<type4, wrap<boost::mpl::placeholders::_1> >( Print() );

      // second problem is that I'd like to have typedef of 1 typelist only, not
      // type1, type2, ... typeN, since I don't know the exact number of classes

      return 0;
}

【问题讨论】:

  • 你看Boost.MPL了吗?
  • 是的,虽然我对元编程很陌生。任何进一步的指针表示赞赏。如何将类型 T 附加到例如向量,不用重新typedef类型列表?
  • “将类型 T 附加到例如向量”是什么意思?你能举一个例子来说明你想要完成的事情吗?我现在看不到矢量在哪里播放
  • @surfcode 模板元编程是 100% 功能性的 - 这意味着一切都是不可变的。不可变的push() 实际上是returnCopyWithNewElementPushed()。这意味着 only 的追加方式是 typedefing。
  • @nijansen,Angew:感谢您的回复。编辑帖子以更好地描述问题。

标签: c++ metaprogramming template-meta-programming


【解决方案1】:

首先,您的代码无法编译的实际问题是您正在对操作进行类型定义,而不是它们的结果。改成这样:

typedef boost::mpl::push_back<type1, A>::type type2;
typedef boost::mpl::push_back<type2, B>::type type3;
typedef boost::mpl::push_back<type3, C>::type type4;

现在更全面地了解为什么需要 typedef。

使用元编程技术(如您的情况下的模板元编程)需要心理上的“转变”,因为范式不同于“普通”C++。 C++ 本身是一种命令式语言。元程序是功能性的

就数据结构而言,函数式范式意味着数据结构始终是不可变的。例如,在命令式语言中,您可以这样做:

vector<int> v;
v.push(4);
v.push(7);
v.push(42);
process(&v);
print(v);

在函数式语言中,等效代码为:

v = vector<int>;
v1 = v.push(4);
v2 = v1.push(7);
v3 = v2.push(42);
v4 = process(v3);
print(v4);

或者,使用更多功能符号,如下所示:

print(process(vector<int>().push(4).push(7).push(42)));

在纯函数式(LISP 样式)表示法中,它是:

(print (process (push (push (push (vector<int>) 4) 7) 42)))

另一种说法是,在函数式语言中,数据结构不是存储的,它们是产生的。

回到 C++ 模板元编程和 Boost.MPL,这意味着将类型 T 附加到类型列表(MPL 向量)v 的唯一方法是将 boost::mpl::push_back&lt;v, T&gt;::type 传递到您要处理“带有T 的向量被推回”。为了维护DRY,您通常使用 typedef 为“带有T 推回的向量”。


很遗憾,这意味着无法按照您的意愿创建REGISTER_CLASS 宏。您可以通过更多元编程(包括使用 Boost.Preprocessor)来制作类似的东西。

这个想法是使用 Boost.Preprocessor slot 来保存类型列表的最后一个标识符。与其直接调用REGISTER_CLASS 宏,不如直接调用#included,类名通过另一个宏传递。像这样的:

internal_register.hpp

//This file MUST NOT have include guards

#ifndef CLASS_TO_REGISTER
  #error You must define CLASS_TO_REGISTER before executing REGISTER_CLASS()
#endif

typedef boost::mpl::push_back<
  CURRENT_TYPELIST(),
  CLASS_TO_REGISTER
>::type BOOST_PP_CAT(registered, BOOST_PP_INC(BOOST_PP_SLOT(1)));

#undef CLASS_TO_REGISTER

#define BOOST_PP_VALUE BOOST_PP_SLOT(1) + 1
#include BOOST_PP_ASSIGN_SLOT(1)

registration.hpp

typedef boost::mpl::vector<>::type registered0;

#define BOOST_PP_VALUE 0
#include BOOST_PP_ASSIGN_SLOT(1)

#define REGISTER_CLASS() "internal_register.hpp"

#define CURRENT_TYPELIST() BOOST_PP_CAT(registered, BOOST_PP_SLOT(1))

ma​​in.cpp(或任何其他用法):

#include "registration.hpp"

class A {};
class B {};
class C {};

#define CLASS_TO_REGISTER A
#include REGISTER_CLASS()

#define CLASS_TO_REGISTER B
#include REGISTER_CLASS()

#define CLASS_TO_REGISTER C
#include REGISTER_CLASS()

template <typename T> struct wrap {};

struct Print
{
      template <typename T> void operator()( wrap<T> t ) const
      {
            cout << typeid( T ).name() << endl;
      }
};

int main()
{
      boost::mpl::for_each<CURRENT_TYPELIST(), wrap<boost::mpl::placeholders::_1> >( Print() );

      return 0;
}

当涉及多个翻译单元时,需要进行更多调整才能正常工作(在这种情况下有些事情根本无法完成,如果将适当的标识符放在匿名命名空间中,有些事情可以完成)。但它应该作为一个起点。

【讨论】:

  • +1,这是一个很好的起点,谢谢!虽然这意味着,我需要有 1 个文件(可能是标题),其中包括 registration.h、a.h、b.h、c.h(所有类)并在那里进行注册。每当添加新类时,都需要将其添加到该文件中,这可能很困难。另外,需要考虑例如链接的.so中的类。
  • 对于 boost::mpl:: 向量可以推送多少个也有限制吗? (例如,对于 boost::variant,您只能做 20 个,最多 50 个)
  • @surfcode * 是的,您最终可以生成相当多的基础设施来让这样的事情正常工作。但这对于元程序来说是可以预料的。它们不适合胆小的人。你知道你的项目的细节,所以只有你才能权衡不同方法的利弊。 *至于限制,默认为20,但可以通过configuration macros配置。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-06-23
  • 1970-01-01
  • 2015-06-02
  • 1970-01-01
  • 2019-10-11
  • 2020-04-20
  • 2014-07-28
相关资源
最近更新 更多