aboutsummaryrefslogtreecommitdiff
path: root/test/source/TestFixedFunction.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'test/source/TestFixedFunction.cpp')
-rw-r--r--test/source/TestFixedFunction.cpp614
1 files changed, 614 insertions, 0 deletions
diff --git a/test/source/TestFixedFunction.cpp b/test/source/TestFixedFunction.cpp
new file mode 100644
index 0000000..272b545
--- /dev/null
+++ b/test/source/TestFixedFunction.cpp
@@ -0,0 +1,614 @@
+/////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+/////////////////////////////////////////////////////////////////////////////
+
+#include <EABase/eabase.h>
+#include <EAAssert/eaassert.h>
+
+// Included prior to EASTLTest.h to guard against the following bug resurfacing:
+// https://github.com/electronicarts/EASTL/issues/275
+#include <EASTL/fixed_function.h>
+
+#include "EASTLTest.h"
+#include <EASTL/numeric.h>
+
+EA_DISABLE_ALL_VC_WARNINGS()
+#include <functional>
+EA_RESTORE_ALL_VC_WARNINGS()
+
+
+///////////////////////////////////////////////////////////////////////////////
+// TestFixedFunctionDtor
+//
+int TestFixedFunctionDtor()
+{
+ using namespace eastl;
+
+ int nErrorCount = 0;
+
+ {
+ TestObject to;
+ TestObject::Reset();
+ {
+ eastl::fixed_function<sizeof(TestObject), void(void)> ff = [to] {};
+ ff();
+ }
+ VERIFY(TestObject::IsClear());
+ }
+
+ return nErrorCount;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// TestFixedFunctionStdBind
+//
+int TestFixedFunctionStdBind()
+{
+ using namespace eastl;
+
+ int nErrorCount = 0;
+ int val = 0;
+
+ {
+ TestObject to;
+ auto lambda = [to, &val] { ++val; };
+ TestObject::Reset();
+ {
+ eastl::fixed_function<64, void(void)> ff = std::bind(lambda);
+ ff();
+ }
+ VERIFY(TestObject::IsClear());
+ VERIFY(val == 1);
+ }
+ {
+ TestObject to;
+ auto lambda = [to, &val] { ++val; };
+ TestObject::Reset();
+ {
+ eastl::fixed_function<64, void(void)> ff = nullptr;
+ ff = std::bind(lambda);
+ ff();
+ }
+ VERIFY(TestObject::IsClear());
+ VERIFY(val == 2);
+ }
+
+ return nErrorCount;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// TestFixedFunctionReferenceWrapper
+//
+int TestFixedFunctionReferenceWrapper()
+{
+ using namespace eastl;
+
+ int nErrorCount = 0;
+ int val = 0;
+
+ {
+ TestObject to;
+ auto lambda = [to, &val] { ++val; };
+ TestObject::Reset();
+ {
+ eastl::fixed_function<sizeof(eastl::reference_wrapper<decltype(lambda)>), void(void)> ff = eastl::reference_wrapper<decltype(lambda)>(lambda);
+ ff();
+ }
+ VERIFY(TestObject::IsClear());
+ VERIFY(val == 1);
+ }
+ {
+ TestObject to;
+ auto lambda = [to, &val] { ++val; };
+ TestObject::Reset();
+ {
+ eastl::fixed_function<sizeof(eastl::reference_wrapper<decltype(lambda)>), void(void)> ff = nullptr;
+ ff = eastl::reference_wrapper<decltype(lambda)>(lambda);
+ ff();
+ }
+ VERIFY(TestObject::IsClear());
+ VERIFY(val == 2);
+ }
+
+ return nErrorCount;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// TestFixedFunctionFunctionPointer
+//
+
+static void TestVoidRet(int* p)
+{
+ *p += 1;
+}
+
+static int TestIntRet(int* p)
+{
+ int ret = *p;
+ *p += 1;
+ return ret;
+}
+
+int TestFixedFunctionFunctionPointer()
+{
+ using namespace eastl;
+
+ typedef int (*FuncPtrInt)(int*);
+ typedef void (*FuncPtrVoid)(int*);
+
+ int nErrorCount = 0;
+ int val = 0;
+
+ {
+ eastl::fixed_function<sizeof(FuncPtrVoid), void(int*)> ff = &TestVoidRet;
+ ff(&val);
+ VERIFY(val == 1);
+ }
+ {
+ eastl::fixed_function<sizeof(FuncPtrVoid), void(int*)> ff;
+ ff = &TestVoidRet;
+ ff(&val);
+ VERIFY(val == 2);
+ }
+ {
+ eastl::fixed_function<sizeof(FuncPtrInt), int(int*)> ff = &TestIntRet;
+ int ret = ff(&val);
+ VERIFY(ret == 2);
+ VERIFY(val == 3);
+ }
+ {
+ eastl::fixed_function<sizeof(FuncPtrInt), int(int*)> ff;
+ ff = &TestIntRet;
+ int ret = ff(&val);
+ VERIFY(ret == 3);
+ VERIFY(val == 4);
+ }
+
+ return nErrorCount;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// TestFixedFunctionPointerToMemberFunction
+//
+
+int TestFixedFunctionPointerToMemberFunction()
+{
+ using namespace eastl;
+
+ struct TestVoidRet
+ {
+ TestVoidRet() : x(0) {}
+ ~TestVoidRet() = default;
+
+ void IncX() const
+ {
+ ++x;
+ }
+
+ void IncX()
+ {
+ ++x;
+ }
+
+ mutable int x = 0;
+ };
+
+ struct TestIntRet
+ {
+ TestIntRet() : x(0) {}
+
+ int IncX() const
+ {
+ return x++;
+ }
+
+ int IncX()
+ {
+ return x++;
+ }
+
+ mutable int x = 0;
+ };
+
+ int nErrorCount = 0;
+ TestVoidRet voidRet;
+ TestIntRet intRet;
+ const TestVoidRet cvoidRet;
+ const TestIntRet cintRet;
+
+ typedef void (TestVoidRet::*PTMFSize)(void);
+
+ {
+ eastl::fixed_function<sizeof(PTMFSize), void(const TestVoidRet&)> ff = static_cast<void(TestVoidRet::*)() const>(&TestVoidRet::IncX);
+ ff(cvoidRet);
+ VERIFY(cvoidRet.x == 1);
+ }
+ {
+ eastl::fixed_function<sizeof(PTMFSize), void(const TestVoidRet&)> ff = static_cast<void(TestVoidRet::*)() const>(&TestVoidRet::IncX);
+ ff(voidRet);
+ VERIFY(voidRet.x == 1);
+ }
+ {
+ eastl::fixed_function<sizeof(PTMFSize), void(TestVoidRet&)> ff = static_cast<void(TestVoidRet::*)()>(&TestVoidRet::IncX);
+ ff(voidRet);
+ VERIFY(voidRet.x == 2);
+ }
+
+ {
+ eastl::fixed_function<sizeof(PTMFSize), int(const TestIntRet&)> ff = static_cast<int(TestIntRet::*)() const>(&TestIntRet::IncX);
+ int ret = ff(cintRet);
+ VERIFY(ret == 0);
+ VERIFY(cintRet.x == 1);
+ }
+ {
+ eastl::fixed_function<sizeof(PTMFSize), int(const TestIntRet&)> ff = static_cast<int(TestIntRet::*)() const>(&TestIntRet::IncX);
+ int ret = ff(intRet);
+ VERIFY(ret == 0);
+ VERIFY(intRet.x == 1);
+ }
+ {
+ eastl::fixed_function<sizeof(PTMFSize), int(TestIntRet&)> ff = static_cast<int(TestIntRet::*)()>(&TestIntRet::IncX);
+ int ret = ff(intRet);
+ VERIFY(ret == 1);
+ VERIFY(intRet.x == 2);
+ }
+
+ return nErrorCount;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// TestFixedFunctionPointerToMemberData
+//
+
+int TestFixedFunctionPointerToMemberData()
+{
+ using namespace eastl;
+
+ struct Test
+ {
+ Test() : x(1) {}
+ int x = 1;
+ };
+
+ int nErrorCount = 0;
+
+ Test t;
+ const Test ct;
+
+ {
+ eastl::fixed_function<sizeof(void*), int(const Test&)> ff = &Test::x;
+ int ret = ff(t);
+ VERIFY(ret == 1);
+ }
+ {
+ eastl::fixed_function<sizeof(void*), int(const Test&)> ff = &Test::x;
+ int ret = ff(ct);
+ VERIFY(ret == 1);
+ }
+ {
+ eastl::fixed_function<sizeof(void*), int(const Test&)> ff;
+ ff = &Test::x;
+ int ret = ff(t);
+ VERIFY(ret == 1);
+ }
+ {
+ eastl::fixed_function<sizeof(void*), int(const Test&)> ff;
+ ff = &Test::x;
+ int ret = ff(ct);
+ VERIFY(ret == 1);
+ }
+
+ return nErrorCount;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// TestFixedFunctionExistingClosure
+//
+int TestFixedFunctionExistingClosure()
+{
+ using namespace eastl;
+
+ int nErrorCount = 0;
+
+ {
+ TestObject to;
+ {
+ using ff_t = eastl::fixed_function<sizeof(TestObject), void(void)>;
+ {
+ ff_t ff1 = [to] {};
+ ff_t ff3 = [to] {};
+ TestObject::Reset();
+ {
+ ff_t ff2 = ff1;
+ ff2 = ff3; // copy over function that holds existing closure state
+ }
+ VERIFY(TestObject::IsClear());
+ }
+ {
+ ff_t ff1 = [to] {};
+ TestObject::Reset();
+ ff_t ff3 = [to] {};
+ {
+ ff_t ff2 = ff1;
+ ff2 = eastl::move(ff3); // copy over function that holds existing closure state
+ }
+ VERIFY(TestObject::IsClear());
+ }
+ {
+ ff_t ff1 = [to] {};
+ TestObject::Reset();
+ {
+ ff_t ff2 = ff1;
+ ff2 = nullptr;
+ }
+ VERIFY(TestObject::IsClear());
+ }
+ {
+ TestObject::Reset();
+ ff_t ff1 = [to] {};
+ {
+ ff_t ff2 = eastl::move(ff1);
+ ff2 = nullptr;
+ }
+ VERIFY(TestObject::IsClear());
+ }
+ }
+ }
+
+ return nErrorCount;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// TestFixedFunctionCaptureless
+//
+// Tests calling a captureless (eg. function pointer) callable with variable
+// eastl::fixed_function size types.
+//
+template<class FixedFunctionT>
+int TestFixedFunctionCaptureless()
+{
+ int nErrorCount = 0;
+
+ FixedFunctionT fn;
+
+ EATEST_VERIFY(!fn);
+
+ fn = [](int in) { return in; };
+
+ EATEST_VERIFY(!!fn);
+
+ EATEST_VERIFY(fn(42) == 42);
+
+ return nErrorCount;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// TestFixedFunctionBasic
+//
+int TestFixedFunctionBasic()
+{
+ using namespace eastl;
+
+ int nErrorCount = 0;
+
+ {
+ struct Functor { void operator()() { return; } };
+ fixed_function<24, void(void)> fn;
+ fixed_function<24, void(void)> fn2 = nullptr;
+ EATEST_VERIFY(!fn);
+ EATEST_VERIFY(!fn2);
+ EATEST_VERIFY(fn == nullptr);
+ EATEST_VERIFY(fn2 == nullptr);
+ EATEST_VERIFY(nullptr == fn);
+ EATEST_VERIFY(nullptr == fn2);
+ fn = Functor();
+ fn2 = Functor();
+ EATEST_VERIFY(!!fn);
+ EATEST_VERIFY(!!fn2);
+ EATEST_VERIFY(fn != nullptr);
+ EATEST_VERIFY(fn2 != nullptr);
+ EATEST_VERIFY(nullptr != fn);
+ EATEST_VERIFY(nullptr != fn2);
+ fn = nullptr;
+ fn2 = fn;
+ EATEST_VERIFY(!fn);
+ EATEST_VERIFY(!fn2);
+ EATEST_VERIFY(fn == nullptr);
+ EATEST_VERIFY(fn2 == nullptr);
+ EATEST_VERIFY(nullptr == fn);
+ EATEST_VERIFY(nullptr == fn2);
+ }
+
+ {
+ using eastl::swap;
+ struct Functor { int operator()() { return 5; } };
+ fixed_function<24, int(void)> fn = Functor();
+ fixed_function<24, int(void)> fn2;
+ EATEST_VERIFY(fn() == 5);
+ EATEST_VERIFY(!fn2);
+ fn.swap(fn2);
+ EATEST_VERIFY(!fn);
+ EATEST_VERIFY(fn2() == 5);
+ swap(fn, fn2);
+ EATEST_VERIFY(fn() == 5);
+ EATEST_VERIFY(!fn2);
+ }
+
+ {
+ struct Functor { int operator()() { return 42; } };
+ fixed_function<0, int(void)> fn = Functor();
+ EATEST_VERIFY(fn() == 42);
+ }
+
+ {
+ struct Functor { int operator()(int in) { return in; } };
+ fixed_function<0, int(int)> fn = Functor();
+ EATEST_VERIFY(fn(24) == 24);
+ }
+
+ {
+ eastl::fixed_function<0, void(void)> fn;
+
+ EATEST_VERIFY(!fn);
+ fn = [] {};
+ EATEST_VERIFY(!!fn);
+ }
+
+ {
+ eastl::fixed_function<0, int(int)> fn = [](int param) { return param; };
+ EATEST_VERIFY(fn(42) == 42);
+ }
+
+ {
+ eastl::fixed_function<0, int(int)> fn = ReturnVal;
+ EATEST_VERIFY(fn(42) == 42);
+ }
+
+ {
+ eastl::fixed_function<0, int()> fn0 = ReturnZero;
+ eastl::fixed_function<0, int()> fn1 = ReturnOne;
+
+ EATEST_VERIFY(fn0() == 0 && fn1() == 1);
+ swap(fn0, fn1);
+ EATEST_VERIFY(fn0() == 1 && fn1() == 0);
+ }
+
+ {
+ eastl::fixed_function<0, int()> fn0 = ReturnZero;
+ eastl::fixed_function<0, int()> fn1 = ReturnOne;
+
+ EATEST_VERIFY(fn0() == 0 && fn1() == 1);
+ fn0 = fn1;
+ EATEST_VERIFY(fn0() == 1 && fn1() == 1);
+ }
+
+ {
+ eastl::fixed_function<0, int()> fn0 = ReturnZero;
+ eastl::fixed_function<0, int()> fn1 = ReturnOne;
+
+ EATEST_VERIFY(fn0() == 0 && fn1() == 1);
+ fn0 = eastl::move(fn1);
+ EATEST_VERIFY(fn0() == 1 && fn1 == nullptr);
+ }
+
+ {
+ eastl::fixed_function<0, int(int)> f1(nullptr);
+ EATEST_VERIFY(!f1);
+
+ eastl::fixed_function<0, int(int)> f2 = nullptr;
+ EATEST_VERIFY(!f2);
+ }
+
+ {
+ // test using a large lambda capture
+ uint64_t a = 1, b = 2, c = 3, d = 4, e = 5, f = 6;
+ auto large_add = [=] { return a + b + c + d + e + f; };
+
+ {
+ eastl::fixed_function<48, uint64_t(void)> fn = large_add;
+ auto result = fn();
+ EATEST_VERIFY(result == 21);
+ }
+
+ {
+ eastl::fixed_function<sizeof(large_add), uint64_t(void)> fn = large_add;
+ auto result = fn();
+ EATEST_VERIFY(result == 21);
+ }
+ }
+
+ {
+ using ff_0 = eastl::fixed_function<0, int(int)>;
+ using ff_1 = eastl::fixed_function<1, int(int)>;
+ using ff_4 = eastl::fixed_function<4, int(int)>;
+ using ff_8 = eastl::fixed_function<8, int(int)>;
+ using ff_64 = eastl::fixed_function<64, int(int)>;
+ using ff_128 = eastl::fixed_function<128, int(int)>;
+ using ff_4096 = eastl::fixed_function<4096, int(int)>;
+
+ static_assert(sizeof(ff_0) >= sizeof(void*), "error");
+ static_assert(sizeof(ff_1) >= sizeof(void*), "error");
+ static_assert(sizeof(ff_4) >= sizeof(void*), "error");
+ static_assert(sizeof(ff_8) >= 8, "error");
+ static_assert(sizeof(ff_64) >= 64, "error");
+ static_assert(sizeof(ff_128) >= 128, "error");
+ static_assert(sizeof(ff_4096) >= 4096, "error");
+
+ nErrorCount += TestFixedFunctionCaptureless<ff_0>();
+ nErrorCount += TestFixedFunctionCaptureless<ff_1>();
+ nErrorCount += TestFixedFunctionCaptureless<ff_4>();
+ nErrorCount += TestFixedFunctionCaptureless<ff_8>();
+ nErrorCount += TestFixedFunctionCaptureless<ff_64>();
+ nErrorCount += TestFixedFunctionCaptureless<ff_128>();
+ nErrorCount += TestFixedFunctionCaptureless<ff_4096>();
+ }
+
+ // Verify conversions to fixed_function<N> for sizes greater or equal to the source size.
+ {
+ uint32_t v0 = 130480, v1 = 936780302;
+ const uint32_t result = v0 + v1;
+
+ eastl::fixed_function<8, uint32_t(void)> ff8 = [v0, v1]
+ { return v0 + v1; };
+
+ {
+ eastl::fixed_function<16, uint32_t(void)> ff16(ff8);
+ VERIFY(result == ff16());
+ }
+
+ {
+ eastl::fixed_function<16, uint32_t(void)> ff16 = ff8;
+ VERIFY(result == ff16());
+ }
+
+ {
+ eastl::fixed_function<16, uint32_t(void)> ff16;
+ ff16 = ff8;
+ VERIFY(result == ff16());
+ }
+
+ {
+ auto ff8Copy = ff8;
+ eastl::fixed_function<16, uint32_t(void)> ff16(eastl::move(ff8Copy));
+ VERIFY(result == ff16());
+ }
+
+ {
+ auto ff8Copy = ff8;
+ eastl::fixed_function<16, uint32_t(void)> ff16 = eastl::move(ff8Copy);
+ VERIFY(result == ff16());
+ }
+
+ {
+ auto ff8Copy = ff8;
+ eastl::fixed_function<16, uint32_t(void)> ff16;
+ ff16 = eastl::move(ff8Copy);
+ VERIFY(result == ff16());
+ }
+ }
+
+ return nErrorCount;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// TestFunctional
+//
+int TestFixedFunction()
+{
+ using namespace eastl;
+
+ int nErrorCount = 0;
+
+ nErrorCount += TestFixedFunctionBasic();
+ nErrorCount += TestFixedFunctionDtor();
+ nErrorCount += TestFixedFunctionExistingClosure();
+ nErrorCount += TestFixedFunctionReferenceWrapper();
+ nErrorCount += TestFixedFunctionFunctionPointer();
+ nErrorCount += TestFixedFunctionPointerToMemberFunction();
+ nErrorCount += TestFixedFunctionPointerToMemberData();
+ nErrorCount += TestFixedFunctionStdBind();
+
+ return nErrorCount;
+}