From 92523a13b25fa18068d040a23b7d8cbc3d6eaf55 Mon Sep 17 00:00:00 2001 From: _Redstone_c_ Date: Thu, 7 Apr 2022 15:57:02 +0800 Subject: [PATCH] feat(templates): add TFunction and the corresponding testing --- .../Private/Testing/TemplatesTesting.cpp | 447 +++++++++++++---- .../Source/Public/Templates/Function.h | 459 ++++++++++++++++++ .../Source/Public/Templates/Templates.h | 1 + .../Source/Public/Testing/TemplatesTesting.h | 1 + 4 files changed, 809 insertions(+), 99 deletions(-) create mode 100644 Redcraft.Utility/Source/Public/Templates/Function.h diff --git a/Redcraft.Utility/Source/Private/Testing/TemplatesTesting.cpp b/Redcraft.Utility/Source/Private/Testing/TemplatesTesting.cpp index ef3cd0b..98b256c 100644 --- a/Redcraft.Utility/Source/Private/Testing/TemplatesTesting.cpp +++ b/Redcraft.Utility/Source/Private/Testing/TemplatesTesting.cpp @@ -22,6 +22,7 @@ void TestTemplates() TestVariant(); TestAny(); TestTuple(); + TestFunction(); TestMiscTemplates(); } @@ -532,107 +533,107 @@ void TestAny() void TestTuple() { - always_check((TIsSame&>().GetValue<0>()), int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), const int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), volatile int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), const volatile int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), const int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), const int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), const volatile int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), const volatile int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), volatile int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), const volatile int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), volatile int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), const volatile int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), const volatile int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), const volatile int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), const volatile int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), const volatile int&>::Value)); + always_check((TIsSame&>().GetValue<0>()), int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), const int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), volatile int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), const volatile int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), const int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), const int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), const volatile int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), const volatile int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), volatile int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), const volatile int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), volatile int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), const volatile int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), const volatile int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), const volatile int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), const volatile int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), const volatile int32&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), int&&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), const int&&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), volatile int&&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), const volatile int&&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), const int&&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), const int&&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), const volatile int&&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), const volatile int&&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), volatile int&&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), const volatile int&&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), volatile int&&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), const volatile int&&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), const volatile int&&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), const volatile int&&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), const volatile int&&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), const volatile int&&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), int32&&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), const int32&&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), volatile int32&&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), const volatile int32&&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), const int32&&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), const int32&&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), const volatile int32&&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), const volatile int32&&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), volatile int32&&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), const volatile int32&&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), volatile int32&&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), const volatile int32&&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), const volatile int32&&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), const volatile int32&&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), const volatile int32&&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), const volatile int32&&>::Value)); - always_check((TIsSame&>().GetValue<0>()), int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), const int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), volatile int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), const volatile int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), const int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), volatile int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), const volatile int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), const int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), volatile int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), const volatile int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), const int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), volatile int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), const volatile int&>::Value)); + always_check((TIsSame&>().GetValue<0>()), int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), const int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), volatile int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), const volatile int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), const int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), volatile int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), const volatile int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), const int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), volatile int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), const volatile int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), const int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), volatile int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), const volatile int32&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), int&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), const int&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), volatile int&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), const volatile int&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), int&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), const int&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), volatile int&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), const volatile int&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), int&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), const int&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), volatile int&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), const volatile int&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), int&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), const int&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), volatile int&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), const volatile int&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), int32&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), const int32&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), volatile int32&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), const volatile int32&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), int32&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), const int32&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), volatile int32&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), const volatile int32&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), int32&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), const int32&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), volatile int32&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), const volatile int32&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), int32&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), const int32&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), volatile int32&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), const volatile int32&>::Value)); - always_check((TIsSame&>().GetValue<0>()), int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), const int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), volatile int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), const volatile int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), const int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), volatile int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), const volatile int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), const int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), volatile int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), const volatile int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), const int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), volatile int&>::Value)); - always_check((TIsSame&>().GetValue<0>()), const volatile int&>::Value)); + always_check((TIsSame&>().GetValue<0>()), int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), const int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), volatile int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), const volatile int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), const int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), volatile int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), const volatile int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), const int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), volatile int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), const volatile int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), const int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), volatile int32&>::Value)); + always_check((TIsSame&>().GetValue<0>()), const volatile int32&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), int&&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), const int&&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), volatile int&&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), const volatile int&&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), int&&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), const int&&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), volatile int&&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), const volatile int&&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), int&&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), const int&&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), volatile int&&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), const volatile int&&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), int&&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), const int&&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), volatile int&&>::Value)); - always_check((TIsSame&&>().GetValue<0>()), const volatile int&&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), int32&&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), const int32&&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), volatile int32&&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), const volatile int32&&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), int32&&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), const int32&&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), volatile int32&&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), const volatile int32&&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), int32&&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), const int32&&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), volatile int32&&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), const volatile int32&&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), int32&&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), const int32&&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), volatile int32&&>::Value)); + always_check((TIsSame&&>().GetValue<0>()), const volatile int32&&>::Value)); always_check((TIsSame>::Type, double>::Value)); always_check((TIsSame>::Type, float&>::Value)); @@ -660,11 +661,11 @@ void TestTuple() always_check((TTupleElementIndex>::Value == 1)); always_check((TTupleElementIndex>::Value == 2)); - always_check((TTupleElementIndex>::Value == INDEX_NONE)); + always_check((TTupleElementIndex>::Value == INDEX_NONE)); -// always_check((TIsSame::Type, double>::Value)); +// always_check((TIsSame::Type, double>::Value)); -// always_check((TTupleElementIndex::Value == 0)); +// always_check((TTupleElementIndex::Value == 0)); // always_check((TIsSame>::Type, double>::Value)); @@ -910,6 +911,254 @@ void TestTuple() NAMESPACE_UNNAMED_BEGIN +struct FFunctionDebug +{ + int32 Index = 0; + int32 Output[12]; + void Print(int32 In) { Output[Index++] = In; } +}; + +FFunctionDebug FunctionDebug; + +struct FPrintAdd +{ + FPrintAdd(int32 InNum) : Num(InNum) { } + void F(int32 I) const { FunctionDebug.Print(Num + I); } + int32 Num; +}; + +void PrintNum(int32 I) +{ + FunctionDebug.Print(I); +} + +struct FPrintNum +{ + void operator()(int32 I) const + { + FunctionDebug.Print(I); + } +}; + +NAMESPACE_UNNAMED_END + +void TestFunction() +{ + { +// TFunctionRef TempA; + TFunction TempB; + TUniqueFunction TempC; + } + + { + struct FFunctor + { + int32 operator()() & { return 0; } + int32 operator()() && { return 1; } + int32 operator()() const& { return 2; } + int32 operator()() const&& { return 3; } + }; + + FFunctor Functor; + + TFunctionRef TempA = Functor; + TFunctionRef TempB = Functor; + TFunctionRef TempC = Functor; + TFunctionRef TempD = Functor; + TFunctionRef TempE = Functor; + TFunctionRef TempF = Functor; + + always_check( TempA() == 0); + always_check( TempB() == 0); + always_check(MoveTemp(TempC)() == 1); + always_check( TempD() == 2); + always_check( TempE() == 2); + always_check(MoveTemp(TempF)() == 3); + } + + { + int32 Offset = 0xFA00; + auto FuncA = [&Offset](int32 In) { return In + Offset; }; + + TFunctionRef TempA = FuncA; + Offset = 0xFB00; + always_check(TempA(0xAA) == 0xFBAA); + + TFunction TempB = FuncA; + Offset = 0xFC00; + always_check(TempB(0xAB) == 0xFCAB); + + TUniqueFunction TempC = FuncA; + Offset = 0xFD00; + always_check(TempC(0xAC) == 0xFDAC); + } + + { + struct FFunctor + { + int32 A; + FFunctor(int32 InA) : A(InA) { } + int32 operator()() const { return A; } + }; + + TFunction TempA = FFunctor(0xAA); + TFunction TempB(InPlaceType, 0xBB); + + TempA(); + TempB(); + + TFunction TempC = FFunctor(0xAA); + TFunction TempD(InPlaceType, 0xBB); + + always_check(TempC() == 0xAA); + always_check(TempD() == 0xBB); + + TempA = nullptr; + TempB = nullptr; + + always_check(!TempA.IsValid()); + always_check(!TempB.IsValid()); + + TempA = FFunctor(0xCC); + TempB.Emplace(0xDD); + + always_check(TempA.IsValid()); + always_check(TempB.IsValid()); + + TempA(); + TempB(); + + TempC.Reset(); + TempD.Reset(); + + always_check(!TempC.IsValid()); + always_check(!TempD.IsValid()); + + TempC = FFunctor(0xEE); + TempD.Emplace(0xFF); + + always_check(TempC.IsValid()); + always_check(TempD.IsValid()); + + always_check(TempC() == 0xEE); + always_check(TempD() == 0xFF); + + always_check(TempC.TargetType() == Typeid(FFunctor)); + always_check(TempD.TargetType() == Typeid(FFunctor)); + } + + { + TFunctionRef RefA = [] { }; + TFunction ObjectA = [] { }; + TUniqueFunction UniqueA = [] { }; + + TFunctionRef RefB = RefA; +// TFunction ObjectB = RefA; +// TUniqueFunction UniqueB = RefA; + + TFunctionRef RefC = ObjectA; + TFunction ObjectC = ObjectA; + TUniqueFunction UniqueC = ObjectA; + + TFunctionRef RefD = UniqueA; +// TFunction ObjectD = UniqueA; +// TUniqueFunction UniqueD = UniqueA; + + TFunctionRef RefE = MoveTemp(RefA); +// TFunction ObjectE = MoveTemp(RefA); +// TUniqueFunction UniqueE = MoveTemp(RefA); + + TFunctionRef RefF = MoveTemp(ObjectA); + TFunction ObjectF = MoveTemp(ObjectA); + TUniqueFunction UniqueF = MoveTemp(ObjectA); + + TFunctionRef RefG = MoveTemp(UniqueA); +// TFunction ObjectG = MoveTemp(UniqueA); + TUniqueFunction UniqueG = MoveTemp(UniqueA); + } + + { + TFunctionRef RefA = [] { }; + TFunction ObjectA = [] { }; + TUniqueFunction UniqueA = [] { }; + +// TFunctionRef RefB; RefB = RefA; +// TFunction ObjectB; ObjectB = RefA; +// TUniqueFunction UniqueB; UniqueB = RefA; + +// TFunctionRef RefC; RefC = ObjectA; + TFunction ObjectC; ObjectC = ObjectA; + TUniqueFunction UniqueC; UniqueC = ObjectA; + +// TFunctionRef RefD; RefD = UniqueA; +// TFunction ObjectD; ObjectD = UniqueA; +// TUniqueFunction UniqueD; UniqueD = UniqueA; + +// TFunctionRef RefE; RefE = MoveTemp(RefA); +// TFunction ObjectE; ObjectE = MoveTemp(RefA); +// TUniqueFunction UniqueE; UniqueE = MoveTemp(RefA); + +// TFunctionRef RefF; RefF = MoveTemp(ObjectA); + TFunction ObjectF; ObjectF = MoveTemp(ObjectA); + TUniqueFunction UniqueF; UniqueF = MoveTemp(ObjectA); + +// TFunctionRef RefG; RefG = MoveTemp(UniqueA); +// TFunction ObjectG; ObjectG = MoveTemp(UniqueA); + TUniqueFunction UniqueG; UniqueG = MoveTemp(UniqueA); + } + + { + TFunction Display = PrintNum; + Display(-9); + + TFunction Display42 = [] { PrintNum(42); }; + Display42(); + + TFunction Display31337 = [] { PrintNum(31337); }; + Display31337(); + + TFunction AddDisplay = &FPrintAdd::F; + const FPrintAdd Foo(314159); + AddDisplay(Foo, 1); + AddDisplay(314159, 1); + + TFunction Num = &FPrintAdd::Num; + FunctionDebug.Print(Num(Foo)); + + TFunction AddDisplay2 = [Foo](int32 A) { Foo.F(A); }; + AddDisplay2(2); + + TFunction AddDisplay3 = [Ptr = &Foo](int32 A) { Ptr->F(A); }; + AddDisplay3(3); + + TFunction DisplayObject = FPrintNum(); + DisplayObject(18); + + auto Factorial = [](int32 N) { + TFunction Fac = [&](int32 N) { return (N < 2) ? 1 : N * Fac(N - 1); }; + return Fac(N); + }; + + for (int32 I = 5; I < 8; ++I) FunctionDebug.Print(Factorial(I)); + + always_check(FunctionDebug.Index == 12); + always_check(FunctionDebug.Output[0] == -9); + always_check(FunctionDebug.Output[1] == 42); + always_check(FunctionDebug.Output[2] == 31337); + always_check(FunctionDebug.Output[3] == 314160); + always_check(FunctionDebug.Output[4] == 314160); + always_check(FunctionDebug.Output[5] == 314159); + always_check(FunctionDebug.Output[6] == 314161); + always_check(FunctionDebug.Output[7] == 314162); + always_check(FunctionDebug.Output[8] == 18); + always_check(FunctionDebug.Output[9] == 120); + always_check(FunctionDebug.Output[10] == 720); + always_check(FunctionDebug.Output[11] == 5040); + } +} + +NAMESPACE_UNNAMED_BEGIN + template struct TTestStructA { diff --git a/Redcraft.Utility/Source/Public/Templates/Function.h b/Redcraft.Utility/Source/Public/Templates/Function.h new file mode 100644 index 0000000..1fcfeb3 --- /dev/null +++ b/Redcraft.Utility/Source/Public/Templates/Function.h @@ -0,0 +1,459 @@ +#pragma once + +#include "CoreTypes.h" +#include "Memory/Memory.h" +#include "Templates/Any.h" +#include "Templates/Tuple.h" +#include "Templates/Invoke.h" +#include "Templates/Container.h" +#include "TypeTraits/TypeTraits.h" +#include "Miscellaneous/TypeInfo.h" +#include "Miscellaneous/AssertionMacros.h" + +NAMESPACE_REDCRAFT_BEGIN +NAMESPACE_MODULE_BEGIN(Redcraft) +NAMESPACE_MODULE_BEGIN(Utility) + +NAMESPACE_PRIVATE_BEGIN + +enum class EFunctionType +{ + Reference, + Object, + Unique, +}; + +enum class EFunctionSpecifiers +{ + None, + LValue, + RValue, + Const, + ConstLValue, + ConstRValue, +}; + +template +struct TFunctionImpl; + +template struct TIsTFunctionImpl : FFalse { }; +template struct TIsTFunctionImpl> : FTrue { }; + +template struct TIsTFunctionRef : FFalse { }; +template struct TIsTFunctionRef> : FTrue { }; + +template struct TIsTFunction : FFalse { }; +template struct TIsTFunction> : FTrue { }; + +template struct TIsTUniqueFunction : FFalse { }; +template struct TIsTUniqueFunction> : FTrue { }; + +struct FFunctionIsBoundImpl +{ + template + static constexpr bool F(const T& Func) + { + if constexpr (TIsPointer::Value || TIsMemberPointer::Value || TIsTFunctionImpl::Value) + { + return !!Func; + } + else + { + return true; + } + } +}; + +template +struct TIsInvocableResultWithSpecifiers : FFalse { }; + +template +struct TIsInvocableResultWithSpecifiers + : TBoolConstant::Value && TIsInvocableResult::Value> +{ }; + +template +struct TIsInvocableResultWithSpecifiers : TIsInvocableResult { }; + +template +struct TIsInvocableResultWithSpecifiers : TIsInvocableResult { }; + +template +struct TIsInvocableResultWithSpecifiers + : TBoolConstant::Value && TIsInvocableResult::Value> +{ }; + +template +struct TIsInvocableResultWithSpecifiers : TIsInvocableResult { }; + +template +struct TIsInvocableResultWithSpecifiers : TIsInvocableResult { }; + +template +struct TFunctionCallImpl; + +template +struct TFunctionCallImpl +{ + static inline R F(void* Func, Types&&... Args) + { + return InvokeResult(*reinterpret_cast(Func), Forward(Args)...); + } +}; + +template +struct TFunctionCallImpl +{ + static inline R F(void* Func, Types&&... Args) + { + return InvokeResult(*reinterpret_cast(Func), Forward(Args)...); + } +}; + +template +struct TFunctionCallImpl +{ + static inline R F(void* Func, Types&&... Args) + { + return InvokeResult(MoveTemp(*reinterpret_cast(Func)), Forward(Args)...); + } +}; + +template +struct TFunctionCallImpl +{ + static inline R F(void* Func, Types&&... Args) + { + return InvokeResult(AsConst(*reinterpret_cast(Func)), Forward(Args)...); + } +}; + +template +struct TFunctionCallImpl +{ + static inline R F(void* Func, Types&&... Args) + { + return InvokeResult(AsConst(*reinterpret_cast(Func)), Forward(Args)...); + } +}; + +template +struct TFunctionCallImpl +{ + static inline R F(void* Func, Types&&... Args) + { + return InvokeResult(MoveTemp(AsConst(*reinterpret_cast(Func))), Forward(Args)...); + } +}; + +template +struct TFunctionImpl +{ +public: + + using ResultType = R; + using ArgumentType = TTuple; + + constexpr TFunctionImpl(nullptr_t = nullptr) requires (FunctionType != EFunctionType::Reference) : Call(nullptr) { } + + TFunctionImpl(const TFunctionImpl& InValue) requires (FunctionType != EFunctionType::Unique) + : Call(InValue.Call), Storage(InValue.Storage) + { } + + TFunctionImpl(TFunctionImpl&& InValue) + : Call(InValue.Call), Storage(MoveTemp(InValue.Storage)) + { if constexpr (FunctionType != EFunctionType::Reference) InValue.Reset(); } + + template requires (!TIsTFunctionImpl::Type>::Value) && (!TIsTInPlaceType::Type>::Value) + && TIsInvocableResultWithSpecifiers::Type, Types...>::Value + && (FunctionType == EFunctionType::Reference || TIsConstructible::Type, T&&>::Value) + && ((FunctionType == EFunctionType::Object && TIsCopyConstructible::Type>::Value) + || (FunctionType == EFunctionType::Unique && TIsMoveConstructible::Type>::Value) + || FunctionType == EFunctionType::Reference) + FORCEINLINE TFunctionImpl(T&& InValue) + { + using DecayedFunctorType = typename TDecay::Type; + + if constexpr (FunctionType == EFunctionType::Reference) + { + checkf(FFunctionIsBoundImpl::F(InValue), TEXT("Cannot bind a null/unbound callable to a TFunctionRef")); + } + + if (!FFunctionIsBoundImpl::F(InValue)) Call = nullptr; + else EmplaceImpl(Forward(InValue)); + } + + template requires (FunctionType != EFunctionType::Reference) + && TIsInvocableResultWithSpecifiers::Type, Types...>::Value && TIsConstructible::Type, ArgTypes...>::Value + && ((FunctionType == EFunctionType::Object && TIsCopyConstructible::Type>::Value) + || (FunctionType == EFunctionType::Unique && TIsMoveConstructible::Type>::Value)) + FORCEINLINE TFunctionImpl(TInPlaceType, ArgTypes&&... Args) + { + using DecayedFunctorType = typename TDecay::Type; + EmplaceImpl(Forward(Args)...); + } + + template + requires (FunctionType == EFunctionType::Reference) && (OtherFunctionType != EFunctionType::Reference) + FORCEINLINE TFunctionImpl(const TFunctionImpl& InValue) + { + checkf(FFunctionIsBoundImpl::F(InValue), TEXT("Cannot bind a null/unbound callable to a TFunctionRef")); + EmplaceImpl>(InValue); + } + + FORCEINLINE TFunctionImpl(const TFunctionImpl& InValue) requires (FunctionType == EFunctionType::Unique) + : Call((*reinterpret_cast(&InValue)).Call), Storage((*reinterpret_cast(&InValue)).Storage) + { } + + FORCEINLINE TFunctionImpl(TFunctionImpl&& InValue) requires (FunctionType == EFunctionType::Unique) + : Call((*reinterpret_cast(&InValue)).Call), Storage(MoveTemp((*reinterpret_cast(&InValue)).Storage)) + { InValue.Reset(); } + + ~TFunctionImpl() = default; + + FORCEINLINE TFunctionImpl& operator=(const TFunctionImpl& InValue) requires (FunctionType == EFunctionType::Object) + { + AssignImpl(InValue); + return *this; + } + + FORCEINLINE TFunctionImpl& operator=(TFunctionImpl&& InValue) requires (FunctionType != EFunctionType::Reference) + { + if (&InValue == this) return *this; + AssignImpl(MoveTemp(InValue)); + return *this; + } + + FORCEINLINE TFunctionImpl& operator=(const TFunctionImpl& InValue) requires (FunctionType == EFunctionType::Unique) + { + AssignImpl(*reinterpret_cast(&InValue)); + return *this; + } + + FORCEINLINE TFunctionImpl& operator=(TFunctionImpl&& InValue) requires (FunctionType == EFunctionType::Unique) + { + AssignImpl(MoveTemp(*reinterpret_cast(&InValue))); + return *this; + } + + constexpr TFunctionImpl& operator=(nullptr_t) requires (FunctionType != EFunctionType::Reference) { Reset(); return *this; } + + template requires (FunctionType != EFunctionType::Reference) && (!TIsTFunctionImpl::Type>::Value) + && TIsInvocableResultWithSpecifiers::Type, Types...>::Value + && TIsConstructible::Type, T&&>::Value + && ((FunctionType == EFunctionType::Object && TIsCopyConstructible::Type>::Value) + || (FunctionType == EFunctionType::Unique && TIsMoveConstructible::Type>::Value)) + FORCEINLINE TFunctionImpl& operator=(T&& InValue) + { + using DecayedFunctorType = typename TDecay::Type; + + if (!FFunctionIsBoundImpl::F(InValue)) Reset(); + else EmplaceImpl(Forward(InValue)); + + return *this; + } + + template requires (FunctionType != EFunctionType::Reference) + && TIsInvocableResultWithSpecifiers::Type, Types...>::Value + && TIsConstructible::Type, ArgTypes...>::Value + && ((FunctionType == EFunctionType::Object && TIsCopyConstructible::Type>::Value) + || (FunctionType == EFunctionType::Unique && TIsMoveConstructible::Type>::Value)) + FORCEINLINE typename TDecay::Type& Emplace(ArgTypes&&... Args) + { + using DecayedFunctorType = typename TDecay::Type; + EmplaceImpl(Forward(Args)...); + return *reinterpret_cast::Type*>(Target()); + } + + FORCEINLINE ResultType operator()(Types... Args) requires (Specifiers == EFunctionSpecifiers::None ) { return CallImpl(Forward(Args)...); } + FORCEINLINE ResultType operator()(Types... Args) & requires (Specifiers == EFunctionSpecifiers::LValue ) { return CallImpl(Forward(Args)...); } + FORCEINLINE ResultType operator()(Types... Args) && requires (Specifiers == EFunctionSpecifiers::RValue ) { return CallImpl(Forward(Args)...); } + FORCEINLINE ResultType operator()(Types... Args) const requires (Specifiers == EFunctionSpecifiers::Const ) { return CallImpl(Forward(Args)...); } + FORCEINLINE ResultType operator()(Types... Args) const& requires (Specifiers == EFunctionSpecifiers::ConstLValue) { return CallImpl(Forward(Args)...); } + FORCEINLINE ResultType operator()(Types... Args) const&& requires (Specifiers == EFunctionSpecifiers::ConstRValue) { return CallImpl(Forward(Args)...); } + + constexpr bool IsValid() const { return Call != nullptr; } + constexpr explicit operator bool() const { return Call != nullptr; } + + FORCEINLINE const FTypeInfo& TargetType() const requires (FunctionType != EFunctionType::Reference) { return IsValid() ? Storage.GetTypeInfo() : Typeid(void); }; + + FORCEINLINE void* Target() { if constexpr (FunctionType != EFunctionType::Reference) return Storage.GetData(); else return Storage; } + FORCEINLINE const void* Target() const { if constexpr (FunctionType != EFunctionType::Reference) return Storage.GetData(); else return Storage; } + + constexpr void Reset() requires (FunctionType != EFunctionType::Reference) { Call = nullptr; } + +private: + + using CallFunc = ResultType(*)(void*, Types&&...); + using StorageType = typename TConditional>::Type; + + CallFunc Call; + StorageType Storage; + + template + FORCEINLINE void EmplaceImpl(ArgTypes&&... Args) + { + if constexpr (FunctionType == EFunctionType::Reference) Storage = ((void*)&Args, ...); + else Storage.template Emplace(Forward(Args)...); + Call = &TFunctionCallImpl::F; + } + + FORCEINLINE ResultType CallImpl(Types&&... Args) const + { + checkf(IsValid(), TEXT("Attempting to call an unbound TFunction!")); + return Call(const_cast(*this).Target(), Forward(Args)...); + } + + FORCEINLINE void AssignImpl(const TFunctionImpl& InValue) + { + if (InValue.IsValid()) + { + Call = InValue.Call; + Storage = InValue.Storage; + } + else Reset(); + } + + FORCEINLINE void AssignImpl(TFunctionImpl&& InValue) + { + if (InValue.IsValid()) + { + Call = InValue.Call; + Storage = MoveTemp(InValue.Storage); + InValue.Reset(); + } + else Reset(); + } + +}; + +template +struct TFunctionSelect; + +template +struct TFunctionSelect +{ + using Type = NAMESPACE_PRIVATE::TFunctionImpl; +}; + +template +struct TFunctionSelect +{ + using Type = NAMESPACE_PRIVATE::TFunctionImpl; +}; + +template +struct TFunctionSelect +{ + using Type = NAMESPACE_PRIVATE::TFunctionImpl; +}; + +template +struct TFunctionSelect +{ + using Type = NAMESPACE_PRIVATE::TFunctionImpl; +}; + +template +struct TFunctionSelect +{ + using Type = NAMESPACE_PRIVATE::TFunctionImpl; +}; + +template +struct TFunctionSelect +{ + using Type = NAMESPACE_PRIVATE::TFunctionImpl; +}; + +NAMESPACE_PRIVATE_END + +inline constexpr size_t FUNCTION_DEFAULT_INLINE_SIZE = 32; +inline constexpr size_t FUNCTION_DEFAULT_INLINE_ALIGNMENT = 16; + +template +using TFunctionRef = typename NAMESPACE_PRIVATE::TFunctionSelect::Type; + +template +using TFunction = typename NAMESPACE_PRIVATE::TFunctionSelect::Type; + +template +using TUniqueFunction = typename NAMESPACE_PRIVATE::TFunctionSelect::Type; + +template struct TIsTFunctionRef : NAMESPACE_PRIVATE::TIsTFunctionRef { }; +template struct TIsTFunction : NAMESPACE_PRIVATE::TIsTFunction { }; +template struct TIsTUniqueFunction : NAMESPACE_PRIVATE::TIsTUniqueFunction { }; + +template +constexpr bool operator==(const TFunctionRef& LHS, nullptr_t) +{ + return !LHS; +} + +template +constexpr bool operator==(const TFunction& LHS, nullptr_t) +{ + return !LHS; +} + +template +constexpr bool operator==(const TUniqueFunction& LHS, nullptr_t) +{ + return !LHS; +} + +template +constexpr void Swap(TFunction& A, TFunction& B) +{ + if (!A && !B) return; + + if (A && !B) + { + B = MoveTemp(A); + A = nullptr; + return; + } + + if (B && !A) + { + A = MoveTemp(B); + B = nullptr; + return; + } + + TFunction Temp = MoveTemp(A); + A = MoveTemp(B); + B = MoveTemp(Temp); +} + +template +constexpr void Swap(TUniqueFunction& A, TUniqueFunction& B) +{ + if (!A && !B) return; + + if (A && !B) + { + B = MoveTemp(A); + A = nullptr; + return; + } + + if (B && !A) + { + A = MoveTemp(B); + B = nullptr; + return; + } + + TFunction Temp = MoveTemp(A); + A = MoveTemp(B); + B = MoveTemp(Temp); +} + +static_assert(sizeof(TFunctionRef) == 16, "The byte size of TFunctionRef is unexpected"); +static_assert(sizeof(TFunction) == 64, "The byte size of TFunction is unexpected"); +static_assert(sizeof(TUniqueFunction) == 64, "The byte size of TUniqueFunction is unexpected"); + +NAMESPACE_MODULE_END(Utility) +NAMESPACE_MODULE_END(Redcraft) +NAMESPACE_REDCRAFT_END diff --git a/Redcraft.Utility/Source/Public/Templates/Templates.h b/Redcraft.Utility/Source/Public/Templates/Templates.h index ba950e7..57f58d4 100644 --- a/Redcraft.Utility/Source/Public/Templates/Templates.h +++ b/Redcraft.Utility/Source/Public/Templates/Templates.h @@ -12,3 +12,4 @@ #include "Templates/IntegerSequence.h" #include "Templates/Tuple.h" #include "Templates/TypeHash.h" +#include "Templates/Function.h" diff --git a/Redcraft.Utility/Source/Public/Testing/TemplatesTesting.h b/Redcraft.Utility/Source/Public/Testing/TemplatesTesting.h index a39cf32..c92a87a 100644 --- a/Redcraft.Utility/Source/Public/Testing/TemplatesTesting.h +++ b/Redcraft.Utility/Source/Public/Testing/TemplatesTesting.h @@ -15,6 +15,7 @@ REDCRAFTUTILITY_API void TestOptional(); REDCRAFTUTILITY_API void TestVariant(); REDCRAFTUTILITY_API void TestAny(); REDCRAFTUTILITY_API void TestTuple(); +REDCRAFTUTILITY_API void TestFunction(); REDCRAFTUTILITY_API void TestMiscTemplates(); NAMESPACE_END(Testing)