UnrealEngine プリプロセス後のコードを確認する方法

環境：UE4.27.2

# はじめに
マクロとかプリプロセッサがどう展開されてるか気になったことはありませんか？
今日はUEの環境でプリプロセッサがどのようにコードを変換しているのか見てみましょう。

# プリプロセッサとは
- #define
- #include
- #if
- #ifdef
- #else

とか頭に＃がついているやつです。
こやつらはコンパイルする前にコードを書き換えてくれる便利屋なやつです。
では早速やってみましょう。

# UnrealEngine環境でプリプロセス後のファイルを出力する
1. まず、普通にビルドします。
1. 「x64 Native Tools Command Prompt for VS 2019」を起動
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2778561/10553c3b-0410-3ce6-06e3-6276f309608a.png)
1. カレントディレクトリをエンジンのSourceフォルダに
    ```
    cd UE_4.27\Engine\Source
    ```
1. Intermediateの中にある調べたいソースの.responseファイルを書き換える
例：○○\Intermediate\Build\Win64\UE4Editor\Development\○○\□□.cpp.obj.response
一番下に「/P」と「/D __midl=0」を追加
    ```
    /Zc:inline
    /nologo
    /Oi
    /c
    /Gw
    /Gy
    /Zm1000
    
    ～　省略　～
    
    /I "C:\Program Files (x86)\Windows Kits\10\include\10.0.18362.0\winrt"
    /FI"C:\Users\okqit\Documents\Unreal Projects\Study\Preprocess\Intermediate\Build\Win64\PreprocessEditor\Development\Engine\SharedPCH.Engine.ShadowErrors.h"
    /Yu"C:\Users\okqit\Documents\Unreal Projects\Study\Preprocess\Intermediate\Build\Win64\PreprocessEditor\Development\Engine\SharedPCH.Engine.ShadowErrors.h"
    /Fp"C:\Users\okqit\Documents\Unreal Projects\Study\Preprocess\Intermediate\Build\Win64\PreprocessEditor\Development\Engine\SharedPCH.Engine.ShadowErrors.h.pch"
    "C:\Users\okqit\Documents\Unreal Projects\Study\Preprocess\Source\Preprocess\PreprocessGameModeBase.cpp"
    /FI"C:\Users\okqit\Documents\Unreal Projects\Study\Preprocess\Intermediate\Build\Win64\UE4Editor\Development\Preprocess\Definitions.Preprocess.h"
    /Fo"C:\Users\okqit\Documents\Unreal Projects\Study\Preprocess\Intermediate\Build\Win64\UE4Editor\Development\Preprocess\PreprocessGameModeBase.cpp.obj"
    /TP
    /GR-
    /W4
    /P
    /D __midl=0
    ```
1. 「cl.exe <span>@</span>responseファイル」のコマンドを実行
例：
    ```
    cl.exe @"C:\Users\okqit\Documents\Unreal Projects\〇〇\Intermediate\Build\Win64\UE4Editor\Development\〇〇\□□.cpp.obj.response"
    ```
1. .iファイルがEngine/Source以下にできているのでテキストファイルで開く
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2778561/79cf402f-11b9-8588-b5d4-51d4aebd0fa3.png)
こんな感じの改行だらけのファイルができていたら成功です！
```cpp







template<> __declspec(dllexport) UClass* StaticClass<class APreprocessGameModeBase>();





__pragma(warning(pop))
#line 8 "C:\\Users\\okqit\\Documents\\Unreal Projects\\Study\\Preprocess\\Source\\Preprocess\\PreprocessGameModeBase.h"





class __declspec(dllexport) APreprocessGameModeBase : public AGameModeBase
{
	__pragma(warning(push)) __pragma(warning(disable: 4995)) __pragma(warning(disable: 4996)) public:    private: static void StaticRegisterNativesAPreprocessGameModeBase(); friend struct Z_Construct_UClass_APreprocessGameModeBase_Statics; public: private: APreprocessGameModeBase& operator=(APreprocessGameModeBase&&); APreprocessGameModeBase& operator=(const APreprocessGameModeBase&);  static UClass* GetPrivateStaticClass(); public: enum {StaticClassFlags=(0 | CLASS_Transient | CLASS_Config | CLASS_Intrinsic)}; typedef AGameModeBase Super; typedef APreprocessGameModeBase ThisClass; inline static UClass* StaticClass() { return GetPrivateStaticClass(); } inline static const TCHAR* StaticPackage() { return L"/Script/Preprocess"; } inline static EClassCastFlags StaticClassCastFlags() { return CASTCLASS_None; } inline void* operator new(const size_t InSize, EInternal InInternalOnly, UObject* InOuter = (UObject*)GetTransientPackage(), FName InName = NAME_None, EObjectFlags InSetFlags = RF_NoFlags) { return StaticAllocateObject(StaticClass(), InOuter, InName, InSetFlags); } inline void* operator new( const size_t InSize, EInternal* InMem ) { return (void*)InMem; } friend FArchive &operator<<( FArchive& Ar, APreprocessGameModeBase*& Res ) { return Ar << (UObject*&)Res; } friend void operator<<(FStructuredArchive::FSlot InSlot, APreprocessGameModeBase*& Res) { InSlot << (UObject*&)Res; }  APreprocessGameModeBase(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()) : Super(ObjectInitializer) { }; private:  APreprocessGameModeBase(APreprocessGameModeBase&&);  APreprocessGameModeBase(const APreprocessGameModeBase&); public:  APreprocessGameModeBase(FVTableHelper& Helper);; static UObject* __VTableCtorCaller(FVTableHelper& Helper) { return new (EC_InternalUseOnlyConstructor, (UObject*)GetTransientPackage(), NAME_None, RF_NeedLoad | RF_ClassDefaultObject | RF_TagGarbageTemp) APreprocessGameModeBase(Helper); }; static void __DefaultConstructor(const FObjectInitializer& X) { new((EInternal*)X.GetObj())APreprocessGameModeBase(X); } private: __pragma(warning(pop));
	
};
#line 5 "C:\\Users\\okqit\\Documents\\Unreal Projects\\Study\\Preprocess\\Source\\Preprocess\\PreprocessGameModeBase.cpp"
```
ふむふむ、GENERATED_BODY()はこういうふうに展開されるのか～
classの後ろに書く○○_APIは__declspec(dllexport)に置き換えられるのか～
とか色々見てみると楽しいですね。

# おわりに
役に立つ機会は少ないかもしれないですが謎のビルドエラーに遭遇したときにもしかしたら役に立つかも？しれないのですね。

以上。