aboutsummaryrefslogtreecommitdiff
path: root/test/source/TestFunctional.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'test/source/TestFunctional.cpp')
-rw-r--r--test/source/TestFunctional.cpp1326
1 files changed, 1326 insertions, 0 deletions
diff --git a/test/source/TestFunctional.cpp b/test/source/TestFunctional.cpp
new file mode 100644
index 0000000..88f4c2c
--- /dev/null
+++ b/test/source/TestFunctional.cpp
@@ -0,0 +1,1326 @@
+/////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+/////////////////////////////////////////////////////////////////////////////
+
+
+#include <EABase/eabase.h>
+#include <EAAssert/eaassert.h>
+#include "EASTLTest.h"
+#include <EASTL/memory.h>
+#include <EASTL/functional.h>
+#include <EASTL/hash_set.h>
+#include <EASTL/set.h>
+#include <EASTL/list.h>
+#include <EAStdC/EAString.h>
+
+EA_DISABLE_ALL_VC_WARNINGS()
+#include <functional>
+EA_RESTORE_ALL_VC_WARNINGS()
+
+namespace
+{
+
+ // Used for eastl::function tests
+ static int TestIntRet(int* p)
+ {
+ int ret = *p;
+ *p += 1;
+ return ret;
+ }
+
+ // Used for str_less tests below.
+ template <typename T>
+ struct Results
+ {
+ const T* p1;
+ const T* p2;
+ bool expectedResult; // The expected result of the expression (p1 < p2)
+ };
+
+
+ // Used for const_mem_fun_t below.
+ struct X
+ {
+ X() { }
+ void DoNothing() const { }
+ };
+
+ template <typename T>
+ void foo(typename T::argument_type arg)
+ {
+ typename T::result_type (T::*pFunction)(typename T::argument_type) const = &T::operator();
+ T t(&X::DoNothing);
+ (t.*pFunction)(arg);
+ }
+
+
+ // Used for equal_to_2 tests below.
+ struct N1{
+ N1(int x) : mX(x) { }
+ int mX;
+ };
+
+ struct N2{
+ N2(int x) : mX(x) { }
+ int mX;
+ };
+
+ bool operator==(const N1& n1, const N1& n1a){ return (n1.mX == n1a.mX); }
+ bool operator==(const N1& n1, const N2& n2) { return (n1.mX == n2.mX); }
+ bool operator==(const N2& n2, const N1& n1) { return (n2.mX == n1.mX); }
+
+ bool operator!=(const N1& n1, const N1& n1a){ return (n1.mX != n1a.mX); }
+ bool operator!=(const N1& n1, const N2& n2) { return (n1.mX != n2.mX); }
+ bool operator!=(const N2& n2, const N1& n1) { return (n2.mX != n1.mX); }
+
+ bool operator< (const N1& n1, const N1& n1a){ return (n1.mX < n1a.mX); }
+ bool operator< (const N1& n1, const N2& n2) { return (n1.mX < n2.mX); }
+ bool operator< (const N2& n2, const N1& n1) { return (n2.mX < n1.mX); }
+
+
+ // Used for mem_fun tests below.
+ struct TestClass
+ {
+ mutable int mX;
+
+ TestClass() : mX(37) { }
+
+ void Increment()
+ {
+ mX++;
+ }
+
+ void IncrementConst() const
+ {
+ mX++;
+ }
+
+ int MultiplyBy(int x)
+ {
+ return mX * x;
+ }
+
+ int MultiplyByConst(int x) const
+ {
+ return mX * x;
+ }
+ };
+}
+
+
+// Template instantations.
+// These tell the compiler to compile all the functions for the given class.
+typedef eastl::basic_string<char8_t, MallocAllocator> String8MA;
+typedef eastl::basic_string<char16_t, MallocAllocator> String16MA;
+
+template struct eastl::string_hash<String8MA>;
+template struct eastl::string_hash<String16MA>;
+
+template class eastl::hash_set<String8MA, eastl::string_hash<String8MA> >;
+template class eastl::hash_set<String16MA, eastl::string_hash<String16MA> >;
+
+
+// Helper function for testing our default hash implementations for pod types which
+// simply returns the static_cast<size_t> of the val passed in
+template<typename T>
+int TestHashHelper(T val)
+{
+ int nErrorCount = 0;
+
+ EATEST_VERIFY(eastl::hash<T>()(val) == static_cast<size_t>(val));
+
+ return nErrorCount;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// TestFunctional
+//
+int TestFunctional()
+{
+ using namespace eastl;
+
+ int nErrorCount = 0;
+
+ {
+ // str_equal_to
+ char p0[] = "";
+ char p1[] = "hello";
+ char p2[] = "world";
+ char p3[] = "helllllo";
+ char p4[] = "hello"; // Intentionally the same value as p1.
+
+ // str_equal_to
+ typedef hash_set<const char*, hash<const char*>, str_equal_to<const char*> > StringHashSet;
+ StringHashSet shs;
+
+ shs.insert(p1);
+ shs.insert(p2);
+ shs.insert(p3);
+
+ StringHashSet::iterator it = shs.find(p0);
+ EATEST_VERIFY(it == shs.end());
+
+ it = shs.find(p1);
+ EATEST_VERIFY(it != shs.end());
+
+ it = shs.find(p2);
+ EATEST_VERIFY(it != shs.end());
+
+ it = shs.find(p4);
+ EATEST_VERIFY(it != shs.end());
+ }
+
+ {
+ // str_less<const char8_t*>
+ Results<char> results8[] =
+ {
+ { "", "", false },
+ { "", "a", true },
+ { "a", "", false },
+ { "a", "a", false },
+ { "a", "b", true },
+ { "____a", "____a", false },
+ { "____a", "____b", true },
+ { "____b", "____a", false },
+ { "_\xff", "_a", false }, // Test high values, which exercises the signed/unsiged comparison behavior.
+ { "_a", "_\xff", true }
+ };
+
+ str_less<const char*> sl8;
+ for(size_t i = 0; i < EAArrayCount(results8); i++)
+ {
+ // Verify that our test is in line with the strcmp function.
+ bool bResult = (EA::StdC::Strcmp(results8[i].p1, results8[i].p2) < 0);
+ EATEST_VERIFY_F(bResult == results8[i].expectedResult, "Strcmp failure, test %zu. Expected \"%s\" to be %sless than \"%s\"", i, results8[i].p1, results8[i].expectedResult ? "" : "not ", results8[i].p2);
+
+ // Verify that str_less achieves the expected results.
+ bResult = sl8(results8[i].p1, results8[i].p2);
+ EATEST_VERIFY_F(bResult == results8[i].expectedResult, "str_less test failure, test %zu. Expected \"%s\" to be %sless than \"%s\"", i, results8[i].p1, results8[i].expectedResult ? "" : "not ", results8[i].p2);
+ }
+
+ // str_less<const wchar_t*>
+ Results<wchar_t> resultsW[] =
+ {
+ { L"", L"", false },
+ { L"", L"a", true },
+ { L"a", L"", false },
+ { L"a", L"a", false },
+ { L"a", L"b", true },
+ { L"____a", L"____a", false },
+ { L"____a", L"____b", true },
+ { L"____b", L"____a", false },
+ { L"_\xffff", L"_a", false }, // Test high values, which exercises the signed/unsiged comparison behavior.
+ { L"_a", L"_\xffff", true }
+ };
+
+ str_less<const wchar_t*> slW;
+ for(size_t i = 0; i < EAArrayCount(resultsW); i++)
+ {
+ // Verify that our test is in line with the strcmp function.
+ bool bResult = (EA::StdC::Strcmp(resultsW[i].p1, resultsW[i].p2) < 0);
+ EATEST_VERIFY_F(bResult == resultsW[i].expectedResult, "Strcmp failure, test %zu. Expected \"%s\" to be %sless than \"%s\"", i, results8[i].p1, results8[i].expectedResult ? "" : "not ", results8[i].p2);
+
+ // Verify that str_less achieves the expected results.
+ bResult = slW(resultsW[i].p1, resultsW[i].p2);
+ EATEST_VERIFY_F(bResult == resultsW[i].expectedResult, "str_less test failure, test %zu. Expected \"%ls\" to be %sless than \"%ls\"", i, resultsW[i].p1, resultsW[i].expectedResult ? "" : "not ", resultsW[i].p2);
+ }
+ }
+
+ {
+ // str_less
+ char p0[] = "";
+ char p1[] = "hello";
+ char p2[] = "world";
+ char p3[] = "helllllo";
+ char p4[] = "hello"; // Intentionally the same value as p1.
+
+ typedef set<const char*, str_less<const char*> > StringSet;
+ StringSet ss;
+
+ ss.insert(p1);
+ ss.insert(p2);
+ ss.insert(p3);
+
+ StringSet::iterator it = ss.find(p0);
+ EATEST_VERIFY(it == ss.end());
+
+ it = ss.find(p1);
+ EATEST_VERIFY(it != ss.end());
+
+ it = ss.find(p2);
+ EATEST_VERIFY(it != ss.end());
+
+ it = ss.find(p4);
+ EATEST_VERIFY(it != ss.end());
+ }
+
+ {
+ // equal_to_2
+ N1 n11(1);
+ N1 n13(3);
+ N2 n21(1);
+ N2 n22(2);
+ //const N1 cn11(1);
+ //const N1 cn13(3);
+
+ equal_to_2<N1, N2> e;
+ EATEST_VERIFY(e(n11, n21));
+ EATEST_VERIFY(e(n21, n11));
+
+ equal_to_2<N1, N1> es;
+ EATEST_VERIFY(es(n11, n11));
+
+ //equal_to_2<const N1, N1> ec; // To do: Make this case work.
+ //EATEST_VERIFY(e(cn11, n11));
+
+ // not_equal_to_2
+ not_equal_to_2<N1, N2> n;
+ EATEST_VERIFY(n(n11, n22));
+ EATEST_VERIFY(n(n22, n11));
+
+ not_equal_to_2<N1, N1> ns;
+ EATEST_VERIFY(ns(n11, n13));
+
+ // less_2
+ less_2<N1, N2> le;
+ EATEST_VERIFY(le(n11, n22));
+ EATEST_VERIFY(le(n22, n13));
+
+ less_2<N1, N1> les;
+ EATEST_VERIFY(les(n11, n13));
+ }
+
+
+ {
+ // Test defect report entry #297.
+ const X x;
+ foo< const_mem_fun_t<void, X> >(&x);
+ }
+
+
+ {
+ // mem_fun (no argument version)
+ TestClass tc0, tc1, tc2;
+ TestClass* tcArray[3] = { &tc0, &tc1, &tc2 };
+
+ for_each(tcArray, tcArray + 3, mem_fun(&TestClass::Increment));
+ EATEST_VERIFY((tc0.mX == 38) && (tc1.mX == 38) && (tc2.mX == 38));
+
+ for_each(tcArray, tcArray + 3, mem_fun(&TestClass::IncrementConst));
+ EATEST_VERIFY((tc0.mX == 39) && (tc1.mX == 39) && (tc2.mX == 39));
+ }
+
+
+ {
+ // mem_fun (one argument version)
+ TestClass tc0, tc1, tc2;
+ TestClass* tcArray[3] = { &tc0, &tc1, &tc2 };
+ int intArray1[3] = { -1, 0, 2 };
+ int intArray2[3] = { -9, -9, -9 };
+
+ transform(tcArray, tcArray + 3, intArray1, intArray2, mem_fun(&TestClass::MultiplyBy));
+ EATEST_VERIFY((intArray2[0] == -37) && (intArray2[1] == 0) && (intArray2[2] == 74));
+
+ intArray2[0] = intArray2[1] = intArray2[2] = -9;
+ transform(tcArray, tcArray + 3, intArray1, intArray2, mem_fun(&TestClass::MultiplyByConst));
+ EATEST_VERIFY((intArray2[0] == -37) && (intArray2[1] == 0) && (intArray2[2] == 74));
+ }
+
+
+ {
+ // mem_fun_ref (no argument version)
+ TestClass tcArray[3];
+
+ for_each(tcArray, tcArray + 3, mem_fun_ref(&TestClass::Increment));
+ EATEST_VERIFY((tcArray[0].mX == 38) && (tcArray[1].mX == 38) && (tcArray[2].mX == 38));
+
+ for_each(tcArray, tcArray + 3, mem_fun_ref(&TestClass::IncrementConst));
+ EATEST_VERIFY((tcArray[0].mX == 39) && (tcArray[1].mX == 39) && (tcArray[2].mX == 39));
+ }
+
+
+ {
+ // mem_fun_ref (one argument version)
+ TestClass tcArray[3];
+ int intArray1[3] = { -1, 0, 2 };
+ int intArray2[3] = { -9, -9, -9 };
+
+ transform(tcArray, tcArray + 3, intArray1, intArray2, mem_fun_ref(&TestClass::MultiplyBy));
+ EATEST_VERIFY((intArray2[0] == -37) && (intArray2[1] == 0) && (intArray2[2] == 74));
+
+ intArray2[0] = intArray2[1] = intArray2[2] = -9;
+ transform(tcArray, tcArray + 3, intArray1, intArray2, mem_fun_ref(&TestClass::MultiplyByConst));
+ EATEST_VERIFY((intArray2[0] == -37) && (intArray2[1] == 0) && (intArray2[2] == 74));
+ }
+
+
+ {
+ // Template instantations.
+ // These tell the compiler to compile all the functions for the given class.
+ eastl::hash_set<String8MA, eastl::string_hash<String8MA> > hs8;
+ eastl::hash_set<String16MA, eastl::string_hash<String16MA> > hs16;
+
+ EATEST_VERIFY(hs8.empty());
+ EATEST_VERIFY(hs16.empty());
+ }
+
+ {
+ // unary_compose
+ /*
+ eastl::vector<double> angles;
+ eastl::vector<double> sines;
+
+ eastl::transform(angles.begin(), angles.end(), sines.begin(),
+ eastl::compose1(eastl::negate<double>(),
+ eastl::compose1(eastl::ptr_fun(sin),
+ eastl::bind2nd(eastl::multiplies<double>(), 3.14159 / 180.0))));
+ */
+
+ // binary_compose
+ list<int> L;
+
+ eastl::list<int>::iterator in_range =
+ eastl::find_if(L.begin(), L.end(),
+ eastl::compose2(eastl::logical_and<bool>(),
+ eastl::bind2nd(eastl::greater_equal<int>(), 1),
+ eastl::bind2nd(eastl::less_equal<int>(), 10)));
+ EATEST_VERIFY(in_range == L.end());
+ }
+
+ {
+ nErrorCount += TestHashHelper<int>(4330);
+ nErrorCount += TestHashHelper<bool>(true);
+ nErrorCount += TestHashHelper<char>('E');
+ nErrorCount += TestHashHelper<signed char>('E');
+ nErrorCount += TestHashHelper<unsigned char>('E');
+ nErrorCount += TestHashHelper<char8_t>('E');
+ nErrorCount += TestHashHelper<char16_t>(0xEAEA);
+ nErrorCount += TestHashHelper<char32_t>(0x00EA4330);
+ #if !defined(EA_WCHAR_T_NON_NATIVE)
+ nErrorCount += TestHashHelper<wchar_t>(L'E');
+ #endif
+ nErrorCount += TestHashHelper<signed short>(4330);
+ nErrorCount += TestHashHelper<unsigned short>(4330u);
+ nErrorCount += TestHashHelper<signed int>(4330);
+ nErrorCount += TestHashHelper<unsigned int>(4330u);
+ nErrorCount += TestHashHelper<signed long>(4330l);
+ nErrorCount += TestHashHelper<unsigned long>(4330ul);
+ nErrorCount += TestHashHelper<signed long long>(4330ll);
+ nErrorCount += TestHashHelper<unsigned long long>(4330ll);
+ nErrorCount += TestHashHelper<float>(4330.099999f);
+ nErrorCount += TestHashHelper<double>(4330.055);
+ nErrorCount += TestHashHelper<long double>(4330.0654l);
+
+ {
+ enum hash_enum_test { e1, e2, e3 };
+ nErrorCount += TestHashHelper<hash_enum_test>(e1);
+ nErrorCount += TestHashHelper<hash_enum_test>(e2);
+ nErrorCount += TestHashHelper<hash_enum_test>(e3);
+ }
+ }
+
+
+#if defined(EA_COMPILER_CPP11_ENABLED) && EASTL_VARIADIC_TEMPLATES_ENABLED
+ // On platforms do not support variadic templates the eastl::invoke (eastl::mem_fn is built on eastl::invoke)
+ // implementation is extremely basic and does not hold up. A significant amount of code would have to be written
+ // and I don't believe the investment is justified at this point. If you require this functionality on older
+ // compilers please contact us.
+ //
+
+ // eastl::invoke
+ {
+ struct TestStruct
+ {
+ TestStruct(int inValue) : value(inValue) {}
+ void Add(int addAmount) { value += addAmount; }
+ int GetValue() { return value; }
+ int& GetValueReference() { return value; }
+ int value;
+ };
+
+ struct TestFunctor
+ {
+ void operator()() { called = true; }
+ bool called = false;
+ };
+
+ struct TestFunctorArguments
+ {
+ void operator()(int i) { value = i; }
+ int value = 0;
+ };
+
+ {
+ TestStruct a(42);
+ eastl::invoke(&TestStruct::Add, a, 10);
+ EATEST_VERIFY(a.value == 52);
+
+ static_assert(eastl::is_same<typename eastl::invoke_result<decltype(&TestStruct::Add), TestStruct, int>::type, void>::value, "incorrect type for invoke_result");
+ static_assert(eastl::is_invocable<decltype(&TestStruct::Add), TestStruct, int>::value, "incorrect value for is_invocable");
+ }
+ {
+ TestStruct a(42);
+ eastl::invoke(&TestStruct::Add, &a, 10);
+ EATEST_VERIFY(a.value == 52);
+
+ static_assert(eastl::is_same<typename eastl::invoke_result<decltype(&TestStruct::Add), TestStruct *, int>::type, void>::value, "incorrect type for invoke_result");
+ static_assert(eastl::is_invocable<decltype(&TestStruct::Add), TestStruct *, int>::value, "incorrect value for is_invocable");
+ }
+ {
+ TestStruct a(42);
+ eastl::reference_wrapper<TestStruct> r(a);
+ eastl::invoke(&TestStruct::Add, r, 10);
+ EATEST_VERIFY(a.value == 52);
+
+ static_assert(eastl::is_same<typename eastl::invoke_result<decltype(&TestStruct::Add), eastl::reference_wrapper<TestStruct>, int>::type, void>::value, "incorrect type for invoke_result");
+ static_assert(eastl::is_invocable<decltype(&TestStruct::Add), eastl::reference_wrapper<TestStruct>, int>::value, "incorrect value for is_invocable");
+ }
+ {
+ TestStruct a(42);
+ eastl::invoke(&TestStruct::GetValueReference, a) = 43;
+ EATEST_VERIFY(a.value == 43);
+
+ static_assert(eastl::is_same<typename eastl::invoke_result<decltype(&TestStruct::GetValueReference), TestStruct &>::type, int &>::value, "incorrect type for invoke_result");
+ static_assert(eastl::is_invocable<decltype(&TestStruct::GetValueReference), TestStruct &>::value, "incorrect value for is_invocable");
+ }
+ {
+ TestStruct a(42);
+ EATEST_VERIFY(eastl::invoke(&TestStruct::value, a) == 42);
+
+ static_assert(eastl::is_same<typename eastl::invoke_result<decltype(&TestStruct::value), TestStruct &>::type, int &>::value, "incorrect type for invoke_result");
+ static_assert(eastl::is_invocable<decltype(&TestStruct::value), TestStruct &>::value, "incorrect value for is_invocable");
+ }
+ {
+ TestStruct a(42);
+ eastl::invoke(&TestStruct::value, a) = 43;
+ EATEST_VERIFY(a.value == 43);
+
+ static_assert(eastl::is_same<typename eastl::invoke_result<decltype(&TestStruct::value), TestStruct &>::type, int &>::value, "incorrect type for invoke_result");
+ static_assert(eastl::is_invocable<decltype(&TestStruct::value), TestStruct &>::value, "incorrect value for is_invocable");
+ }
+ {
+ TestStruct a(42);
+ eastl::invoke(&TestStruct::value, &a) = 43;
+ EATEST_VERIFY(a.value == 43);
+
+ static_assert(eastl::is_same<typename eastl::invoke_result<decltype(&TestStruct::value), TestStruct *>::type, int &>::value, "incorrect type for invoke_result");
+ static_assert(eastl::is_invocable<decltype(&TestStruct::value), TestStruct *>::value, "incorrect value for is_invocable");
+ }
+ {
+ TestStruct a(42);
+ eastl::reference_wrapper<TestStruct> r(a);
+ eastl::invoke(&TestStruct::value, r) = 43;
+ EATEST_VERIFY(a.value == 43);
+
+ static_assert(eastl::is_same<typename eastl::invoke_result<decltype(&TestStruct::value), eastl::reference_wrapper<TestStruct>>::type, int &>::value, "incorrect type for invoke_result");
+ static_assert(eastl::is_invocable<decltype(&TestStruct::GetValue), eastl::reference_wrapper<TestStruct>>::value, "incorrect value for is_invocable");
+ }
+
+ #ifndef EA_COMPILER_GNUC
+ {
+ TestStruct a(42);
+ EATEST_VERIFY(eastl::invoke(&TestStruct::GetValue, a) == 42);
+
+ static_assert(
+ eastl::is_same<typename eastl::invoke_result<decltype(&TestStruct::GetValue), TestStruct*>::type, int>::value,
+ "incorrect type for invoke_result");
+
+ static_assert(eastl::is_invocable<decltype(&TestStruct::GetValue), TestStruct*>::value, "incorrect value for is_invocable");
+ }
+ #endif
+ {
+ TestFunctor f;
+ eastl::invoke(f);
+ EATEST_VERIFY(f.called);
+
+ static_assert(eastl::is_same<typename eastl::invoke_result<decltype(f)>::type, void>::value, "incorrect type for invoke_result");
+ static_assert(eastl::is_invocable<decltype(f)>::value, "incorrect value for is_invocable");
+ }
+ {
+ TestFunctorArguments f;
+ eastl::invoke(f, 42);
+ EATEST_VERIFY(f.value == 42);
+
+ static_assert(eastl::is_same<typename eastl::invoke_result<decltype(f), int>::type, void>::value, "incorrect type for invoke_result");
+ static_assert(eastl::is_invocable<decltype(f), int>::value, "incorrect value for is_invocable");
+ }
+ {
+ static bool called = false;
+ auto f = [] {called = true;};
+ eastl::invoke(f);
+ EATEST_VERIFY(called);
+
+ static_assert(eastl::is_same<typename eastl::invoke_result<decltype(f)>::type, void>::value, "incorrect type for invoke_result");
+ static_assert(eastl::is_invocable<decltype(f)>::value, "incorrect value for is_invocable");
+ }
+ {
+ static int value = 0;
+ auto f = [](int i) {value = i;};
+ eastl::invoke(f, 42);
+ EATEST_VERIFY(value == 42);
+
+ static_assert(eastl::is_same<typename eastl::invoke_result<decltype(f), int>::type, void>::value, "incorrect type for invoke_result");
+ static_assert(eastl::is_invocable<decltype(f), int>::value, "incorrect value for is_invocable");
+ }
+ {
+ struct A {};
+ struct B : public A {};
+
+ struct TestStruct
+ {
+ A a() { return A(); };
+ B b() { return B(); };
+ };
+
+ static_assert(!eastl::is_invocable_r<B, decltype(&TestStruct::a), TestStruct>::value, "incorrect value for is_invocable_r");
+ static_assert(eastl::is_invocable_r<A, decltype(&TestStruct::b), TestStruct>::value, "incorrect value for is_invocable_r");
+ static_assert(eastl::is_invocable_r<B, decltype(&TestStruct::b), TestStruct>::value, "incorrect value for is_invocable_r");
+ }
+ }
+
+ // eastl::mem_fn
+ {
+ struct AddingStruct
+ {
+ AddingStruct(int inValue) : value(inValue) {}
+ void Add(int addAmount) { value += addAmount; }
+ void Add2(int add1, int add2) { value += (add1 + add2); }
+ int value;
+ };
+
+ struct OverloadedStruct
+ {
+ OverloadedStruct(int inValue) : value(inValue) {}
+ int &Value() { return value; }
+ const int &Value() const { return value; }
+ int value;
+ };
+
+ {
+ AddingStruct a(42);
+ eastl::mem_fn(&AddingStruct::Add)(a, 6);
+ EATEST_VERIFY(a.value == 48);
+ }
+ {
+ AddingStruct a(42);
+ eastl::mem_fn(&AddingStruct::Add2)(a, 3, 3);
+ EATEST_VERIFY(a.value == 48);
+ }
+ {
+ AddingStruct a(42);
+ auto fStructAdd = eastl::mem_fn(&AddingStruct::Add);
+ fStructAdd(a,6);
+ EATEST_VERIFY(a.value == 48);
+ }
+ {
+ OverloadedStruct a(42);
+ EATEST_VERIFY(eastl::mem_fn<int &()>(&OverloadedStruct::Value)(a) == 42);
+ EATEST_VERIFY(eastl::mem_fn<const int &() const>(&OverloadedStruct::Value)(a) == 42);
+ }
+ }
+#endif
+
+ // eastl::function
+ {
+ {
+ {
+ struct Functor { int operator()() { return 42; } };
+ eastl::function<int(void)> fn = Functor();
+ EATEST_VERIFY(fn() == 42);
+ }
+
+ {
+ struct Functor { int operator()(int in) { return in; } };
+ eastl::function<int(int)> fn = Functor();
+ EATEST_VERIFY(fn(24) == 24);
+ }
+ }
+
+ {
+ int val = 0;
+ auto lambda = [&val] { ++val; };
+ {
+ eastl::function<void(void)> ff = std::bind(lambda);
+ ff();
+ VERIFY(val == 1);
+ }
+ {
+ eastl::function<void(void)> ff = nullptr;
+ ff = std::bind(lambda);
+ ff();
+ VERIFY(val == 2);
+ }
+ }
+
+ {
+ int val = 0;
+ {
+ eastl::function<int(int*)> ff = &TestIntRet;
+ int ret = ff(&val);
+ EATEST_VERIFY(ret == 0);
+ EATEST_VERIFY(val == 1);
+ }
+ {
+ eastl::function<int(int*)> ff;
+ ff = &TestIntRet;
+ int ret = ff(&val);
+ EATEST_VERIFY(ret == 1);
+ EATEST_VERIFY(val == 2);
+ }
+ }
+
+ {
+ struct Test { int x = 1; };
+ Test t;
+ const Test ct;
+
+ {
+ eastl::function<int(const Test&)> ff = &Test::x;
+ int ret = ff(t);
+ EATEST_VERIFY(ret == 1);
+ }
+ {
+ eastl::function<int(const Test&)> ff = &Test::x;
+ int ret = ff(ct);
+ EATEST_VERIFY(ret == 1);
+ }
+ {
+ eastl::function<int(const Test&)> ff;
+ ff = &Test::x;
+ int ret = ff(t);
+ EATEST_VERIFY(ret == 1);
+ }
+ {
+ eastl::function<int(const Test&)> ff;
+ ff = &Test::x;
+ int ret = ff(ct);
+ EATEST_VERIFY(ret == 1);
+ }
+ }
+
+ {
+ struct TestVoidRet
+ {
+ void IncX() const
+ {
+ ++x;
+ }
+
+ void IncX()
+ {
+ ++x;
+ }
+
+ mutable int x = 0;
+ };
+
+ TestVoidRet voidRet;
+ const TestVoidRet cvoidRet;
+
+ {
+ eastl::function<void(const TestVoidRet&)> ff = static_cast<void(TestVoidRet::*)() const>(&TestVoidRet::IncX);
+ ff(cvoidRet);
+ VERIFY(cvoidRet.x == 1);
+ }
+ {
+ eastl::function<void(const TestVoidRet&)> ff = static_cast<void(TestVoidRet::*)() const>(&TestVoidRet::IncX);
+ ff(voidRet);
+ VERIFY(voidRet.x == 1);
+ }
+ {
+ eastl::function<void(TestVoidRet&)> ff = static_cast<void(TestVoidRet::*)()>(&TestVoidRet::IncX);
+ ff(voidRet);
+ VERIFY(voidRet.x == 2);
+ }
+ }
+
+ {
+ int val = 0;
+ struct Functor { void operator()(int* p) { *p += 1; } };
+ Functor functor;
+ {
+ eastl::function<void(int*)> ff = eastl::reference_wrapper<Functor>(functor);
+ ff(&val);
+ EATEST_VERIFY(val == 1);
+ }
+
+ {
+ eastl::function<void(int*)> ff;
+ ff = eastl::reference_wrapper<Functor>(functor);
+ ff(&val);
+ EATEST_VERIFY(val == 2);
+ }
+ }
+
+ {
+ {
+ auto lambda = []{};
+ EA_UNUSED(lambda);
+ static_assert(internal::is_functor_inplace_allocatable<decltype(lambda), EASTL_FUNCTION_DEFAULT_CAPTURE_SSO_SIZE>::value == true, "lambda equivalent to function pointer does not fit in eastl::function local memory.");
+ }
+
+ {
+ eastl::function<void(void)> fn;
+
+ EATEST_VERIFY(!fn);
+ fn = [] {};
+ EATEST_VERIFY(!!fn);
+ }
+
+ {
+ eastl::function<int(int)> fn = [](int param) { return param; };
+ EATEST_VERIFY(fn(42) == 42);
+ }
+
+ {
+ eastl::function<int(int)> fn = ReturnVal;
+ EATEST_VERIFY(fn(42) == 42);
+ }
+
+ {
+ eastl::function<int()> fn0 = ReturnZero;
+ eastl::function<int()> fn1 = ReturnOne;
+
+ EATEST_VERIFY(fn0() == 0 && fn1() == 1);
+ swap(fn0, fn1);
+ EATEST_VERIFY(fn0() == 1 && fn1() == 0);
+ }
+
+ {
+ eastl::function<int()> fn0 = ReturnZero;
+ eastl::function<int()> fn1 = ReturnOne;
+
+ EATEST_VERIFY(fn0() == 0 && fn1() == 1);
+ fn0 = fn1;
+ EATEST_VERIFY(fn0() == 1 && fn1() == 1);
+ }
+
+ {
+ eastl::function<int()> fn0 = ReturnZero;
+ eastl::function<int()> fn1 = ReturnOne;
+
+ EATEST_VERIFY(fn0() == 0 && fn1() == 1);
+ fn0 = eastl::move(fn1);
+ EATEST_VERIFY(fn0() == 1 && fn1 == nullptr);
+ }
+
+ {
+ eastl::function<int(int)> f1(nullptr);
+ EATEST_VERIFY(!f1);
+
+ eastl::function<int(int)> f2 = nullptr;
+ EATEST_VERIFY(!f2);
+ }
+ }
+
+ {
+ // test the default allocator path by using a lambda capture too large to fit into the eastl::function local
+ // storage.
+ uint64_t a = 1, b = 2, c = 3, d = 4, e = 5, f = 6;
+ eastl::function<uint64_t(void)> fn = [=] { return a + b + c + d + e + f; };
+ auto result = fn();
+ EATEST_VERIFY(result == 21);
+ }
+
+ {
+ struct Functor { void operator()() { return; } };
+ eastl::function<void(void)> fn;
+ eastl::function<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; } };
+ eastl::function<int(void)> fn = Functor();
+ eastl::function<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);
+ }
+
+ {
+ uint64_t a = 1, b = 2, c = 3, d = 4, e = 5, f = 6;
+ eastl::function<uint64_t(void)> fn([=] { return a + b + c + d + e + f; });
+
+ auto result = fn();
+ EATEST_VERIFY(result == 21);
+ }
+
+ // user regression "self assigment" tests
+ {
+ eastl::function<int(void)> fn = [cache = 0] () mutable { return cache++; };
+
+ EATEST_VERIFY(fn() == 0);
+ EATEST_VERIFY(fn() == 1);
+ EATEST_VERIFY(fn() == 2);
+
+ EA_DISABLE_CLANG_WARNING(-Wunknown-pragmas)
+ EA_DISABLE_CLANG_WARNING(-Wunknown-warning-option)
+ EA_DISABLE_CLANG_WARNING(-Wself-assign-overloaded)
+ fn = fn;
+ EA_RESTORE_CLANG_WARNING()
+ EA_RESTORE_CLANG_WARNING()
+ EA_RESTORE_CLANG_WARNING()
+
+ EATEST_VERIFY(fn() == 3);
+ EATEST_VERIFY(fn() == 4);
+ EATEST_VERIFY(fn() == 5);
+
+ fn = eastl::move(fn);
+
+ EATEST_VERIFY(fn() == 6);
+ EATEST_VERIFY(fn() == 7);
+ EATEST_VERIFY(fn() == 8);
+ }
+
+ // user regression for memory leak when re-assigning an eastl::function which already holds a large closure.
+ {
+ static int sCtorCount = 0;
+ static int sDtorCount = 0;
+
+ {
+ struct local
+ {
+ local() { sCtorCount++; }
+ local(const local&) { sCtorCount++; }
+ local(local&&) { sCtorCount++; }
+ ~local() { sDtorCount++; }
+
+ void operator=(const local&) = delete; // suppress msvc warning
+ } l;
+
+ eastl::function<bool()> f;
+
+ f = [l]() { return false; };
+
+ // ensure closure resources are cleaned up when assigning to a non-null eastl::function.
+ f = [l]() { return true; };
+ }
+
+ EATEST_VERIFY(sCtorCount == sDtorCount);
+ }
+ }
+
+ // Checking _MSC_EXTENSIONS is required because the Microsoft calling convention classifiers are only available when
+ // compiler specific C/C++ language extensions are enabled.
+ #if defined(EA_PLATFORM_MICROSOFT) && defined(_MSC_EXTENSIONS)
+ {
+ // no arguments
+ typedef void(__stdcall * StdCallFunction)();
+ typedef void(__cdecl * CDeclFunction)();
+
+ // only varargs
+ typedef void(__stdcall * StdCallFunctionWithVarargs)(...);
+ typedef void(__cdecl * CDeclFunctionWithVarargs)(...);
+
+ // arguments and varargs
+ typedef void(__stdcall * StdCallFunctionWithVarargsAtEnd)(int, int, int, ...);
+ typedef void(__cdecl * CDeclFunctionWithVarargsAtEnd)(int, short, long, ...);
+
+ static_assert(!eastl::is_function<StdCallFunction>::value, "is_function failure");
+ static_assert(!eastl::is_function<CDeclFunction>::value, "is_function failure");
+ static_assert(eastl::is_function<typename eastl::remove_pointer<StdCallFunction>::type>::value, "is_function failure");
+ static_assert(eastl::is_function<typename eastl::remove_pointer<CDeclFunction>::type>::value, "is_function failure");
+ static_assert(eastl::is_function<typename eastl::remove_pointer<StdCallFunctionWithVarargs>::type>::value, "is_function failure");
+ static_assert(eastl::is_function<typename eastl::remove_pointer<CDeclFunctionWithVarargs>::type>::value, "is_function failure");
+ static_assert(eastl::is_function<typename eastl::remove_pointer<StdCallFunctionWithVarargsAtEnd>::type>::value, "is_function failure");
+ static_assert(eastl::is_function<typename eastl::remove_pointer<CDeclFunctionWithVarargsAtEnd>::type>::value, "is_function failure");
+ }
+ #endif
+
+ // Test Function Objects
+ #if defined(EA_COMPILER_CPP14_ENABLED)
+ {
+ // eastl::plus<void>
+ {
+ {
+ auto result = eastl::plus<>{}(40, 2);
+ EA_UNUSED(result);
+ EATEST_VERIFY(result == 42);
+ }
+
+ {
+ auto result = eastl::plus<>{}(40.0, 2.0);
+ EA_UNUSED(result);
+ EATEST_VERIFY(result == 42.0);
+ }
+
+ {
+ auto result = eastl::plus<>{}(eastl::string("4"), "2");
+ EA_UNUSED(result);
+ EATEST_VERIFY(result == "42");
+ }
+ }
+
+ // eastl::minus<void>
+ {
+ {
+ auto result = eastl::minus<>{}(6, 2);
+ EA_UNUSED(result);
+ EATEST_VERIFY(result == 4);
+ }
+
+ {
+ auto result = eastl::minus<>{}(6.0, 2.0);
+ EA_UNUSED(result);
+ EATEST_VERIFY(result == 4.0);
+ }
+ }
+
+ // eastl::multiplies
+ {
+ {
+ auto result = eastl::multiplies<>{}(6, 2);
+ EA_UNUSED(result);
+ EATEST_VERIFY(result == 12);
+ }
+
+ {
+ auto result = eastl::multiplies<>{}(6.0, 2.0);
+ EA_UNUSED(result);
+ EATEST_VERIFY(result == 12.0);
+ }
+ }
+
+
+ // eastl::divides
+ {
+ {
+ auto result = eastl::divides<>{}(6, 2);
+ EA_UNUSED(result);
+ EATEST_VERIFY(result == 3);
+ }
+
+ {
+ auto result = eastl::divides<>{}(6.0, 2.0);
+ EA_UNUSED(result);
+ EATEST_VERIFY(result == 3.0);
+ }
+ }
+
+ // eastl::modulus
+ {
+ {
+ auto result = eastl::modulus<>{}(6, 2);
+ EA_UNUSED(result);
+ EATEST_VERIFY(result == 0);
+ }
+
+ {
+ auto result = eastl::modulus<>{}(7, 2);
+ EA_UNUSED(result);
+ EATEST_VERIFY(result == 1);
+ }
+ }
+
+ // eastl::negate
+ {
+ {
+ auto result = eastl::negate<>{}(42);
+ EA_UNUSED(result);
+ EATEST_VERIFY(result == -42);
+ }
+
+ {
+ auto result = eastl::negate<>{}(42.0);
+ EA_UNUSED(result);
+ EATEST_VERIFY(result == -42.0);
+ }
+ }
+
+ // eastl::equal_to
+ {
+ {
+ auto result = eastl::equal_to<>{}(40, 2);
+ EA_UNUSED(result);
+ EATEST_VERIFY(!result);
+ }
+
+ {
+ auto result = eastl::equal_to<>{}(40, 40);
+ EA_UNUSED(result);
+ EATEST_VERIFY(result);
+ }
+ }
+
+ // eastl::not_equal_to
+ {
+ {
+ auto result = eastl::not_equal_to<>{}(40, 2);
+ EA_UNUSED(result);
+ EATEST_VERIFY(result);
+ }
+
+ {
+ auto result = eastl::not_equal_to<>{}(40, 40);
+ EA_UNUSED(result);
+ EATEST_VERIFY(!result);
+ }
+ }
+
+ // eastl::greater<void>
+ {
+ {
+ auto result = eastl::greater<>{}(40, 2);
+ EA_UNUSED(result);
+ EATEST_VERIFY(result);
+ }
+
+ {
+ auto result = eastl::greater<>{}(1, 2);
+ EA_UNUSED(result);
+ EATEST_VERIFY(!result);
+ }
+
+ {
+ auto result = eastl::greater<>{}(eastl::string("4"), "2");
+ EA_UNUSED(result);
+ EATEST_VERIFY(result);
+ }
+ }
+
+ // eastl::less<void>
+ {
+ {
+ auto result = eastl::less<>{}(40, 2);
+ EA_UNUSED(result);
+ EATEST_VERIFY(!result);
+ }
+
+ {
+ auto result = eastl::less<>{}(1, 2);
+ EA_UNUSED(result);
+ EATEST_VERIFY(result);
+ }
+
+ {
+ auto result = eastl::less<>{}(eastl::string("4"), "2");
+ EA_UNUSED(result);
+ EATEST_VERIFY(!result);
+ }
+ }
+
+ // eastl::greater_equal<void>
+ {
+ {
+ auto result = eastl::greater_equal<>{}(40, 2);
+ EA_UNUSED(result);
+ EATEST_VERIFY(result);
+ }
+
+ {
+ auto result = eastl::greater_equal<>{}(40, 40);
+ EA_UNUSED(result);
+ EATEST_VERIFY(result);
+ }
+
+ {
+ auto result = eastl::greater_equal<>{}(40, 43);
+ EA_UNUSED(result);
+ EATEST_VERIFY(!result);
+ }
+ }
+
+ // eastl::less_equal<void>
+ {
+ {
+ auto result = eastl::less_equal<>{}(40, 2);
+ EA_UNUSED(result);
+ EATEST_VERIFY(!result);
+ }
+
+ {
+ auto result = eastl::less_equal<>{}(40, 40);
+ EA_UNUSED(result);
+ EATEST_VERIFY(result);
+ }
+
+ {
+ auto result = eastl::less_equal<>{}(40, 43);
+ EA_UNUSED(result);
+ EATEST_VERIFY(result);
+ }
+ }
+
+ // eastl::logical_and
+ {
+ auto result = eastl::logical_and<>{}(true, true);
+ EATEST_VERIFY(result);
+ result = eastl::logical_and<>{}(true, false);
+ EATEST_VERIFY(!result);
+ result = eastl::logical_and<>{}(false, true);
+ EATEST_VERIFY(!result);
+ result = eastl::logical_and<>{}(false, false);
+ EATEST_VERIFY(!result);
+
+ bool b = false;
+ result = eastl::logical_and<>{}(b, false);
+ EATEST_VERIFY(!result);
+ }
+
+ // eastl::logical_or
+ {
+ auto result = eastl::logical_or<>{}(true, true);
+ EATEST_VERIFY(result);
+ result = eastl::logical_or<>{}(true, false);
+ EATEST_VERIFY(result);
+ result = eastl::logical_or<>{}(false, true);
+ EATEST_VERIFY(result);
+ result = eastl::logical_or<>{}(false, false);
+ EATEST_VERIFY(!result);
+
+ bool b = false;
+ result = eastl::logical_or<>{}(b, false);
+ EATEST_VERIFY(!result);
+ result = eastl::logical_or<>{}(b, true);
+ EATEST_VERIFY(result);
+ }
+
+ // eastl::logical_not
+ {
+ auto result = eastl::logical_not<>{}(true);
+ EATEST_VERIFY(!result);
+ result = eastl::logical_not<>{}(result);
+ EATEST_VERIFY(result);
+ result = eastl::logical_not<>{}(false);
+ EATEST_VERIFY(result);
+ }
+ }
+ #endif
+
+ // not_fn
+ {
+ {
+ auto ft = eastl::not_fn([] { return true; });
+ auto ff = eastl::not_fn([] { return false; });
+
+ EATEST_VERIFY(ft() == false);
+ EATEST_VERIFY(ff() == true);
+ }
+ }
+
+ // reference_wrapper
+ {
+ // operator T&
+ {
+ int i = 0;
+ eastl::reference_wrapper<int> r(i);
+ int &j = r;
+ j = 42;
+
+ EATEST_VERIFY(i == 42);
+ }
+
+ // get
+ {
+ int i = 0;
+ eastl::reference_wrapper<int> r(i);
+ r.get() = 42;
+
+ EATEST_VERIFY(i == 42);
+ }
+
+ // copy constructor
+ {
+ int i = 0;
+ eastl::reference_wrapper<int> r(i);
+ eastl::reference_wrapper<int> copy(r);
+ copy.get() = 42;
+
+ EATEST_VERIFY(i == 42);
+ }
+
+ // assignment
+ {
+ int i = 0;
+ int j = 0;
+
+ eastl::reference_wrapper<int> r1(i);
+ eastl::reference_wrapper<int> r2(j);
+
+ r2 = r1; // rebind r2 to refer to i
+ r2.get() = 42;
+
+ EATEST_VERIFY(i == 42);
+ EATEST_VERIFY(j == 0);
+ }
+
+ // invoke
+ {
+ struct Functor
+ {
+ bool called = false;
+ void operator()() {called = true;}
+ };
+
+ Functor f;
+ eastl::reference_wrapper<Functor> r(f);
+ r();
+
+ EATEST_VERIFY(f.called == true);
+ }
+
+ // ref/cref
+ {
+ {
+ int i = 0;
+ eastl::reference_wrapper<int> r1 = eastl::ref(i);
+ r1.get() = 42;
+
+ eastl::reference_wrapper<int> r2 = eastl::ref(r1);
+
+ EATEST_VERIFY(i == 42);
+ EATEST_VERIFY(r2 == 42);
+ }
+
+ {
+ int i = 1337;
+ eastl::reference_wrapper<const int> r1 = eastl::cref(i);
+ EATEST_VERIFY(r1 == 1337);
+
+ eastl::reference_wrapper<const int> r2 = eastl::cref(r1);
+ EATEST_VERIFY(r2 == 1337);
+ }
+ }
+ }
+
+ return nErrorCount;
+}
+
+// Test that we can instantiate invoke_result with incorrect argument types.
+// This should be instantiable, but should not have a `type` typedef.
+struct TestInvokeResult
+{
+ int f(int i) {return i;}
+};
+
+template struct eastl::invoke_result<decltype(&TestInvokeResult::f), TestInvokeResult, void>;
+static_assert(!eastl::is_invocable<decltype(&TestInvokeResult::f), TestInvokeResult, void>::value, "incorrect value for is_invocable");
+static_assert(eastl::is_invocable<decltype(&TestInvokeResult::f), TestInvokeResult, int>::value, "incorrect value for is_invocable");