我最近获得了一些我希望移植到Linux的代码。但是在一个头文件中,我有一些好奇的代码,我希望有人可以解释一下。在头文件中,在定义其他类的命名空间内,我有以下内容:
#define CREATE_SINGLETON_METHODS(s) \
private: \
friend class Singleton<c>; \
##c(); \
virtual ~##c();
我理解##
是令牌粘贴操作,但我无法弄清楚为什么原作者(我不知道,也无法联系)使用它。我有一个看起来像这样的实现类:
class MapManager : public Singleton<MapManager> {
CREATE_SINGLETON_METHODS(MapManager)
private:
...
编译时,我收到以下错误:
error: pasting ";" and "MapManager" does not give a valid preprocessing token
这会在Windows和一些早期版本的gcc(pre 4.x)上编译find。关于这里可能会发生什么的任何想法?谢谢!
答案 0 :(得分:2)
我使用自己的通用单例模板: 尝试用这个模板替换那些icky宏:
#ifndef SINGLETON_HPP_STYLET
#define SINGLETON_HPP_STYLET 0
/*
*
*
* Generic Singleton implementation
*
*
*/
template <class T>
class SingletonHolder : public T
{
public:
static SingletonHolder<T>& getInstance()
{
static SingletonHolder<T> instance;
return instance;
}
private:
SingletonHolder()
{
};
virtual ~SingletonHolder()
{
};
};//class SingletonHolder
#endif //SINGLETON_HPP_STYLET
<强> // --------------------------------------- --------------------------- 强>
<强> USAGE:强>
class SomeClass;
typedef SingletonHolder<SomeClass> SomeClassSingleton;
SomeClassSingleton::getInstance().doSomething();
答案 1 :(得分:1)
在构造函数没用之前,我觉得像##一样。析构函数中的那个用于将波浪号粘贴到typename。也许是原作者复制粘贴,并没有得到编译错误,因为它是一个旧版本,所以没有发现错误。
答案 2 :(得分:1)
尝试将宏更改为:
#define CREATE_SINGLETON_METHODS(c) \
private: \
friend class Singleton<c>; \
c(); // Note the change on this line! \
virtual ~##c();
我拿出了一个##运算符。好像 ”;”正在与“c();”合并在gcc 4中。
答案 3 :(得分:1)
我总是发现这种预处理器“魔术”(读取垃圾)会造成比它值得更多的麻烦。在某些情况下,这是一个必要的邪恶,但我不认为这适用于此。我会在代码中重写它的任何实例,并消除那些可怕的事情。
答案 4 :(得分:1)
'stringizing'和令牌粘贴('#
'和'##
')的预处理程序运算符只能在宏参数上可靠地使用。此外,您通常需要使用间接级别来使它们在所有情况下都能正常工作(特别是在将它们用于本身就是宏的情况下)。
见
了解更多细节。
答案 5 :(得分:1)
前一段时间我遇到过类似的问题。尝试运行:
g ++ -E mytestfile.h
(当然使用您自己的文件名)在命令行上查看预处理器的结果。它可能有助于显示正在发生的事情。我正在运行gcc 3.4.4并获得您描述的仅在4.x中发生的相同错误(尽管代码在Visual Studio 2008中正常工作)。
消除令牌化器将解决问题:
#define CREATE_SINGLETON_METHODS(c) \
private: \
friend class Singleton<c>; \
c(); \
virtual ~c();
此代码为我提供了以下预处理器结果(使用-E选项从命令行复制):
private: friend class Singleton<MapManager>; MapManager(); virtual ~MapManager();
编译器很满意。
根据http://gcc.gnu.org/onlinedocs/gcc-4.0.4/cpp/Tokenization.html:
在这种情况下,一旦输入文件被分解为标记,标记边界就不会改变,除非使用“##”预处理运算符将标记粘贴在一起。 编译器不会重新标记预处理器的输出。每个预处理令牌都成为一个编译器令牌。
'##'会破坏标记化规则。根据你得到的错误,预处理器最有可能给编译器一个带有“; MapManager()”的令牌集,其中4.x编译器显然不喜欢。
此处的一些信息(http://gcc.gnu.org/onlinedocs/cpp/Concatenation.html#Concatenation)也值得注意:
通常会发现不必要的用途 复杂宏中的'##'。如果你得到 这个警告,很可能是你 可以简单地删除'##'。
希望有所帮助。
答案 6 :(得分:0)
你是如何在任何编译器上编译的?首先它应该是:
#define CREATE_SINGLETON_METHODS(c) \
private: \
friend class Singleton<c>; \
##c(); \
virtual ~##c();
因为我假设它是定义友元类的宏参数,私有构造函数和私有析构函数。
答案 7 :(得分:-1)
这看起来像是在自动单例创建方面做得不好。一般情况下,如果您可以使用虚拟函数或虚拟继承以及一些聪明的模板技巧,则可以不使用宏。
如果我是你,我会手动重写受影响的类的相关代码。对于像这样的东西,宏是坏消息。