![]() |
|
Spaces home Johnny's MadhousePhotosProfileFriendsMore ![]() | ![]() |
|
Johnny's MadhouseKage Engine Log
April 06 Using gcc to check syntax error in template.我不知道大家有没有用过gcc 3.14版本后的gch pre-compile header. 其实和msvc的pch概念上是很相似的,这类预编译技术总是会做一些cpp的语法检测,来及早的发现代码编写中的错误.但是相比msvc只做普通class的编译,gcc更加入了template的预编译检测,这使得我们可以通过gch对我们编写的template做一个初级的语法解析和验证,很多vc中需要template instance后才会发现的语法问题,在这里可以得到轻松解决.我们再也不会遇到在msvc下编译通过仍然忧心匆匆,而不得不再一个test cpp代码中instance一个template来验证的困窘情况了(你可是要验证所有的member function,否则msvc只会check你调用的function的语法). 这对于库工作者和发布者无疑是一个很好的工具. 废话不多说,我就说说我的使用方法和新得.
编译生成gch方法很简单,你只需要在你的makefile中指定好要编译的header file,输出为与其同一目录下的gch文件即可,for example:
假设我们的目录结构为,gcc 操作目录为Solution
Solution
|- Base
|- base.h
|- base.cpp
|- Engine
|- engine.h
|- engine.cpp
我们需要编译base.h 为gch,写
c:\Solution:> gcc -c ./Base/base.h -o./Base/base.h.gch
好了, 你不用特别指定gch,所有该目录下的cpp都会先找是否有gch文件存在,如果存在就会去include这个编译好的东西.
ok,介绍一下我的做法,由于跨平台工作的关系,每个平台都需要有自己的gch/pch file, msvc上可以通过指定pch来回避这个问题,但是gcc的gch文件必须要在和其原文件一致的目录下(我知道-I可以解决,但是那样太丑...), gcc提供的解决方案是:
把base.h.gch做成目录,然后在目录里放入你compile好的gch文件,而文件和文件数目并不受限制,gcc在compile到include base.h的cpp文件时,会把目录中的文件都搜索一遍找出第一个符合条件的文件(refer to: gcc manual-3.20 Using Precompiled Header - section 7 ), 所以我们就可以将上诉代码写成:
c:\Solution:> gcc -c ./Base/base.h -o./Base/base.h.gch/base_win32_debug.h.gch (当然之前要把base.h.gch folder create出来) 我的makefile 写法如下:
# ----------------------------------------------------------------
Platform = PS3
Configuration = Debug
FullPath_GchSrcs += ./QuickTest.h GchDir := $(addsuffix .gch,$(FullPath_GchSrcs)) FullPath_Gchs := $(addsuffix _$(Platform)_$(Configuration).h.gch,$(addprefix $(GchDir)/,$(basename $(notdir $(FullPath_GchSrcs))))) # Debug Flag ( choose debug or not )
# Optimization Flag ( -O0:disable -O/-O1:general opt -O2:advance opt -O3:all opt ) ifeq ($(Configuration),Debug) Flag_Debug := -g Flag_Opt := -O0 else Flag_Debug := Flag_Opt := -O1 endif CFlags := $(Flag_Debug) $(Flag_Opt) $(Flag_PreDef) $(Flag_Inc) $(Flag_BuiltIn_Functions) $(CFlag_Spec) # flag_inc, flag_predef, flag_builtin_functions, cflag_spec 的飞机自己搞
.PHONY: gchs clean-gchs
gchs: $(FullPath_Gchs) $(FullPath_Gchs): $(MKDIR) $(ErrDir) $(MKDIR) $(GchDir) $(ECHO) compiling $(basename $@)... $(ECHO) - > $(ErrDir)/$(ErrLogName) $(ECHO) --[$(Project)]$(patsubst %/,%,$(notdir $@))-- >> $(ErrDir)/$(ErrLogName) $(CC) -c $(CFlags) $(basename $(GchDir)) -o $@ 2>>$(ErrDir)/$(ErrLogName) $(CAT) $(ErrDir)/$(ErrLogName) >> $(ErrDir)/$(Project).err # ----------------------------------------------------------------
说了那么多但其实下面才是重点. 虽然说gch可以pre-check template 的 syntax, 但是这也带来了一些不必要的麻烦,比如说我们习惯性地在template 中写一些inline define, 而这些define往往会调用一些其他类或class, 在vc中由于template 编译放在template instance后, 一些因为头文件放置关系不严谨的define 和 declaration的问题不会很容易出现, 但是这种形式的放置在gch编译中就会出现错误.举个例子
在 test_tmpl.h 中我们有如下定义
template <typename T>
class TTestUndefine
{
public:
inline void test_function()
{
CTestMng::GetInstance()->DoSomething();
}
};
在test_mng.h 中我们定义:
clas CTestMng
{
public:
static CTestMng* GetInstance() { return ms_instance; }
void DoSomething() { return; }
protected:
static CTestMng* ms_instance;
};
OK, 我们不要care 这个Singleton的构造啊,销毁阿等OOXX的东西,就假设我们已经有这么一个singleton.
然后我们做了如下的precompile header
base.h
// api includes
// template includes
#include "test_tmpl.h"
#include ...
...
...
// common classes includes
#include "test_mgn.h"
#include ...
...
...
好了,这个base.pch文件在vc上编译不会出现问题,但是gcc中,由于会对template作预编译处理,我们会发现类似CTestMng undefine这样的错误,原因是由于我们的header file组织是按照先template后common class的顺序来的. 由于header file的include并没有特别好的组织形式,对于大型项目我们很难预知两个header file的dependence关系( 虽然我有自己做的exIncludeCheck 工具 :), 不过广大群众们没有:p ), 所以我的做法是,把template 的inline function defines都写入hpp中,然后在gch include的最末端include 这一系列的hpp文件,如下:
base.h
// api includes
// template includes
#include "test_tmpl.h"
#include ...
...
...
// common classes includes
#include "test_mgn.h"
#include ...
...
...
// late-inline defines
#include "test_tmpl.hpp"
#include ...
...
这样的形式你就不用再为哪个头文件放前哪个头文件放后而烦恼了.
gch编译常见问题:
对于如下class (代码简写)
tmpl <typ T> struct classA { int data_mbr1; };
tmpl <typ T> struct classB : public classA<T>
{
int dosomething() { return data_mbr1;}
classB( const classB& _b )
: data_mbr1(_b.data_mbr1)
{
}
};
会编译错误 -- undeclare data_mbr1.
-- undeclare _b.
这个虽然我觉得不应该,但gcc tmpl的check对于member的归属很严格,所以代码最后写成:
tmpl <typ T> struct classA { int data_mbr1; };
tmpl <typ T> struct classB : public classA<T>
{
typedef classA<T> base_t;
typedef classB<T> self_t;
int dosomething() { return base_t::data_mbr1;}
classB( const self_t& _b )
: data_mbr1(_b.data_mbr1)
{
} }; 好了, 以上只是我用gch来写template的一些心得和技巧.
高效编程需要一系列辅助工具,还是希望大家多share技巧和工具.
众人: O_O O_O O_O O_O O_O O_O O_O O_O O_O
当然exIncludeCheck暂时不share了, 这个嘛..... 我开发很久的也,怎么能够说share 就 share !!!!!!
众人: 囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧
March 26 被这东西搞死了Macro的一个大陷阱,今天被这个东西搞死了! 看看下面这段代码为何会编译错误:
// ===================================================================
#define EX_DEFINE_COMMON_CONTAINER_FUNCTIONS( _std_container_t ) \
INLINE AllocatorType GetAllocator() { return _std_container_t::get_allocator(); } \ INLINE Iterator Begin() { return _std_container_t::begin(); } \ INLINE ConstIterator Begin() const { return _std_container_t::begin(); } \ INLINE Iterator End() { return _std_container_t::end(); } \ INLINE ConstIterator End() const { return _std_container_t::end(); } \ INLINE R_Iterator RBegin() { return _std_container_t::rbegin(); } \ INLINE R_ConstIterator RBegin() const { return _std_container_t::rbegin(); } \ INLINE R_Iterator REnd() { return _std_container_t::rend(); } \ INLINE R_ConstIterator REnd() const { return _std_container_t::rend(); } \ INLINE bool Empty() const { return _std_container_t::empty(); } \ INLINE SizeType Size() const { return _std_container_t::size(); } \ INLINE SizeType MaxSize() const { return _std_container_t::max_size(); } \ INLINE void Erase( Iterator _pos ) { _std_container_t::erase( _pos ); } \ INLINE void Erase( Iterator _first, Iterator _last ) { _std_container_t::erase( _first, _last ); } \ INLINE void Clear() { _std_container_t::clear(); } \ INLINE void Swap( self_t& _container ) { _std_container_t::swap( _container ); } template < typename T >
class TVector : protected std::vector<T>
{
public:
typedef TVector<T> self_t;
typedef std::vector<T> base_container_t;
EX_DEFINE_COMMON_CONTAINER_FUNCTIONS(base_container_t)
}; // end class
// ===================================================================
尽管复制上面的代码...错误原因居然是:
...............................
...............................
...............................
...............................
...............................
我在#define后面的那个 " \" 后面手贱多敲了几个空格.... 囧囧囧囧囧囧囧囧囧
还好vim的syntax highlight够强.....搞了我半个多小时,shit........... March 04 我又开始幼稚了.....囧!!!我发现我果然是狂热的技术主义者,所以我只能凸那些成天把商业盈利拿出来叫嚣的人啊,其实他们在寻找的是一种自我安慰而已-_-|||||,却发现技艺上已经追不上你的步伐了! 我正在找新一轮的挑战. 下个目标 -------------- 还是Griffon Engine (囧.....上次的不也是这个吗) August 28 加班归来还在说周二打球可以慢有兴致地更新呢,结果 T_T 加班...........
最近最郁闷的事情要属我那台360 由传说中的3红变成1红,太猛了-_-||| 就等着秋季微软出65nm版本了, 要不是sony的PS3没游戏玩我真想买台来以示我对微软的抗议!!
话说回来ps3是可以装linux用cell sdk来写程序的, 就是gpu用不了, 有兴趣的玩家可以试试. 这算什么,算是sony对索饭们的弥补么?
再次喷一下360那无良的质量!
再说程序, 周六闲来写了棵红黑树, 结果bug太多于是放弃, 想想stl就有了何必再写, 顺手改了改, 加了些查找代码放到Griffon里运行正常, 也就没做太多测试了. 周日无聊写了个光源管理器,其实就是把LightGroup加上一堆范围检测最终得到每个物体的LightList. 写完了,但是还没来得及测试0_0....恩..不管了,能用就行,有bug再说......
August 21 Johnny的YY编程空间重新开张重新整理空间,把所有以前的日记都删除了,所以留过言的同学们千万不要生气。。。 话说Beowulf的项目还没结束,所以也就不多说了。接下去准备好好把这个空间办起来,就以Griffon Engine 的开发作为日记吧。主要写空间太废时间了,最近又忙到飞起,想来周二公司有乒乓球可以打,所以回来后正好可以整理一周的头绪,所以暂定周二作为更新日记的时间,周六周天不定期更新。 那么。。。。。。今天还是就先这样吧-_-|||||||||| 华丽的闪人 |
|||||||||||||||||||||||||||||||||||||||
|
|