Was able to parse UObject with gencpp!!!

This commit is contained in:
Edward R. Gonzalez 2024-04-14 21:51:14 -04:00
parent 7b9e277bc1
commit 48d21ddd15
25 changed files with 855 additions and 422 deletions

4
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,4 @@
{
"autoHide.autoHideSideBar": false,
"autoHide.autoHidePanel": false
}

Binary file not shown.

View File

@ -4,6 +4,7 @@
GameDefaultMap=/Game/Levels/StartupMap.StartupMap
EditorStartupMap=/Game/Levels/StartupMap.StartupMap
GlobalDefaultGameMode=/Game/Core/Game/BP_GameMode.BP_GameMode_C
GameInstanceClass=/Script/Gasa.GasaGameInstance
[/Script/WindowsTargetPlatform.WindowsTargetSettings]
DefaultGraphicsRHI=DefaultGraphicsRHI_DX12

Binary file not shown.

BIN
Project/Content/Levels/StartupMap.umap (Stored with Git LFS)

Binary file not shown.

View File

@ -21,7 +21,6 @@ public:
FGameplayAttributeData MaxMana;
UGasaAttributeSet();
UFUNCTION()
void Client_OnRep_Health( FGameplayAttributeData& PrevHealth );
UFUNCTION()

View File

@ -3,7 +3,6 @@
#include "Camera/CameraComponent.h"
#include "GameFramework/SpringArmComponent.h"
ACameraMount::ACameraMount()
{
PrimaryActorTick.bCanEverTick = true;

View File

@ -22,4 +22,3 @@ public:
void PostInitializeComponents() override;
#pragma endregion Actor
};

View File

@ -5,4 +5,7 @@ void UGasaGameInstance::Init()
Super::Init();
DevOptionsCache.CachedDevOptions();
using namespace Gasa;
Log(FString::Printf(TEXT("UObject Size: %d RT: %d"), sizeof(UObject), UObject::StaticClass()->PropertiesSize ));
}

View File

@ -0,0 +1,9 @@
// Don't keep this included anywhere
// Purely for inspection purposes
#include "GasaCommon.h"
void test()
{
UObject::StaticClass()->PropertiesSize
}

View File

@ -0,0 +1,7 @@
#include "GasaUserWidget.h"
UGasaUserWidget::UGasaUserWidget(FObjectInitializer const& ObjectInitializer)
: UUserWidget(ObjectInitializer)
{
}

View File

@ -0,0 +1,26 @@
#pragma once
#include "Blueprint/UserWidget.h"
#include "GasaUserWidget.generated.h"
UCLASS(Blueprintable)
class GASA_API UGasaUserWidget : public UUserWidget
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadOnly)
TObjectPtr<UObject> WidgetController;
UGasaUserWidget(FObjectInitializer const& ObjectInitializer);
UFUNCTION(BlueprintCallable)
void SetWidgetController(UObject* Controller)
{
WidgetController = Controller;
OnWidgetControllerSet();
}
UFUNCTION(BlueprintImplementableEvent)
void OnWidgetControllerSet();
};

View File

@ -0,0 +1,9 @@
#pragma once
#include "Components/ProgressBar.h"
UCLASS()
class GASA_API UProgressIndicator : public UProgressBar
{
};

View File

@ -0,0 +1 @@
#include "WidgetController.h"

View File

@ -0,0 +1,23 @@
#pragma once
#include "GasaCommon.h"
#include "WidgetController.generated.h"
UCLASS(BlueprintType)
class GASA_API UWdgetController : public UObject
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadOnly, Category="Player")
TObjectPtr<APlayerController> Controller;
UPROPERTY(BlueprintReadOnly, Category="Player")
TObjectPtr<APlayerState> PlayerState;
UPROPERTY(BlueprintReadOnly, Category="Player")
TObjectPtr<UAbilitySystemComponent> AbilitySystem;
UPROPERTY(BlueprintReadOnly, Category="Player")
TObjectPtr<UAttributeSet> Attributes;
};

View File

@ -5,280 +5,136 @@
#define GEN_IMPLEMENTATION
#include "gen.cpp"
#include "gen.builder.cpp"
#include "gen.scanner.hpp"
using namespace gen;
// Program assumes its working directory is the project
#define path_config "./Source/Config/"
#define path_module_gasa "./Source/Gasa/"
#define path_gasa_ability_system path_module_gasa "AbilitySystem/"
#include "GasaGenCommon.cpp"
#include "GasaGen_UGasaAttributeSet.cpp"
void def_attribute_properties( CodeBody body, Array<StringCached> properties )
{
for ( StringCached property : properties )
{
Code field_uproperty = code_fmt( "property", (StrC)property, stringize(
UPROPERTY(ReplicatedUsing=Client_OnRep_<property>, EditAnywhere, BlueprintReadWrite, Category="Attributes")
));
CodeType type_FGameplayAttributeData = def_type( txt("FGameplayAttributeData"));
body.append(fmt_newline);
body.append( field_uproperty );
body.append(fmt_newline);
body.append( def_variable( type_FGameplayAttributeData, StrC(property)) );
}
}
void def_attribute_field_on_reps( CodeBody body, Array<StringCached> fields )
{
for ( StringCached field : fields )
{
Code umeta_UFUNCTION = code_str( UFUNCTION() );
body.append(fmt_newline);
body.append( umeta_UFUNCTION );
body.append(fmt_newline);
body.append( code_fmt( "field", (StrC)field, stringize(
void Client_OnRep_<field>(FGameplayAttributeData& Prev<field>);
)));
}
}
void def_attribute_field_property_getters( CodeBody body, StrC class_name, Array<StringCached> properties )
{
for ( String property : properties )
{
CodeFn generated_get_attribute = parse_function(
token_fmt( "class_name", class_name, "property", (StrC)property,
stringize(
static FGameplayAttribute Get<property>Attribute()
{
static FProperty* Prop = FindFieldChecked<FProperty>(<class_name>::StaticClass(), GET_MEMBER_NAME_CHECKED(<class_name>, <property>));
return Prop;
}
)));
body.append( generated_get_attribute );
}
}
void def_attribute_field_value_getters( CodeBody body, Array<StringCached> properties )
{
for ( String property : properties )
{
#pragma push_macro(FORCEINLINE)
#undef FORCEINLINE
body.append( code_fmt( "property", (StrC)property,
stringize(
FORCEINLINE float Get<property>() const
{
return <property>.GetCurrentValue();
}
)));
#pragma pop_macro(FORCEINLINE)
}
}
void def_attribute_field_value_setters( CodeBody body, Array<StringCached> properties )
{
for ( String property : properties )
{
body.append( code_fmt( "property", (StrC)property,
stringize(
FORCEINLINE void Set<property>(float NewVal)
{
UAbilitySystemComponent* AbilityComp = GetOwningAbilitySystemComponent();
if (ensure(AbilityComp))
{
AbilityComp->SetNumericAttributeBase(Get<property>Attribute(), NewVal);
};
}
)));
}
}
void def_attribute_field_initers ( CodeBody body, Array<StringCached> properties )
{
for ( String property : properties )
{
body.append( code_fmt( "property", (StrC)property,
stringize(
FORCEINLINE void Init<property>(float NewVal)
{
<property>.SetBaseValue(NewVal);
<property>.SetCurrentValue(NewVal);
}
)));
}
}
void impl_attribute_fields( CodeBody body, StrC class_name, Array<StringCached> properties )
{
for ( String property : properties )
{
body.append(fmt_newline);
CodeFn field_impl = parse_function( token_fmt( "class_name", class_name, "property", (StrC)property,
stringize(
void <class_name>::Client_OnRep_<property>(FGameplayAttributeData& Prev<property>)
{
GAMEPLAYATTRIBUTE_REPNOTIFY(<class_name>, <property>, Prev<property>)
}
)));
body.append( field_impl );
}
}
int gen_main()
{
gen::init();
log_fmt("Generating code for the Gasa module");
Code umeta_uclass = code_str( UCLASS() );
Code umeta_generated_body = code_str( GENERATED_BODY() );
Code gasa_api = code_str( GASA_API );
// Initialize Globals
umeta_uclass = code_str( UCLASS() );
umeta_generated_body = code_str( GENERATED_BODY() );
gasa_api = code_str( GASA_API );
CodeType type_UAttributeSet = def_type( txt("UAttributeSet") );
StrC str_generated_body = txt("GENERATED_BODY(");
StrC str_generated_uclass_body = txt("GENERATED_UCLASS_BODY(");
StrC str_property_binding_impl = txt("PROPERTY_BINDING_IMPLEMENTATION");
StrC str_uclass = txt("UCLASS(");
StrC str_ue_deprecated = txt("UE_DEPRECATED(");
StrC str_ufunction = txt("UFUNCTION(");
StrC str_uproperty = txt("UPROPERTY(");
StrC str_umg_api = txt("UMG_API");
CodeComment generation_notice = def_comment(txt("This was generated by GasaGen/GasaGen.cpp"));
StrC str_declare_log_category_extern = txt("DECLARE_LOG_CATEGORY_EXTERN(");
StrC str_enum_class_flags = txt("ENUM_CLASS_FLAGS(");
StrC str_declare_class = txt("DECLARE_CLASS(");
StrC str_define_default_object_initializer_constructor_call = txt("DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL(");
StrC str_core_object_api = txt("COREUOBJECT_API");
StrC str_macro_text = txt("TEXT(");
StrC str_declare_multicast_delegate_one_parameter = txt("DECLARE_MULTICAST_DELEGATE_OneParam(");
StrC str_declare_multicast_delegate_two_parameter = txt("DECLARE_MULTICAST_DELEGATE_TwoParams(");
StrC str_declare_multicast_delegate_three_parameter = txt("DECLARE_MULTICAST_DELEGATE_ThreeParams(");
StrC str_declare_delegate_retval_one_param = txt("DECLARE_DELEGATE_RetVal_OneParam(");
StrC str_declare_function = txt("DECLARE_FUNCTION(");
StrC str_result_decl = txt("RESULT_DECL");
StrC str_property_binding_implementation = txt("PROPERTY_BINDING_IMPLEMENTATION(");
StrC str_FORCEINLINE = txt("FORCEINLINE");
Array<StringCached> attribute_fields = Array<StringCached>::init( GlobalAllocator);
attribute_fields.append( get_cached_string(txt("Health")));
attribute_fields.append( get_cached_string(txt("MaxHealth")));
attribute_fields.append( get_cached_string(txt("Mana")));
attribute_fields.append( get_cached_string(txt("MaxMana")));
PreprocessorDefines.append( get_cached_string(str_generated_body));
PreprocessorDefines.append( get_cached_string(str_generated_uclass_body));
PreprocessorDefines.append( get_cached_string(str_property_binding_impl));
PreprocessorDefines.append( get_cached_string(str_ue_deprecated));
PreprocessorDefines.append( get_cached_string(str_uclass));
PreprocessorDefines.append( get_cached_string(str_ufunction));
PreprocessorDefines.append( get_cached_string(str_uproperty));
PreprocessorDefines.append( get_cached_string(str_umg_api));
PreprocessorDefines.append( get_cached_string(str_declare_log_category_extern));
PreprocessorDefines.append( get_cached_string(str_enum_class_flags));
PreprocessorDefines.append( get_cached_string(str_declare_class));
PreprocessorDefines.append( get_cached_string(str_define_default_object_initializer_constructor_call));
PreprocessorDefines.append( get_cached_string(str_core_object_api));
PreprocessorDefines.append( get_cached_string(str_macro_text));
PreprocessorDefines.append( get_cached_string(str_declare_multicast_delegate_one_parameter));
PreprocessorDefines.append( get_cached_string(str_declare_multicast_delegate_two_parameter));
PreprocessorDefines.append( get_cached_string(str_declare_multicast_delegate_three_parameter));
PreprocessorDefines.append( get_cached_string(str_declare_delegate_retval_one_param));
PreprocessorDefines.append( get_cached_string(str_declare_function));
PreprocessorDefines.append( get_cached_string(str_result_decl));
PreprocessorDefines.append( get_cached_string(str_property_binding_implementation));
PreprocessorDefines.append( get_cached_string(str_FORCEINLINE));
#define path_UProgressBar \
"C:/projects/Unreal/Surgo/UE/Engine/Source/Runtime/UMG/Public/Components/ProgressBar.h"
StrC attributeset_name = txt("UGasaAttributeSet");
FileContents content = file_read_contents( GlobalAllocator, true, path_UProgressBar );
CodeBody parsed_uprogressbar = parse_global_body( StrC { content.size, (char const*)content.data });
Builder header = Builder::open( path_gasa_ability_system "GasaAttributeSet.h");
log_fmt("\n\n");
for ( Code gcode : parsed_uprogressbar )
{
header.print(generation_notice);
header.print(fmt_newline);
if ( gcode->Type == CodeT::Class )
{
CodeInclude Include_AttributeSet = def_include(txt("AttributeSet.h"));
CodeInclude Include_AbilitySystemComponent = def_include(txt("AbilitySystemComponent.h"));
CodeInclude Include_GasaAttributeSet_Generated = def_include(txt("GasaAttributeSet.generated.h"));
log_fmt("Class %S - Definitions:\n", gcode->Name);
CodeAttributes attributes = def_attributes( gasa_api->Name);
CodeClass GasaAttributeSet = {};
if (gcode->Body->Type != CodeT::Class_Body)
continue;
for ( Code class_code : gcode->Body->cast<CodeBody>() )
{
CodeBody body = def_body( CodeT::Class_Body );
switch ( class_code->Type )
{
body.append( umeta_generated_body);
body.append( fmt_newline);
body.append( access_public );
def_attribute_properties( body, attribute_fields);
body.append(fmt_newline);
body.append( def_constructor() );
def_attribute_field_on_reps( body, attribute_fields);
body.append(fmt_newline);
body.append( fmt_newline );
body.append( def_pragma(code( region Getters )));
def_attribute_field_property_getters( body, attributeset_name, attribute_fields );
def_attribute_field_value_getters( body, attribute_fields );
body.append( def_pragma(code( endregion Getters )));
body.append( fmt_newline );
body.append( def_pragma(code( region Setters )));
def_attribute_field_value_setters( body, attribute_fields );
def_attribute_field_initers( body, attribute_fields );
body.append( def_pragma(code( endregion Setters )));
body.append( fmt_newline );
body.append( def_pragma( txt("region UObject")));
body.append( parse_function( code(
void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
)));
body.append( def_pragma( txt("endregion UObject")));
case CodeT::Variable:
case CodeT::Function:
case CodeT::Function_Fwd:
if ( class_code->Name )
{
log_fmt("%s\n", class_code->Name );
}
GasaAttributeSet = def_class( txt("UGasaAttributeSet"), body
, type_UAttributeSet
, AccessSpec::Public
, attributes
);
}
CodeNS ns_gasa = parse_namespace( code(
namespace Gasa
{
inline
UGasaAttributeSet const* GetAttributeSet( UAbilitySystemComponent* ASC )
{
return Cast<UGasaAttributeSet>(ASC->GetAttributeSet( UGasaAttributeSet::StaticClass() ));
break;
}
}
));
header.print( Include_AttributeSet);
header.print( Include_AbilitySystemComponent);
header.print( Include_GasaAttributeSet_Generated);
header.print( fmt_newline);
header.print(umeta_uclass);
header.print(GasaAttributeSet);
header.print(ns_gasa);
}
header.write();
}
Builder source = Builder::open( path_gasa_ability_system "GasaAttributeSet.cpp" );
#define path_UObject \
R"(C:\projects\Unreal\Surgo\UE\Engine\Source\Runtime\CoreUObject\Public\UObject\Object.h)"
content = file_read_contents( GlobalAllocator, true, path_UObject );
CodeBody parsed_uobject = parse_global_body( StrC { content.size, (char const*)content.data });
log_fmt("\n\n");
for ( Code gcode : parsed_uobject )
{
source.print(generation_notice);
source.print( def_include( txt("GasaAttributeSet.h")));
source.print(fmt_newline);
source.print( def_include( txt("AbilitySystemComponent.h")));
source.print( def_include( txt("Net/UnrealNetwork.h")));
source.print( def_include( txt("Networking/GasaNetLibrary.h")));
if ( gcode->Type == CodeT::Class )
{
CodeBody body = def_body( CodeT::Global_Body );
body.append(fmt_newline);
body.append(code_str(
UGasaAttributeSet::UGasaAttributeSet()
log_fmt("Class %S - Definitions:\n", gcode->Name);
if (gcode->Body->Type != CodeT::Class_Body)
continue;
for ( Code class_code : gcode->Body->cast<CodeBody>() )
{
InitHealth( 100.f );
InitMaxHealth( 100.f );
InitMana(( 50.f ));
InitMaxMana( 50.f );
switch ( class_code->Type )
{
case CodeT::Variable:
case CodeT::Function:
case CodeT::Function_Fwd:
if ( class_code->Name )
{
log_fmt("%s\n", class_code->Name );
}
break;
}
}
}
));
body.append(fmt_newline);
impl_attribute_fields( body, attributeset_name, attribute_fields);
CodeFn GetLifetimeOfReplicatedProps;
{
CodeBody field_lifetimes = def_body( CodeT::Function_Body);
for (StringCached field : attribute_fields)
{
field_lifetimes.append( code_fmt( "field", (StrC)field, stringize(
DOREPLIFETIME_DEFAULT_GAS(UGasaAttributeSet, <field>);
)));
}
GetLifetimeOfReplicatedProps = parse_function( token_fmt( "body", (StrC)(field_lifetimes.to_string()), stringize(
void UGasaAttributeSet::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
<body>
}
)));
}
body.append(GetLifetimeOfReplicatedProps);
StrC str_gasa_api = txt("GASA_API");
source.print(body);
}
source.write();
}
// gen::deinit();
gen_UGasaAttributeSet();
return 0;
}

View File

@ -0,0 +1,13 @@
#pragma once
// Program assumes its working directory is the project
#define path_config "./Source/Config/"
#define path_module_gasa "./Source/Gasa/"
#define path_gasa_ability_system path_module_gasa "AbilitySystem/"
#pragma region Globals
// These Code objects are created before anything else after gencpp does its initializatioon
global Code umeta_uclass;
global Code umeta_generated_body;
global Code gasa_api;
#pragma endregion Globals

View File

@ -0,0 +1,268 @@
// Used in the GasaGen.cpp translation unit
void def_attribute_properties ( CodeBody body, Array<StringCached> properties );
void def_attribute_field_on_reps ( CodeBody body, Array<StringCached> properties );
void def_attribute_field_property_getters( CodeBody body, StrC class_name, Array<StringCached> properties );
void def_attribute_field_value_getters ( CodeBody body, Array<StringCached> properties );
void def_attribute_field_value_setters ( CodeBody body, Array<StringCached> properties );
void def_attribute_field_initers ( CodeBody body, Array<StringCached> properties );
void impl_attribute_fields ( CodeBody body, StrC class_name, Array<StringCached> properties );
void gen_UGasaAttributeSet()
{
CodeType type_UAttributeSet = def_type( txt("UAttributeSet") );
CodeComment generation_notice = def_comment(txt("This was generated by GasaGen/GasaGen.cpp"));
Array<StringCached> attribute_fields = Array<StringCached>::init( GlobalAllocator);
attribute_fields.append( get_cached_string(txt("Health")));
attribute_fields.append( get_cached_string(txt("MaxHealth")));
attribute_fields.append( get_cached_string(txt("Mana")));
attribute_fields.append( get_cached_string(txt("MaxMana")));
StrC class_name = txt("UGasaAttributeSet");
Builder header = Builder::open( path_gasa_ability_system "GasaAttributeSet.h");
{
header.print(generation_notice);
header.print(fmt_newline);
{
CodeInclude Include_AttributeSet = def_include(txt("AttributeSet.h"));
CodeInclude Include_AbilitySystemComponent = def_include(txt("AbilitySystemComponent.h"));
CodeInclude Include_GasaAttributeSet_Generated = def_include(txt("GasaAttributeSet.generated.h"));
CodeAttributes api_attribute= def_attributes( gasa_api->Name);
CodeClass GasaAttributeSet = {};
{
CodeBody body = def_body( CodeT::Class_Body );
{
body.append( umeta_generated_body);
body.append( fmt_newline);
body.append( access_public );
def_attribute_properties( body, attribute_fields);
body.append(fmt_newline);
body.append( def_constructor() );
def_attribute_field_on_reps( body, attribute_fields);
body.append(fmt_newline);
body.append( fmt_newline );
body.append( def_pragma(code( region Getters )));
def_attribute_field_property_getters( body, class_name, attribute_fields );
def_attribute_field_value_getters( body, attribute_fields );
body.append( def_pragma(code( endregion Getters )));
body.append( fmt_newline );
body.append( def_pragma(code( region Setters )));
def_attribute_field_value_setters( body, attribute_fields );
def_attribute_field_initers( body, attribute_fields );
body.append( def_pragma(code( endregion Setters )));
body.append( fmt_newline );
body.append( def_pragma( txt("region UObject")));
body.append( parse_function( code(
void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
)));
body.append( def_pragma( txt("endregion UObject")));
}
GasaAttributeSet = def_class( class_name, body
, type_UAttributeSet, AccessSpec::Public
, api_attribute
);
}
CodeNS ns_gasa = parse_namespace( code(
namespace Gasa
{
inline
UGasaAttributeSet const* GetAttributeSet( UAbilitySystemComponent* ASC )
{
return Cast<UGasaAttributeSet>(ASC->GetAttributeSet( UGasaAttributeSet::StaticClass() ));
}
}
));
header.print( Include_AttributeSet);
header.print( Include_AbilitySystemComponent);
header.print( Include_GasaAttributeSet_Generated);
header.print( fmt_newline);
header.print(umeta_uclass);
header.print(GasaAttributeSet);
header.print(ns_gasa);
}
header.write();
}
Builder source = Builder::open( path_gasa_ability_system "GasaAttributeSet.cpp" );
{
source.print(generation_notice);
header.print(fmt_newline);
source.print( def_include( txt("GasaAttributeSet.h")));
source.print(fmt_newline);
source.print( def_include( txt("AbilitySystemComponent.h")));
source.print( def_include( txt("Net/UnrealNetwork.h")));
source.print( def_include( txt("Networking/GasaNetLibrary.h")));
{
CodeBody body = def_body( CodeT::Global_Body );
body.append(fmt_newline);
CodeConstructor constructor_for_UGasaAttributeSet = parse_constructor( code(
UGasaAttributeSet::UGasaAttributeSet()
{
InitHealth( 100.f );
InitMaxHealth( 100.f );
InitMana(( 50.f ));
InitMaxMana( 50.f );
}
));
body.append(constructor_for_UGasaAttributeSet );
body.append(fmt_newline);
impl_attribute_fields( body, class_name, attribute_fields);
CodeFn GetLifetimeOfReplicatedProps;
{
CodeBody field_lifetimes = def_body( CodeT::Function_Body);
for (StringCached field : attribute_fields)
{
field_lifetimes.append( code_fmt( "field", (StrC)field, stringize(
DOREPLIFETIME_DEFAULT_GAS(UGasaAttributeSet, <field>);
)));
}
GetLifetimeOfReplicatedProps = parse_function( token_fmt( "body", (StrC)(field_lifetimes.to_string()), stringize(
void UGasaAttributeSet::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
<body>
}
)));
}
body.append(GetLifetimeOfReplicatedProps);
source.print(body);
}
source.write();
}
}
void def_attribute_properties( CodeBody body, Array<StringCached> properties )
{
for ( StringCached property : properties )
{
Code field_uproperty = code_fmt( "property", (StrC)property, stringize(
UPROPERTY(ReplicatedUsing=Client_OnRep_<property>, EditAnywhere, BlueprintReadWrite, Category="Attributes")
));
CodeType type_FGameplayAttributeData = def_type( txt("FGameplayAttributeData"));
body.append(fmt_newline);
body.append( field_uproperty );
body.append(fmt_newline);
body.append( def_variable( type_FGameplayAttributeData, StrC(property)) );
}
}
void def_attribute_field_on_reps( CodeBody body, Array<StringCached> properties )
{
for ( StringCached property : properties )
{
Code umeta_UFUNCTION = code_str( UFUNCTION() );
body.append(fmt_newline);
body.append( umeta_UFUNCTION );
body.append(fmt_newline);
body.append( code_fmt( "property", (StrC)property, stringize(
void Client_OnRep_<property>(FGameplayAttributeData& Prev<property>);
)));
}
}
void def_attribute_field_property_getters( CodeBody body, StrC class_name, Array<StringCached> properties )
{
for ( String property : properties )
{
CodeFn generated_get_attribute = parse_function(
token_fmt( "class_name", class_name, "property", (StrC)property,
stringize(
static FGameplayAttribute Get<property>Attribute()
{
static FProperty* Prop = FindFieldChecked<FProperty>(<class_name>::StaticClass(), GET_MEMBER_NAME_CHECKED(<class_name>, <property>));
return Prop;
}
)));
body.append( generated_get_attribute );
}
}
void def_attribute_field_value_getters( CodeBody body, Array<StringCached> properties )
{
for ( String property : properties )
{
#pragma push_macro(FORCEINLINE)
#undef FORCEINLINE
body.append( code_fmt( "property", (StrC)property,
stringize(
FORCEINLINE float Get<property>() const
{
return <property>.GetCurrentValue();
}
)));
#pragma pop_macro(FORCEINLINE)
}
}
void def_attribute_field_value_setters( CodeBody body, Array<StringCached> properties )
{
for ( String property : properties )
{
body.append( code_fmt( "property", (StrC)property,
stringize(
FORCEINLINE void Set<property>(float NewVal)
{
UAbilitySystemComponent* AbilityComp = GetOwningAbilitySystemComponent();
if (ensure(AbilityComp))
{
AbilityComp->SetNumericAttributeBase(Get<property>Attribute(), NewVal);
};
}
)));
}
}
void def_attribute_field_initers ( CodeBody body, Array<StringCached> properties )
{
for ( String property : properties )
{
body.append( code_fmt( "property", (StrC)property,
stringize(
FORCEINLINE void Init<property>(float NewVal)
{
<property>.SetBaseValue(NewVal);
<property>.SetCurrentValue(NewVal);
}
)));
}
}
void impl_attribute_fields( CodeBody body, StrC class_name, Array<StringCached> properties )
{
for ( String property : properties )
{
body.append(fmt_newline);
CodeFn field_impl = parse_function( token_fmt( "class_name", class_name, "property", (StrC)property,
stringize(
void <class_name>::Client_OnRep_<property>(FGameplayAttributeData& Prev<property>)
{
GAMEPLAYATTRIBUTE_REPNOTIFY(<class_name>, <property>, Prev<property>)
}
)));
body.append( field_impl );
}
}

View File

@ -0,0 +1,14 @@
# GasaGen
This is a single translation unit meta-program for generating code for the Gasa modules.
It runs before UHT.
GasaGen.cpp is the effective translation unit.
All related code for GasaGen is prefixed with:
```GasaGen_```
Anything with the `gen` namespace (case sensitive, including the files), is related to the Gencpp header. Genccp is used.
Gencpp is a cpp library to make ergonomic use of stage metaprogramming for C++ in C++.
For how GasaGen is built see [`scripts/gen_pass_gasa.ps1`](../../../scripts/gen_pass_gasa.ps1)

View File

@ -1564,7 +1564,12 @@ String CodeConstructor::to_string()
void CodeConstructor::to_string_def( String& result )
{
AST* ClassStructParent = ast->Parent->Parent;
if (ClassStructParent) {
result.append( ClassStructParent->Name );
}
else {
result.append( ast->Name );
}
if ( ast->Params )
result.append_fmt( "( %S )", ast->Params.to_string() );
@ -1588,12 +1593,15 @@ void CodeConstructor::to_string_fwd( String& result )
if ( ast->Params )
result.append_fmt( "( %S )", ast->Params.to_string() );
else
{
result.append_fmt("()");
if (ast->Body)
result.append_fmt( " = %S", ast->Body.to_string() );
if ( ast->InlineCmt )
result.append_fmt( "(); // %S\n", ast->InlineCmt->Content );
result.append_fmt( "; // %S\n", ast->InlineCmt->Content );
else
result.append( "();\n" );
}
result.append( ";" );
}
String CodeClass::to_string()
@ -2263,15 +2271,21 @@ String CodeParam::to_string()
void CodeParam::to_string( String& result )
{
if ( ast->ValueType.ast == nullptr )
if ( ast->Macro )
{
result.append_fmt( "%S", ast->Name );
// Were using the convention that if the value type is a macro we ignore everything else
// Related to parsing: ( <macro>, ... )
result.append( ast->Macro.ast->Content );
return;
}
if ( ast->Name )
{
if ( ast->ValueType.ast == nullptr )
result.append_fmt( "%S", ast->Name );
else
result.append_fmt( "%S %S", ast->ValueType.to_string(), ast->Name );
}
else
result.append_fmt( "%S", ast->ValueType.to_string() );
@ -5826,11 +5840,12 @@ namespace parser
TF_Preprocess = bit( 2 ),
TF_Preprocess_Cond = bit( 3 ),
TF_Attribute = bit( 6 ),
TF_AccessSpecifier = bit( 7 ),
TF_Specifier = bit( 8 ),
TF_EndDefinition = bit( 9 ), // Either ; or }
TF_Formatting = bit( 10 ),
TF_Literal = bit( 11 ),
TF_AccessOperator = bit( 7 ),
TF_AccessSpecifier = bit( 8 ),
TF_Specifier = bit( 9 ),
TF_EndDefinition = bit( 10 ), // Either ; or }
TF_Formatting = bit( 11 ),
TF_Literal = bit( 12 ),
TF_Null = 0,
};
@ -5854,6 +5869,11 @@ namespace parser
return { Length, Text };
}
bool is_access_operator()
{
return bitfield_is_equal( u32, Flags, TF_AccessOperator );
}
bool is_access_specifier()
{
return bitfield_is_equal( u32, Flags, TF_AccessSpecifier );
@ -6258,6 +6278,11 @@ namespace parser
TokType type = ETokType::to_type( token );
if (type <= TokType::Access_Public && type >= TokType::Access_Private )
{
token.Flags |= TF_AccessSpecifier;
}
if ( type == ETokType::Decl_Extern_Linkage )
{
SkipWhitespace();
@ -6370,7 +6395,7 @@ namespace parser
scanner++;
length++;
}
if ( scanner[ 1 ] == '(' )
if ( scanner[ 0 ] == '(' )
{
length++;
}
@ -6439,7 +6464,7 @@ namespace parser
token.Text = scanner;
token.Length = 1;
token.Type = TokType::Access_MemberSymbol;
token.Flags = TF_AccessSpecifier;
token.Flags = TF_AccessOperator;
if ( left )
{
@ -6805,7 +6830,7 @@ namespace parser
{
token.Length++;
// token.Type = TokType::Access_PointerToMemberSymbol;
token.Flags |= TF_AccessSpecifier;
token.Flags |= TF_AccessOperator;
move_forward();
if ( current == '*' )
@ -7199,6 +7224,7 @@ namespace parser
internal CodeBody parse_class_struct_body( TokType which, Token name = NullToken );
internal Code parse_class_struct( TokType which, bool inplace_def );
internal CodeDefine parse_define();
internal Code parse_expression();
internal Code parse_forward_or_definition( TokType which, bool is_inplace );
internal CodeFn parse_function_after_name( ModuleFlag mflags, CodeAttributes attributes, CodeSpecifiers specifiers, CodeType ret_type, Token name );
internal Code parse_function_body();
@ -7217,7 +7243,7 @@ namespace parser
internal CodeVar parse_variable_declaration_list();
internal CodeClass parse_class( bool inplace_def = false );
internal CodeConstructor parse_constructor();
internal CodeConstructor parse_constructor( CodeSpecifiers specifiers );
internal CodeDestructor parse_destructor( CodeSpecifiers specifiers = NoCode );
internal CodeEnum parse_enum( bool inplace_def = false );
internal CodeBody parse_export_body();
@ -7707,6 +7733,7 @@ namespace parser
{
access = currtok.to_access_specifier();
// <ModuleFlags> <class/struct> <Attributes> <Name> : <Access Specifier>
eat( currtok.Type );
}
Token parent_tok = parse_identifier();
@ -7791,6 +7818,13 @@ namespace parser
switch ( currtok_noskip.Type )
{
case TokType::Statement_End:
{
// TODO(Ed): Convert this to a general warning procedure
log_fmt("Dangling end statement found %S\n", currtok_noskip.to_string());
eat( TokType::Statement_End );
continue;
}
case TokType::NewLine :
member = fmt_newline;
eat( TokType::NewLine );
@ -7941,12 +7975,14 @@ namespace parser
case TokType::Spec_Consteval :
case TokType::Spec_Constexpr :
case TokType::Spec_Constinit :
case TokType::Spec_Explicit:
case TokType::Spec_ForceInline :
case TokType::Spec_Inline :
case TokType::Spec_Mutable :
case TokType::Spec_NeverInline :
case TokType::Spec_Static :
case TokType::Spec_Volatile :
case TokType::Spec_Virtual:
{
SpecifierT specs_found[ 16 ] { ESpecifier::NumSpecifiers };
s32 NumSpecifiers = 0;
@ -7955,28 +7991,40 @@ namespace parser
{
SpecifierT spec = ESpecifier::to_type( currtok );
b32 ignore_spec = false;
switch ( spec )
{
case ESpecifier::Constexpr :
case ESpecifier::Constinit :
case ESpecifier::Explicit:
case ESpecifier::Inline :
case ESpecifier::ForceInline :
case ESpecifier::Mutable :
case ESpecifier::NeverInline :
case ESpecifier::Static :
case ESpecifier::Volatile :
case ESpecifier::Virtual:
break;
case ESpecifier::Consteval :
expects_function = true;
break;
case ESpecifier::Const :
ignore_spec = true;
break;
default :
log_failure( "Invalid specifier %s for variable\n%s", ESpecifier::to_str( spec ), Context.to_string() );
Context.pop();
return CodeInvalid;
}
// Every specifier after would be considered part of the type type signature
if (ignore_spec)
break;
specs_found[ NumSpecifiers ] = spec;
NumSpecifiers++;
eat( currtok.Type );
@ -8018,7 +8066,7 @@ namespace parser
{
if ( str_compare( name.Text, currtok.Text, name.Length ) == 0 )
{
member = parse_constructor();
member = parse_constructor( specifiers );
// <Attributes> <Specifiers> <Name>()
break;
}
@ -8109,12 +8157,38 @@ namespace parser
}
Token tok = tokens[ idx - 1 ];
if ( tok.is_specifier() && is_trailing( ESpecifier::to_type(tok)) )
{
// <which> <type_identifier>(...) <specifier> ...;
s32 spec_idx = idx - 1;
Token spec = tokens[spec_idx];
while ( spec.is_specifier() && is_trailing( ESpecifier::to_type(spec)) )
{
-- spec_idx;
spec = tokens[spec_idx];
}
if ( tokens[spec_idx].Type == TokType::Capture_End )
{
// Forward declaration with trailing specifiers for a procedure
tok = tokens[spec_idx];
Code result = parse_operator_function_or_variable( false, { nullptr }, { nullptr } );
// <Attributes> <Specifiers> <ReturnType/ValueType> <operator <Op>, or Name> ...
Context.pop();
return result;
}
log_failure( "Unsupported or bad member definition after %s declaration\n%s", to_str(which), Context.to_string() );
Context.pop();
return CodeInvalid;
}
if ( tok.Type == TokType::Identifier )
{
tok = tokens[ idx - 2 ];
bool is_indirection = tok.Type == TokType::Ampersand || tok.Type == TokType::Star;
bool ok_to_parse = false;
if ( tok.Type == TokType::BraceCurly_Close )
@ -8130,6 +8204,17 @@ namespace parser
// <which> <type_identifier> <identifier>;
ok_to_parse = true;
}
else if ( tok.Type == TokType::Assign_Classifer
&& tokens[idx - 4].Type == TokType::Decl_Class
&& tokens[idx - 5].Type == which )
{
// Its a forward declaration of an enum class
// <enum> <class> <type_identifier> : <identifier>;
ok_to_parse = true;
Code result = parse_enum();
Context.pop();
return result;
}
else if ( is_indirection )
{
// Its a indirection type with type ID using struct namespace.
@ -8139,7 +8224,7 @@ namespace parser
if ( ! ok_to_parse )
{
log_failure( "Unsupported or bad member definition after struct declaration\n%s", Context.to_string() );
log_failure( "Unsupported or bad member definition after %s declaration\n%s", to_str(which), Context.to_string() );
Context.pop();
return CodeInvalid;
}
@ -8167,7 +8252,7 @@ namespace parser
}
else
{
log_failure( "Unsupported or bad member definition after struct declaration\n%s", Context.to_string() );
log_failure( "Unsupported or bad member definition after %s declaration\n%S", to_str(which).Ptr, Context.to_string() );
Context.pop();
return CodeInvalid;
}
@ -8219,6 +8304,40 @@ namespace parser
return define;
}
internal inline
Code parse_assignment_expression()
{
Code expr = { nullptr };
eat( TokType::Operator );
// <Attributes> <Specifiers> <ValueType> <Name> =
Token expr_tok = currtok;
if ( currtok.Type == TokType::Statement_End && currtok.Type != TokType::Comma )
{
log_failure( "Expected expression after assignment operator\n%s", Context.to_string() );
Context.pop();
return CodeInvalid;
}
s32 level = 0;
while ( left && currtok.Type != TokType::Statement_End && (currtok.Type != TokType::Comma || level > 0) )
{
if (currtok.Type == TokType::Capture_Start)
level++;
else if (currtok.Type == TokType::Capture_End)
level--;
eat( currtok.Type );
}
expr_tok.Length = ( ( sptr )currtok.Text + currtok.Length ) - ( sptr )expr_tok.Text - 1;
expr = untyped_str( expr_tok );
// = <Expression>
return expr;
}
internal inline Code parse_forward_or_definition( TokType which, bool is_inplace )
{
Code result = CodeInvalid;
@ -8416,6 +8535,13 @@ namespace parser
switch ( currtok_noskip.Type )
{
case TokType::Statement_End:
{
// TODO(Ed): Convert this to a general warning procedure
log_fmt("Dangling end statement found %S\n", currtok_noskip.to_string());
eat( TokType::Statement_End );
continue;
}
case TokType::NewLine :
// Empty lines are auto skipped by Tokens.current()
member = fmt_newline;
@ -8625,7 +8751,7 @@ namespace parser
case TokType::Type_double :
case TokType::Type_int :
{
bool found_operator_cast = false;
bool found_operator_cast_outside_class_implmentation = false;
s32 idx = Context.Tokens.Idx;
for ( ; idx < Context.Tokens.Arr.num(); idx++ )
@ -8643,12 +8769,12 @@ namespace parser
}
if ( tok.Type == TokType::Decl_Operator )
found_operator_cast = true;
found_operator_cast_outside_class_implmentation = true;
break;
}
if ( found_operator_cast )
if ( found_operator_cast_outside_class_implmentation )
{
member = parse_operator_cast();
// <Attributes> <Specifiers> <Name>::operator <Type>() { ... }
@ -9197,8 +9323,10 @@ namespace parser
return { nullptr };
}
Code macro = { nullptr };
CodeType type = { nullptr };
Code value = { nullptr };
Token name = NullToken;
if ( check( TokType::Varadic_Argument ) )
{
@ -9211,6 +9339,12 @@ namespace parser
// or < ... >
}
// Ex: Unreal has this type of macro: vvvvvvvv
// COREUOBJECT_API void CallFunction( FFrame& Stack, RESULT_DECL, UFunction* Function );
// This is so that a 'ValueType for the param is a preprocesor macro to support this nasty macro usage'
if ( ! check(TokType::Preprocess_Macro))
{
type = parse_type();
if ( type == Code::Invalid )
{
@ -9219,8 +9353,6 @@ namespace parser
}
// ( <ValueType>
Token name = NullToken;
if ( check( TokType::Identifier ) )
{
name = currtok;
@ -9251,10 +9383,17 @@ namespace parser
// ( <ValueType> <Name> = <Expression>
}
}
}
else {
macro = parse_simple_preprocess(ETokType::Preprocess_Macro);
// ( <Macro>
}
CodeParam result = ( CodeParam )make_code();
result->Type = Parameters;
result->Macro = macro;
if ( name.Length > 0 )
result->Name = get_cached_string( name );
@ -9281,6 +9420,8 @@ namespace parser
// ( <ValueType> <Name> = <Expression>, ...
}
if ( ! check(TokType::Preprocess_Macro))
{
type = parse_type();
if ( type == Code::Invalid )
{
@ -9322,10 +9463,17 @@ namespace parser
}
}
// ( <ValueType> <Name> = <Expression>, <ValueType> <Name> = <Expression>, ..
}
else {
macro = parse_simple_preprocess(ETokType::Preprocess_Macro);
// ( ..., <Macro>
}
CodeParam param = ( CodeParam )make_code();
param->Type = Parameters;
param->Macro = macro;
if ( name.Length > 0 )
param->Name = get_cached_string( name );
@ -9552,26 +9700,8 @@ namespace parser
if ( bitfield_is_equal( u32, currtok.Flags, TF_Assign ) )
{
eat( TokType::Operator );
// <Attributes> <Specifiers> <ValueType> <Name> =
Token expr_tok = currtok;
if ( currtok.Type == TokType::Statement_End || currtok.Type != TokType::Comma )
{
log_failure( "Expected expression after assignment operator\n%s", Context.to_string() );
Context.pop();
return CodeInvalid;
}
while ( left && currtok.Type != TokType::Statement_End || currtok.Type != TokType::Comma )
{
eat( currtok.Type );
}
expr_tok.Length = ( ( sptr )currtok.Text + currtok.Length ) - ( sptr )expr_tok.Text - 1;
expr = untyped_str( expr_tok );
// <Attributes> <Specifiers> <ValueType> <Name> = <Expression>
expr = parse_assignment_expression();
}
if ( currtok.Type == TokType::BraceCurly_Open )
@ -9789,7 +9919,7 @@ namespace parser
return result;
}
internal CodeConstructor parse_constructor()
internal CodeConstructor parse_constructor( CodeSpecifiers specifiers )
{
push_scope();
@ -9808,14 +9938,14 @@ namespace parser
eat( TokType::Assign_Classifer );
// <Name> ( <Parameters> ) :
Token initializer_list_tok = NullToken;
Token initializer_list_tok = currtok;
s32 level = 0;
while ( left && ( currtok.Type != TokType::BraceCurly_Open || level > 0 ) )
{
if ( currtok.Type == TokType::BraceCurly_Open )
if (currtok.Type == TokType::Capture_Start)
level++;
else if ( currtok.Type == TokType::BraceCurly_Close )
else if ( currtok.Type == TokType::Capture_End )
level--;
eat( currtok.Type );
@ -9833,6 +9963,10 @@ namespace parser
body = parse_function_body();
// <Name> ( <Parameters> ) { <Body> }
}
else if ( check( TokType::Operator) && currtok.Text[ 0 ] == '=' )
{
body = parse_assignment_expression();
}
else
{
Token stmt_end = currtok;
@ -9844,15 +9978,21 @@ namespace parser
// <Name> ( <Parameters> ); <InlineCmt>
}
// TODO(Ed): Constructors can have post-fix specifiers
CodeConstructor result = ( CodeConstructor )make_code();
result->Name = get_cached_string(identifier);
result->Specs = specifiers;
if ( params )
result->Params = params;
if ( initializer_list )
result->InitializerList = initializer_list;
if ( body )
if ( body && body->Type == ECode::Function_Body )
{
result->Body = body;
result->Type = ECode::Constructor;
@ -10660,8 +10800,10 @@ namespace parser
s32 NumSpecifiers = 0;
attributes = parse_attributes();
// <export> template< <Parameters> > <Attributes>
// <export> template< <Parameters> > <Attributes> <specifiers.
// Specifiers Parsing
{
while ( left && currtok.is_specifier() )
{
SpecifierT spec = ESpecifier::to_type( currtok );
@ -10694,7 +10836,7 @@ namespace parser
// Ignore const it will be handled by the type
if ( spec == ESpecifier::Const )
continue;
break;
specs_found[ NumSpecifiers ] = spec;
NumSpecifiers++;
@ -10706,8 +10848,10 @@ namespace parser
specifiers = def_specifiers( NumSpecifiers, specs_found );
}
// <export> template< <Parameters> > <Attributes> <Specifiers>
}
// TODO(Ed) : Port over operator cast detection from parse_global_nspace or parse_class_struct_body
// TODO(Ed) : Port over user-defined operator cast detection from
//parse_global_nspace (which is a source defintion version) or parse_class_struct_body (which is an inline class definition)
definition = parse_operator_function_or_variable( expects_function, attributes, specifiers );
// <export> template< <Parameters> > <Attributes> <Specifiers> ...
@ -10781,12 +10925,13 @@ namespace parser
if ( currtok.Type == TokType::Decl_Class || currtok.Type == TokType::Decl_Enum || currtok.Type == TokType::Decl_Struct
|| currtok.Type == TokType::Decl_Union )
{
name = currtok;
eat( currtok.Type );
// <Attributes> <Specifiers> <class, enum, struct, union>
name.Length = ( ( sptr )currtok.Text + currtok.Length ) - ( sptr )name.Text;
eat( TokType::Identifier );
name = parse_identifier();
// name.Length = ( ( sptr )currtok.Text + currtok.Length ) - ( sptr )name.Text;
// eat( TokType::Identifier );
Context.Scope->Name = name;
// <Attributes> <Specifiers> <class, enum, struct, union> <Name>
}
@ -10835,6 +10980,12 @@ namespace parser
name.Length = ( ( sptr )prevtok.Text + prevtok.Length ) - ( sptr )name.Text;
// <Attributes> <Specifiers> <Compound type expression>
}
else if ( currtok.Type == TokType::Type_Typename )
{
name = currtok;
eat(TokType::Type_Typename);
// <typename>
}
// The usual Identifier type signature that may have namespace qualifiers
else
@ -11685,8 +11836,54 @@ CodeConstructor parse_constructor( StrC def )
if ( toks.Arr == nullptr )
return CodeInvalid;
// TODO(Ed): Constructors can have prefix attributes
CodeSpecifiers specifiers;
SpecifierT specs_found[ 16 ] { ESpecifier::NumSpecifiers };
s32 NumSpecifiers = 0;
while ( left && currtok.is_specifier() )
{
SpecifierT spec = ESpecifier::to_type( currtok );
b32 ignore_spec = false;
switch ( spec )
{
case ESpecifier::Constexpr :
case ESpecifier::Explicit:
case ESpecifier::Inline :
case ESpecifier::ForceInline :
case ESpecifier::NeverInline :
break;
case ESpecifier::Const :
ignore_spec = true;
break;
default :
log_failure( "Invalid specifier %s for variable\n%s", ESpecifier::to_str( spec ), Context.to_string() );
Context.pop();
return CodeInvalid;
}
// Every specifier after would be considered part of the type type signature
if (ignore_spec)
break;
specs_found[ NumSpecifiers ] = spec;
NumSpecifiers++;
eat( currtok.Type );
}
if ( NumSpecifiers )
{
specifiers = def_specifiers( NumSpecifiers, specs_found );
// <specifiers> ...
}
Context.Tokens = toks;
CodeConstructor result = parse_constructor();
CodeConstructor result = parse_constructor( specifiers );
return result;
}
@ -11699,6 +11896,9 @@ CodeDestructor parse_destructor( StrC def )
if ( toks.Arr == nullptr )
return CodeInvalid;
// TODO(Ed): Destructors can have prefix attributes
// TODO(Ed): Destructors can have virtual
Context.Tokens = toks;
CodeDestructor result = parse_destructor();
return result;

View File

@ -70,9 +70,9 @@ using LogFailType = sw ( * )( char const*, ... );
enum class AccessSpec : u32
{
Default,
Public,
Protected,
Private,
Protected,
Public,
Num_AccessSpec,
Invalid,
@ -82,9 +82,9 @@ inline char const* to_str( AccessSpec type )
{
local_persist char const* lookup[ ( u32 )AccessSpec::Num_AccessSpec ] = {
"",
"public",
"protected",
"private",
"protected",
"public",
};
if ( type > AccessSpec::Public )
@ -814,6 +814,7 @@ struct AST
union
{
AST* Macro; // Parameter
AST* BitfieldSize; // Variable (Class/Struct Data Member)
AST* Params; // Constructor, Function, Operator, Template, Typename
};
@ -2014,7 +2015,7 @@ struct AST_Constructor
Code Next;
parser::Token* Tok;
Code Parent;
char _PAD_NAME_[ sizeof( StringCached ) ];
StringCached Name;
CodeT Type;
char _PAD_UNUSED_[ sizeof( ModuleFlag ) + sizeof( u32 ) ];
};
@ -2616,11 +2617,11 @@ struct AST_Param
struct
{
char _PAD_PROPERTIES_2_[ sizeof( AST* ) * 3 ];
char _PAD_PROPERTIES_1_[ sizeof( AST* ) * 3 ];
CodeType ValueType;
char _PAD_PROPERTIES_[ sizeof( AST* ) ];
Code Macro;
Code Value;
char _PAD_PROPERTIES_3_[ sizeof( AST* ) ];
char _PAD_PROPERTIES_2_[ sizeof( AST* ) ];
};
};

View File

@ -10,6 +10,7 @@ Implementation design perfs:
* Lift to more specific or generalized code-pathsonly when necessary
* Minimize distinct code-paths
* Use classes as "filters", keep things "mega-structed"
* Use composition for large sets of entiites (when possible).
* Never pre-emtively make interfaces or interface-like patterns
* Keep everything data-wise in the runtime unless there is a measurable performance cost.
* Some exploratory optimizations for educational purposes.

View File

@ -105,7 +105,7 @@ function run-gengasa
$path_AbilitySystem = join-path $path_gasa 'AbilitySystem'
$include = @(
'GasaAttributeSet.h', 'GasaAttributeSet.cpp'
'GasaAttributeSet.h', 'GasaAttributeSet.cpp', 'LETS_SEE.h'
)
format-cpp $path_AbilitySystem $include $null
}