最近把之前的项目升级到了 Cocos2d-x 3.17.2版本,在解决了一系列的问题之后,编译时报错:
error C3130: Internal Compiler Error: failed to write injected code block to PDB
碰到这个之后,我搜索了一下,基本上所有人给出的方案都是将编译选项中关于调试信息生成,换成C7兼容,这样就可以继续编译。
我尝试了一下,将工程的调试信息换成了C7兼容,能顺利运行,但是编译的时间非常的长(每一次链接都需要10分钟以上),而且,链接器的内存消耗最高峰值达到了6G,这个明显存在一些没解决的问题。在尝试几次之后,我取消了C7兼容,换回了之前的模式。
重新观察了产生C3130错误的地方,发现这些错误都是集中在clone这个函数,都是在clone这个地方报错。只要是继承了Clonable接口的子类,都有这个问题,进一步的分析,找到了错误的真实原因:
/**
* Interface that defines how to clone an Ref.
* @lua NA
* @js NA
*/
class CC_DLL Clonable
{
public:
/** Returns a copy of the Ref. */
virtual Clonable* clone() const = 0;
/**
* @js NA
* @lua NA
*/
virtual ~Clonable() {};
/** Returns a copy of the Ref.
* @deprecated Use clone() instead.
*/
CC_DEPRECATED_ATTRIBUTE Ref* copy() const
{
// use "clone" instead
CC_ASSERT(false);
return nullptr;
}
};
注意这个代码,在Clonable基类中,返回值是 Clonable * , 然后在它的派生类中,返回值变了:
* @return A clone action.
*/
virtual Action* clone() const
{
CC_ASSERT(0);
return nullptr;
}
变成了Action *,由于C++对函数的识别只基于函数名和输入参数,不会识别返回值,因此,在继承的时候,这种写法是允许的,VC内部怎么实现,我不清楚,但是,我猜测应该是给每一个派生类,生成多个clone函数,相同的代码来对应不同的返回值,但这样会导致一个问题,如果派生链过长,会让编译生成代码成倍增加。
Clonable 这个类,仅仅是一个接口约定,整个工程基本上不会用到Clonable这个层级,于是,我取消了 Action 类派生于Clonable接口:
/**
* @brief Base class for Action objects.
*/
class CC_DLL Action : public Ref //, public Clonable
{
public:
找到CCAction这个头文件,把 public Clonable注释掉。
接下来,重新编译整个工程,再也没有C3130错误,而且链接时间大大缩短(2分钟以内),生成的PDB也从之前的400多MB,降低到96MB,内存的消耗,最高3个G多一些。
这个只是临时的解决方案,虽然不影响代码执行,但还是破坏了Cocos2d-x的派生体系结构,而且仅仅解决了Action类,还有很多其他的类没处理,所以PDB也没有缩减到理想大小。所以,解决方式还是很 Ugly。
最佳的解决方案,是重新设计 Clonable 的体系,每一个派生类都使用标准的 Clonable * clone() const 函数定义,写一个宏或则模板函数来转换成对应的类。但这样做就是对工程的深度定制,而且以后更新版本也会有麻烦。