2.2 UE4反射分析——带函数与变量的AActor拆解

一、在AActor中添加函数与变量

较之上一节我们添加了两个UBoxComponent作为变量,以及从TestFunc1——6共六种常用的函数
其中TestFunc5需要在cpp中有实现

MyActor.h

// Fill out your copyright notice in the Description page of Project Settings.
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyActor.generated.h"

UCLASS()
class REFLECTTEST_API AMyActor : public AActor
{
	GENERATED_BODY()
private:
	UPROPERTY()
		class UBoxComponent* MyBoxComponent;
	UPROPERTY(EditDefaultsOnly, Category = "BoxComponent")
		class UBoxComponent* MyBoxComponent2;
public:
	UFUNCTION(BlueprintCallable, Category = "TestFunc")
		void TestFunc1() {}
	UFUNCTION(BlueprintCallable, Category = "TestFunc")
		bool TestFunc2() { return true; }
	UFUNCTION(BlueprintCallable, Category = "TestFunc")
		void TestFunc3(int32 IntValue, const FString& Name, bool bShowInWindows) {}
	UFUNCTION()
		bool TestFunc4() { return true; }
	UFUNCTION(BlueprintNativeEvent, Category = "TestFunc")
		bool TestFunc5(int32 IntValue);
	UFUNCTION(BlueprintImplementableEvent, Category = "TestFunc")
		bool TestFunc6(bool bShowWindows);

public:
	// Sets default values for this actor's properties
	AMyActor();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:
	// Called every frame
	virtual void Tick(float DeltaTime) override;
};

MyActor.cpp

// Fill out your copyright notice in the Description page of Project Settings.


#include "../Public/MyActor.h"

bool AMyActor::TestFunc5_Implementation(int32 IntValue)
{
	return true;
}

// Sets default values
AMyActor::AMyActor()
{
	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

}

// Called when the game starts or when spawned
void AMyActor::BeginPlay()
{
	Super::BeginPlay();

}

// Called every frame
void AMyActor::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
}

二、再次拆解GENERATED_BODY()宏

2.1 第一层拆解 GENERATED_BODY()

原有的ReflectTest_Source_ReflectTest_Public_MyActor_h_11_PRIVATE_PROPERTY_OFFSET 和ReflectTest_Source_ReflectTest_Public_MyActor_h_11_RPC_WRAPPERS在AActor为空宏,在此节中将会详细拆解。
之前已拆解过的ReflectTest_Source_ReflectTest_Public_MyActor_h_11_INCLASS 和ReflectTest_Source_ReflectTest_Public_MyActor_h_11_STANDARD_CONSTRUCTORS 不再讨论

	//2.2拆解
	ReflectTest_Source_ReflectTest_Public_MyActor_h_11_PRIVATE_PROPERTY_OFFSET 
	//2.3拆解
	ReflectTest_Source_ReflectTest_Public_MyActor_h_11_RPC_WRAPPERS
	ReflectTest_Source_ReflectTest_Public_MyActor_h_11_CALLBACK_WRAPPERS 
	//上一节已分析
	ReflectTest_Source_ReflectTest_Public_MyActor_h_11_INCLASS 
	ReflectTest_Source_ReflectTest_Public_MyActor_h_11_STANDARD_CONSTRUCTORS 

2.2 第二层拆解 ReflectTest_Source_ReflectTest_Public_MyActor_h_11_PRIVATE_PROPERTY_OFFSET

FORCEINLINE static uint32 __PPO__MyBoxComponent() { 
	return STRUCT_OFFSET(AMyActor, MyBoxComponent); 
}
FORCEINLINE static uint32 __PPO__MyBoxComponent2() { 
	return STRUCT_OFFSET(AMyActor, MyBoxComponent2); 
}

2.3 第二层拆解 ReflectTest_Source_ReflectTest_Public_MyActor_h_11_RPC_WRAPPERS

此层拆解为去除各种宏后的最终程序样式,替换过程较为血腥,已省略

#if 0
/*execTestFunc5原始状态*/
DECLARE_FUNCTION(execTestFunc5)
{
	P_GET_PROPERTY(UIntProperty,Z_Param_IntValue);
	P_FINISH;
	P_NATIVE_BEGIN;
	*(bool*)Z_Param__Result = P_THIS->TestFunc5_Implementation(Z_Param_IntValue);
	P_NATIVE_END;
}
#endif


/*execTestFunc5完全拆解后*/
//Context:我们的对象 Stack:栈帧 Z_Param__Result:函数返回值
static void execTestFunc5(UObject* Context, FFrame& Stack, void* const Z_Param__Result)
{
	//三、P_GET_PROPERTY(UIntProperty,Z_Param_IntValue);
	//替换检查字节码的步骤
	//验证参数的顺序是否正确以及类型是否兼容
	UIntProperty::TCppType Z_Param_IntValue = UIntProperty::GetDefaultPropertyValue();
		Stack.StepCompiledIn<UIntProperty>(&Z_Param_IntValue);
	//P_FINISH展开
	//效果类似Code++;
	Stack.Code += !!Stack.Code;
	//P_NATIVE_BEGIN展开
	{
		//SCOPED_SCRIPT_NATIVE_TIMER(ScopedNativeCallTimer);展开
		//记录函数运行时间
		FBlueprintEventTimer::FScopedNativeTimer ScopedNativeCallTimer;

		*(bool*)Z_Param__Result = P_THIS->TestFunc5_Implementation(Z_Param_IntValue);
		//P_NATIVE_END展开
	}
}

static void execTestFunc4(UObject* Context, FFrame& Stack, void* const Z_Param__Result)
{
	Stack.Code += !!Stack.Code; /* increment the code ptr unless it is null */
	{
		FBlueprintEventTimer::FScopedNativeTimer ScopedNativeCallTimer;
		*(bool*)Z_Param__Result = P_THIS->TestFunc4();
	}
}

static void execTestFunc3(UObject* Context, FFrame& Stack, void* const Z_Param__Result)
{
	//P_GET_PROPERTY(UIntProperty,Z_Param_IntValue);
	UIntProperty::TCppType Z_Param_IntValue = UIntProperty::GetDefaultPropertyValue();				
		Stack.StepCompiledIn<UIntProperty>(&Z_Param_IntValue);
	//P_GET_PROPERTY(UStrProperty,Z_Param_Name);
	UStrProperty::TCppType Z_Param_Name = UStrProperty::GetDefaultPropertyValue();			
		Stack.StepCompiledIn<UStrProperty>(&Z_Param_Name);

	//P_GET_UBOOL(Z_Param_bShowInWindows);
	// translate the bitfield into a bool type for non-intel platforms
	uint32 Z_Param_bShowInWindows32 = 0;
	bool Z_Param_bShowInWindows = false;
	Stack.StepCompiledIn<UBoolProperty>(&Z_Param_bShowInWindows32);
	Z_Param_bShowInWindows = !!Z_Param_bShowInWindows32;
	//P_FINISH;
	Stack.Code += !!Stack.Code;
	//P_NATIVE_BEGIN;
	{
		FBlueprintEventTimer::FScopedNativeTimer ScopedNativeCallTimer;
		(ThisClass*)(Context)->TestFunc3(Z_Param_IntValue, Z_Param_Name, Z_Param_bShowInWindows);
		//P_NATIVE_END;
	}
}

static void execTestFunc2(UObject* Context, FFrame& Stack, void* const Z_Param__Result)
{
	Stack.Code += !!Stack.Code;
	{
		FBlueprintEventTimer::FScopedNativeTimer ScopedNativeCallTimer;
		*(bool*)Z_Param__Result = (ThisClass*)(Context)->TestFunc2();
	}
}

static void execTestFunc1(UObject* Context, FFrame& Stack, void* const Z_Param__Result)
{
	Stack.Code += !!Stack.Code;
	{
		FBlueprintEventTimer::FScopedNativeTimer ScopedNativeCallTimer;
		(ThisClass*)(Context)->TestFunc1();
	}
}
2.3.1 分析代码
  1. 蓝图虚拟机组成
    人宅——UE4编辑器开发进阶:反射与蓝图
    2.P_NATIVE_BEGIN——记录运行时间的一种实现方式
  1. 一个用于构造时开始计时析构时停止计时的Timer类
  2. { Timer timer; FunctionName(); }
  3. 将{Timer timer和}封装为Begin和End宏

三、FFrame初探

3.1 FFrame源码
struct FFrame : public FOutputDevice
{
public:
	// Variables.
	UFunction* Node;	//蓝图节点
	UObject* Object;	//函数所属对象
	uint8* Code;		//字节码数组
	uint8* Locals;		//局部变量

	//最近使用变量的指针和地址
	UProperty* MostRecentProperty;
	uint8* MostRecentPropertyAddress;

	/** The execution flow stack for compiled Kismet code */
	//流程栈——每执行一个流程后弹出,再执行下一个流程
	FlowStackType FlowStack;

	/** Previous frame on the stack */
	//上一帧(之前使用的Frame)
	FFrame* PreviousFrame;

	/** contains information on any out parameters */
	//外界参数包,存放所有外界参数(返回值,&)
	FOutParmRec* OutParms;

	/** If a class is compiled in then this is set to the property chain for compiled-in functions. In that case, we follow the links to setup the args instead of executing by code. */
	//属性链,存放函数的参数
	UField* PropertyChainForCompiledIn;

	/** Currently executed native function */
	//原生函数
	UFunction* CurrentNativeFunction;

	bool bArrayContextFailed;
public:

	// Constructors.
	FFrame(UObject* InObject, UFunction* InNode, void* InLocals, FFrame* InPreviousFrame = NULL, UField* InPropertyChainForCompiledIn = NULL);

	virtual ~FFrame()
	{
#if DO_BLUEPRINT_GUARD
		FBlueprintExceptionTracker& BlueprintExceptionTracker = FBlueprintExceptionTracker::Get();
		if (BlueprintExceptionTracker.ScriptStack.Num())
		{
			BlueprintExceptionTracker.ScriptStack.Pop(false);
		}
#endif
	}

	// Functions.
	//指针当前帧
	COREUOBJECT_API void Step(UObject* Context, RESULT_DECL)
	{
		int32 B = *Code++;
		//GNative是一个函数指针,存放函数数组
		(GNatives[B])(Context, *this, RESULT_PARAM);
	}

	/** Replacement for Step that uses an explicitly specified property to unpack arguments **/
	COREUOBJECT_API void StepExplicitProperty(void* const Result, UProperty* Property);

	/** Replacement for Step that checks the for byte code, and if none exists, then PropertyChainForCompiledIn is used. Also, makes an effort to verify that the params are in the correct order and the types are compatible. **/
	template<class TProperty>
	FORCEINLINE_DEBUGGABLE void StepCompiledIn(void* const Result);

	/** Replacement for Step that checks the for byte code, and if none exists, then PropertyChainForCompiledIn is used. Also, makes an effort to verify that the params are in the correct order and the types are compatible. **/
	template<class TProperty, typename TNativeType>
	FORCEINLINE_DEBUGGABLE TNativeType& StepCompiledInRef(void* const TemporaryBuffer);

	COREUOBJECT_API virtual void Serialize(const TCHAR* V, ELogVerbosity::Type Verbosity, const class FName& Category) override;

	COREUOBJECT_API static void KismetExecutionMessage(const TCHAR* Message, ELogVerbosity::Type Verbosity, FName WarningId = FName());

	/** Returns the current script op code */
	const uint8 PeekCode() const { return *Code; }

	/** Skips over the number of op codes specified by NumOps */
	void SkipCode(const int32 NumOps) { Code += NumOps; }

	//读取不同的类型
	template<typename TNumericType>
	TNumericType ReadInt();
	float ReadFloat();
	FName ReadName();
	UObject* ReadObject();
	int32 ReadWord();
	UProperty* ReadProperty();

	/** May return null */
	UProperty* ReadPropertyUnchecked();

	/**
	 * Reads a value from the bytestream, which represents the number of bytes to advance
	 * the code pointer for certain expressions.
	 *
	 * @param	ExpressionField		receives a pointer to the field representing the expression; used by various execs
	 *								to drive VM logic
	 */
	CodeSkipSizeType ReadCodeSkipCount();

	/**
	 * Reads a value from the bytestream which represents the number of bytes that should be zero'd out if a NULL context
	 * is encountered
	 *
	 * @param	ExpressionField		receives a pointer to the field representing the expression; used by various execs
	 *								to drive VM logic
	 */
	VariableSizeType ReadVariableSize(UProperty** ExpressionField);

	/**
	 * This will return the StackTrace of the current callstack from the last native entry point
	 **/
	COREUOBJECT_API FString GetStackTrace() const;

	/**
	* This will return the StackTrace of the all script frames currently active
	**/
	COREUOBJECT_API static FString GetScriptCallstack();

	/**
	 * This will return a string of the form "ScopeName.FunctionName" associated with this stack frame:
	 */
	COREUOBJECT_API FString GetStackDescription() const;

#if DO_BLUEPRINT_GUARD
	static void InitPrintScriptCallstack();
#endif
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值