用C++03的方式简化接口声明

3
作为减少复制/粘贴相同代码的繁琐工作的一种方式,我转向了黑暗面,并使用宏来代替我完成这项工作。
请注意,这段代码是从一个更大的生产代码中摘取出来的,在没有像这样的宏的帮助下,这个任务更加痛苦。具体来说,它是由一个字符串驱动的静态和虚拟函数的混合体。
现在我知道,宏可能会让你陷入麻烦,以这种方式使用它们相当“臭”,因此我想要更好的东西,但我很难想出比这更简单、更简洁的东西:
#define LAZY(name)\
   static const char * identify() { return name; }\
   virtual const char * getName() { return identify(); }

class A{
public:
   LAZY("class A")

   void foo(){
      std::cout << getName() << std::endl;
   }
};

class B: public A{
public:
   LAZY("class B")
};

std::string somevar( B::identify() );

int main(void){
   B b1;
   B.foo();
}

我采用的其他方法(以及它们失败的原因)如下:

template<class T>
class Base{
public:
   virtual const char * getName(){ return T::identify(); }

   // All other functions driven by string. IE:
   static void register(){ someFactory::reg( T::identify() ); }
   virtual unsigned int getHash(){ return someHashFoo( T::identify() ); }
};

class A: public Base<A> {
public:
   static const char * idenfity(){ return "class A"; }

   void foo(){ std::cout << getname() << std::endl; }
};

class B: public A, public Base<B> {
  // Conflict due to multi-inheritance.
};

另一种失败的方法是由于每个实例浪费内存,而且有点复杂:

class FaceBase{
public:
   virtual const char * getName() =0;
};

template<class T>
class ImplBase: public FaceBase {
public:
   virtual const char * getName(){ return T::identify(); }
};

class A{
public:
   FaceBase & common;

   static const char * identify(){ return "class A"; }

   A(): common(ImplBase<A>()){}

   virtual void foo(){ std::cout << common.getName() << std::endl; }
};

class B: public A{
   static const char * identify(){ return "class B"; }

   B(){ common = ImplBase<B>(); }
};

你为什么一开始需要这个“类名”系统?typeid有什么问题吗? - slaphappy
1
那个名称可以是任何东西,不仅仅是“class A”。类型ID通常也会进行名称混淆,并且在不同平台上也不会保持一致。 - DiscoStu
1
当然,除了标识一个类,这个名称还有什么作用? - slaphappy
实际用例是将字符串显示给用户。然而,在完整项目中,还有其他类型的类似宏,使用符号而不是字符串作为参数,产生类似的效果(驱动一堆静态数据函数)。因此,关注“为什么选用字符串”并不像避免样板文件或浪费内存分配那样重要。 - DiscoStu
1
澄清一下:type_info::namemight返回一个混淆的名称,但这只是一个实现细节,因为有趣的部分是operator== - slaphappy
1
你也许可以通过虚拟继承使第二个方案工作。但这需要一些工作,而且最终可能会比宏解决方案更混乱,所以我认为你的方式很可能是最好的。 - zindorsky
1个回答

6
您并没有提出具体的问题,但是假设您想知道“如何在不使用宏的情况下以一种干净的方式完成这项任务?”,并且进一步假设您所说的“更大”的任务是一个完整的自定义RTTI系统,那么答案是:“您不能”。
我知道的每个完成此类事情的大型项目(MFC、Qt、LLVM)都采用以下几种方法之一:
- 使用宏。(MFC 并在某种程度上也包括 Qt) - 使用自定义代码生成器。(Qt 并在某种程度上也包括 LLVM) - 编写样板代码。(LLVM)

1
所以我想要更好的东西,但是我很难想出来... 我受到了MFC和Qt的部分启发,这很不错。 - DiscoStu
1
放心,这是一种常见且良好的做法。不要被普遍的“避免宏”之类的东西所误导,看看原因,你会发现没有任何不良因素适用于此。与所有良好因素不同。 - Balog Pal
2
我不同意没有任何负面因素适用于这里,但无论如何这都不重要。当其他方法无法合理实现时,预处理器应该是你的最后选择,但这是一个这样的情况。当替代方案更糟糕时,预处理器有多少问题都不重要。 - Sebastian Redl

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