在C ++中编译时间生成块

时间:2014-10-26 00:45:22

标签: c++ templates c++11 macros metaprogramming

我的服务器中有一个结构体,我想用C ++中的宏或模板生成它,因为它有很多多余的东西:

struct MyBlock {
  void Merge(const MyBlock& from) {
    if (apple.HasData()) {
      apple.Merge(from.apple);
    }
    if (banana.HasData()) {
      banana.Merge(from.banana());
    }
    ...
  }

  void Clear() {
    apple.Clear();
    banana.Clear();
    ...
  }

  void Update(const SimpleBlock& simple_block) {
    if (simple_block.apple.Updated()) {
      apple.Add(simple_block.apple);
    }
    if (simple_block.banana.Updated()) {
      banana.Add(simple_block.banana);
    }
    ...
  }
  Fruit apple;
  Fruit banana;
  Animal dog;
  Animal cat;
  ...
}

struct SimpleBlock {
  SimpleFruit apple;
  SimpleFruit banana;
  SimpleAnimal dog;
  SimpleAnimal cat;
  ...;
}

我想在苹果和狗这两个块中定义更多变量。我还想定义更多对这样的块。但它涉及许多琐碎的工作。所以我的问题是如何使用宏,模板或其他一些C ++功能(包括C ++ 11)在编译时生成这些块?

我不使用集合来存储这些变量的原因是因为MyBlock结构将作为参数传递到另一个模板类中,该模板类将在运行时动态分配和释放该块。它实际上是一个可以定期聚合的线程局部块。

3 个答案:

答案 0 :(得分:2)

预处理器列表迭代足够简单:

#define M_NARGS(...) M_NARGS_(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define M_NARGS_(_10, _9, _8, _7, _6, _5, _4, _3, _2, _1, N, ...) N

#define M_CONC(A, B) M_CONC_(A, B)
#define M_CONC_(A, B) A##B
#define M_ID(...) __VA_ARGS__

#define M_LEFT(L, R) L
#define M_RIGHT(L, R) R

#define M_FOR_EACH(ACTN, ...) M_CONC(M_FOR_EACH_, M_NARGS(__VA_ARGS__)) (ACTN, __VA_ARGS__)

#define M_FOR_EACH_0(ACTN, E) E
#define M_FOR_EACH_1(ACTN, E) ACTN(E)
#define M_FOR_EACH_2(ACTN, E, ...) ACTN(E) M_FOR_EACH_1(ACTN, __VA_ARGS__)
#define M_FOR_EACH_3(ACTN, E, ...) ACTN(E) M_FOR_EACH_2(ACTN, __VA_ARGS__)
#define M_FOR_EACH_4(ACTN, E, ...) ACTN(E) M_FOR_EACH_3(ACTN, __VA_ARGS__)
#define M_FOR_EACH_5(ACTN, E, ...) ACTN(E) M_FOR_EACH_4(ACTN, __VA_ARGS__)
//.. extend this to higher numbers with some copy&paste


#define MYBLOCK(...) struct MyBlock { \
  void Merge(const MyBlock& from) { \
      M_FOR_EACH(BLOCK_MERGE, __VA_ARGS__) \
  } \
  void Clear() { \
      M_FOR_EACH(BLOCK_CLEAR, __VA_ARGS__) \
  } \
  void Update(const SimpleBlock& simple_block) { \
      M_FOR_EACH(BLOCK_UPDATE, __VA_ARGS__) \
  } \
  M_FOR_EACH(BLOCK_FIELD, __VA_ARGS__) \
}

#define BLOCK_MERGE(F) if (M_ID(M_RIGHT F).HasData()) { \
  M_ID(M_RIGHT F).Merge(from.M_ID(M_RIGHT F)); \
}
#define BLOCK_CLEAR(F) M_ID(M_RIGHT F).Clear;
#define BLOCK_UPDATE(F) if (simple_block.M_ID(M_RIGHT F).Updated()) { \
  M_ID(M_RIGHT F).Add(simple_block.M_ID(M_RIGHT F)); \
}
#define BLOCK_FIELD(F) M_ID(M_LEFT F) M_ID(M_RIGHT F);


#define SIMPLEBLOCK(...) struct SimpleBlock { M_FOR_EACH(SIMPLE_DECL, __VA_ARGS__) }
#define SIMPLE_DECL(F) M_CONC(Simple, M_ID(M_LEFT F)) M_ID(M_RIGHT F);

#define FIELDS (Fruit, apple),(Fruit,banana),(Animal,dog),(Animal,cat)

MYBLOCK(FIELDS);
SIMPLEBLOCK(FIELDS);

以现有格式将必要的其他成员变量添加到FIELDS,并将它们添加到MYBLOCKSIMPLEBLOCK发出的结构中。 (请记住使用更多迭代扩展M_FOR_EACH ...使用几个ctrl + c,ctrl + v很容易。)

答案 1 :(得分:1)

template <typename SimpleT>
class BlockTemplate
{
public:
void Merge(const BlockTemplate& from) {
    if (HasData()) {
      Merge(from.simpleData);
    }
}

void Update(const SimpleT& simple_block) {
    if (simple_block.Updated()) {
      Add(simple_block.data);
    }
}

protected:
SimpleT simpleData;
};

现在,您可以创建类型为BlockTemplate<SimpleFruit>BlockTemplate<SimpleAnimal>等的对象。在BlockTemplate继承自抽象类型之后,您还可以存储指向容器中所有这些BlockTemplate对象的指针。或者,更好的是,使用新的类型擦除方法 - 例如boost::type_erasure::any

编辑:如果您不想以这种方式使用容器,您还可以使BlockTemplate可变参数并存储一组不同(类型)SimpleT对象的元组并修改MergeUpdate相应地发挥作用。这样做的问题是跟踪SimpleT个对象变得更加困难 - std::tuple不允许您提供姓名。您将这些值称为get<N>(tupleData)

答案 2 :(得分:0)

您不使用集合的原因说明听起来像是一些优化。你有没有测量过?

无论如何,一个简单的解决方案是存储指向集合中对象的指针。

然后你可以遍历集合。