Bingo, Computer Graphics & Game Developer
Unreal为每个平台的底层都封装了一致的高层代码,本文讲述的是Windows下Unreal的源代码细节表现
###1. 使用Mutex来保证当前程序是当前系统中唯一开启的
Unreal源代码
bool MakeNamedMutex( const TCHAR* CmdLine )
{
bool bIsFirstInstance = false;
TCHAR MutexName[MAX_SPRINTF] = TEXT( "" );
FCString::Strcpy( MutexName, MAX_SPRINTF, TEXT( "UnrealEngine4" ) );
GNamedMutex = CreateMutex( NULL, true, MutexName );
if( GNamedMutex && GetLastError() != ERROR_ALREADY_EXISTS && !FParse::Param( CmdLine, TEXT( "NEVERFIRST" ) ) )
{
// We're the first instance!
bIsFirstInstance = true;
}
else
{
// Still need to release it in this case, because it gave us a valid copy
ReleaseNamedMutex();
// There is already another instance of the game running.
bIsFirstInstance = false;
}
return( bIsFirstInstance );
}
其中使得程序不会启动两次的关键在于互斥锁CreateMutex()。我参考了这篇文章CreateMutex,其中这一段讲解清晰了原理
进程在启动时首先检查当前系统是否已经存在有此进程的实例,如果没有,进程将成功创建并设置标识实例已经存在的标记。此后再创建进程时将会通过该标记而知晓其实例已经存在,从而保证进程在系统中只能存在一个实例。
如有同名程序运行,则通过 GetLastError()得到错误代码 ERROR_ALREADY_EXIST。
###2. do{ some_code }while(false);
代码块实现模板switch
Unreal源代码
#define checkCode( Code ) do { Code } while ( false );
大致含义就是代码块中一旦出现错误或者指定情况下可以直接跳过(break)后部代码,实现类似goto的逻辑代码。说的直白一些,我个人更倾向于认为这像是”模板Switch”的实现。
// 若任何类型的switch存在 就会像下面这样
switch(condition)
{
case condition1:
break;
case condition2:
break;
// ...
}
因为switch只能满足标量类型,而搭配此,就无需在每步中额外提供一些标示符来表示执行到的位置。其优点就相当于switch(int)和一堆的if/else组合对比下的优势。就像下面这样,阅读感受很糟糕。
// if/else
// condition1
if ( data == & array[o1])
operation = O1;
// condition2
else if ( data == & array[o2])
operation = O2;
// conditionxxx
else if
//...
// conditionn
else if ( data == & array[on])
operation = ON;
Log( "operation:",operation);
相对比之下,checkCode()的优势很明显
// macro switch
checkCode(
if(!condition1)
break;
if(!condition2)
break;
// ...
)
除了我个人理解的switch以外,它也有一定的错误代码跳转能力。以下某种情况下,some_code
和some_further_code
是有可能不被执行的,实现了类似goto的逻辑。
// template switch
do
{
if (!condition1) break;
some_code;
if (!condition2) break;
some_further_code;
// …
} while(false);
###3. class后部用于方便导出到dll的”关键字”
Unreal源代码
class RHI_API FDynamicRHI
{
public:
// ...
}
找了很久,在WindowsPlatfrom.h
下找到类似定义
#define DLLEXPORT __declspec(dllexport)
#define DLLIMPORT __declspec(dllimport)
因为我只使用过一次dll导出函数使用,同时也没有过导出类的经验,因此遇到这个宏时一无所知。参考与以下文章CSDN, C++编写DLL的方法, 其中第二篇更为精确一点。
这个宏出现最核心的原因就在于,客户端和DLL本身共用同一份头文件
。
如若是客户端,那么需要在class后声明_declspec(dllimport)
用以导入该类给自己,DLL则是class后声明_declspec(dllexport)
,问题就出在这里,要共用一份头文件,但class却有两个写法,岂不是无法起到共用的效果?
解决方案如下
.h
#ifdef DLL_API
#else
#define DLL_API _declspec(dllimport)
#endif
.cpp
#define DLL_API _declspec(dllexport)
#include "DLLTest.h"
此时客户端在导入这个类时,使用_declspec(dllimport)
,DLL使用时已定义DLL_API,因此依旧是class被认定为导出_declspec(dllexport)
。
下次打算自己亲手试一试Windows平台下的dll导出。这样印象更深一点。