先说说,Wm5::WindowApplication类要实现一个什么功能呢?要实现一个窗口操作系统中,提供一个供OpenGL渲染的环境,可以提供鼠标键盘事件响应处理、获取字体大小、鼠标位置控制,帧速监测和显示,渲染场景等功能。总体说有点类似GLUT等框架的功能。
与GLUT不同,作者做了这样一个设计愿景,他希望Application应用类是彻底面向对象的,不需要用户再写主函数和把函数指针注册,每一帧通过回调函数处理事件和渲染。那样是不够oo(object oriented)的。这样在设计上就要用到些技巧。
首先我们要找入口函数,C++中入口函数一定是main方法,但是main方法中的具体内容是在库中已经写好的,如果应用程序工程加载了库,main方法依然是入口函数。下面是main方法(部分):
int main (int numArguments, char* arguments[]) { ... InitTerm::ExecuteInitializers(); int exitCode = Application::Run(numArguments, arguments); InitTerm::ExecuteTerminators(); ... return exitCode; }
完整的代码可以去源码“Wm5Application.cpp”文件中去详查。省略掉的工作大体也是通过一些单例类实现的环境配置如根目录,各资源目录,应用程序内存和参数列表处理等。
这里除了InitTerm单例类执行的初始化和终止操作外,最主要就是Application::Run方法。你可以把它当做一个仿真循环。这里需要注意的是,这个static的Run方法来自Application(WindowApplication的基类),而真正的内容一定是在WindowApplication类中的,这里又做了什么技巧?
这里有理由相信一定是与InitTerm::ExecuteInitializers方法有关系的。这里我们假设它做了初始化工作,但是做了什么初始化一定是要赋予它的。而main方法已经写死了,无法灵活的控制将什么在此初始化。那么又有什么可以先于入口函数main得到执行呢。对,全局的变量。
这里贴一下构建起这个架构的所有定义:
#define WM5_WINDOW_APPLICATION(classname) \ WM5_IMPLEMENT_INITIALIZE(classname); \ WM5_IMPLEMENT_TERMINATE(classname); \ \ void classname::Initialize () \ { \ Application::Run = &WindowApplication::Run; \ TheApplication = new0 classname(); \ } \ \ void classname::Terminate () \ { \ delete0(TheApplication); \ } #define WM5_DECLARE_INITIALIZE \ public: \ static bool RegisterInitialize (); \ static void Initialize (); \ private: \ static bool msInitializeRegistered //---------------------------------------------------------------------------- #define WM5_IMPLEMENT_INITIALIZE(classname) \ bool classname::msInitializeRegistered = false; \ bool classname::RegisterInitialize () \ { \ if (!msInitializeRegistered) \ { \ InitTerm::AddInitializer(classname::Initialize); \ msInitializeRegistered = true; \ } \ return msInitializeRegistered; \ } //---------------------------------------------------------------------------- #define WM5_REGISTER_INITIALIZE(classname) \ static bool gsInitializeRegistered_##classname = \ classname::RegisterInitialize () //---------------------------------------------------------------------------- #define WM5_DECLARE_TERMINATE \ public: \ static bool RegisterTerminate (); \ static void Terminate (); \ private: \ static bool msTerminateRegistered //---------------------------------------------------------------------------- #define WM5_IMPLEMENT_TERMINATE(classname) \ bool classname::msTerminateRegistered = false; \ bool classname::RegisterTerminate () \ { \ if (!msTerminateRegistered) \ { \ InitTerm::AddTerminator(classname::Terminate); \ msTerminateRegistered = true; \ } \ return msTerminateRegistered; \ } //---------------------------------------------------------------------------- #define WM5_REGISTER_TERMINATE(classname) \ static bool gsTerminateRegistered_##classname = \ classname::RegisterTerminate ()
这里全是宏定义,具体仍然可以去源文件中查看。这种通过宏定义构造的方法巧妙而常见,比如著名的MFC和Qt等等。其目的是让构建一个应用程序更方便,因为其实现很通用,只要按照这样的步骤即可为类赋予了已为其设计好的功能。
这里只捋顺一下整体设计,具体实现,可自行理解。
到此我们知道真正的C++入口函数main实际通过WindowApplication::Run方法进入仿真循环。这里要说下WindowApplication类究竟做了哪两方面工作。
开题例举了下WindowApplication类的功能,这里要给它功能分个类:PlatformDependent和PlatformIndependent。因为我们知道对于OpenGL应用窗口的渲染环境是平台相关的,而真正的渲染和缓冲区操作是平台无关的。所以考虑到作为一个跨平台应用,头文件是抽象的,而具体实现分别写在两个cpp中,其中如果平台更换,只要把平台相关的cpp替换即可。
至此,框架分析完毕,因为在Windows下的调用win32创建窗口、获得hwnd然后创建hDc和hRc、并make current,这个过程是如此的通用固定,也不具体分析了。
通过实际例子的接触,相信会发现wild magic5的这个轻量应用架构作为在封装性和灵活性上有很好的权衡,而且对我们的阅读和写作也有很强的指导意义。