pcsx-redux/third_party/EASTL/test/source/TestFunctional.cpp
Nicolas 'Pixel' Noble d63f87a7f4 Adding EASTL.
2022-06-29 19:37:35 -07:00

1495 lines
40 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// 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 volatile N1& n1, const volatile N1& n1a) { return (n1.mX == n1a.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 volatile N1& n1, const volatile N1& n1a) { return (n1.mX != n1a.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 volatile N1& n1, const volatile N1& n1a) { return (n1.mX < n1a.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);
volatile N1 vn11(1);
volatile N1 vn13(3);
const volatile N1 cvn11(1);
const volatile N1 cvn13(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));
EATEST_VERIFY(!es(n11, n13));
equal_to_2<const N1, N1> ec;
EATEST_VERIFY(ec(cn11, n11));
EATEST_VERIFY(ec(n11, cn11));
equal_to_2<N1, const N1> ec2;
EATEST_VERIFY(ec2(n11, cn11));
EATEST_VERIFY(ec2(cn11, n11));
equal_to_2<const N1, const N1> ecc;
EATEST_VERIFY(ecc(cn11, cn11));
equal_to_2<volatile N1, N1> ev;
EATEST_VERIFY(ev(vn11, n11));
EATEST_VERIFY(ev(n11, vn11));
equal_to_2<N1, volatile N1> ev2;
EATEST_VERIFY(ev2(n11, vn11));
EATEST_VERIFY(ev2(vn11, n11));
equal_to_2<volatile N1, volatile N1> evv;
EATEST_VERIFY(evv(vn11, vn11));
equal_to_2<const volatile N1, N1> ecv;
EATEST_VERIFY(ecv(cvn11, n11));
EATEST_VERIFY(ecv(n11, cvn11));
equal_to_2<N1, const volatile N1> ecv2;
EATEST_VERIFY(ecv2(n11, cvn11));
EATEST_VERIFY(ecv2(cvn11, n11));
equal_to_2<const volatile N1, const volatile N1> ecvcv;
EATEST_VERIFY(ecvcv(cvn11, cvn11));
// 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));
EATEST_VERIFY(!ns(n11, n11));
not_equal_to_2<const N1, N1> nc;
EATEST_VERIFY(nc(cn11, n13));
EATEST_VERIFY(nc(n13, cn11));
not_equal_to_2<N1, const N1> nc2;
EATEST_VERIFY(nc2(n13, cn11));
EATEST_VERIFY(nc2(cn11, n13));
not_equal_to_2<const N1, const N1> ncc;
EATEST_VERIFY(ncc(cn11, cn13));
not_equal_to_2<volatile N1, N1> nv;
EATEST_VERIFY(nv(vn11, n13));
EATEST_VERIFY(nv(n11, vn13));
not_equal_to_2<N1, volatile N1> nv2;
EATEST_VERIFY(nv2(n11, vn13));
EATEST_VERIFY(nv2(vn11, n13));
not_equal_to_2<volatile N1, volatile N1> nvv;
EATEST_VERIFY(nvv(vn11, vn13));
not_equal_to_2<const volatile N1, N1> ncv;
EATEST_VERIFY(ncv(cvn11, n13));
EATEST_VERIFY(ncv(n11, cvn13));
not_equal_to_2<N1, const volatile N1> ncv2;
EATEST_VERIFY(ncv2(n11, cvn13));
EATEST_VERIFY(ncv2(cvn11, n13));
not_equal_to_2<const volatile N1, const volatile N1> ncvcv;
EATEST_VERIFY(ncvcv(cvn11, cvn13));
// 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));
less_2<const N1, N1> lec;
EATEST_VERIFY(lec(cn11, n13));
EATEST_VERIFY(lec(n11, cn13));
less_2<N1, const N1> lec2;
EATEST_VERIFY(lec2(n11, cn13));
EATEST_VERIFY(lec2(cn11, n13));
less_2<const N1, const N1> lecc;
EATEST_VERIFY(lecc(cn11, cn13));
less_2<volatile N1, N1> lev;
EATEST_VERIFY(lev(vn11, n13));
EATEST_VERIFY(lev(n11, vn13));
less_2<N1, volatile N1> lev2;
EATEST_VERIFY(lev2(n11, vn13));
EATEST_VERIFY(lev2(vn11, n13));
less_2<volatile N1, volatile N1> levv;
EATEST_VERIFY(levv(vn11, vn13));
less_2<const volatile N1, N1> lecv;
EATEST_VERIFY(lecv(cvn11, n13));
EATEST_VERIFY(lecv(n11, cvn13));
less_2<N1, const volatile N1> lecv2;
EATEST_VERIFY(lecv2(n11, cvn13));
EATEST_VERIFY(lecv2(cvn11, n13));
less_2<const volatile N1, const volatile N1> lecvcv;
EATEST_VERIFY(lecvcv(cvn11, cvn13));
}
{
// 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; }
void NoThrow(int inValue) EA_NOEXCEPT {}
int value;
};
struct TestFunctor
{
void operator()() { called = true; }
bool called = false;
};
struct TestFunctorNoThrow
{
void operator()() EA_NOEXCEPT { 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");
static_assert(eastl::is_nothrow_invocable<decltype(&TestStruct::NoThrow), TestStruct, int>::value, "incorrect value for is_nothrow_invocable");
static_assert(!eastl::is_nothrow_invocable<decltype(&TestStruct::Add), TestStruct, int>::value, "incorrect value for is_nothrow_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");
static_assert(eastl::is_nothrow_invocable<decltype(&TestStruct::NoThrow), TestStruct *, int>::value, "incorrect value for is_nothrow_invocable");
static_assert(!eastl::is_nothrow_invocable<decltype(&TestStruct::Add), TestStruct *, int>::value, "incorrect value for is_nothrow_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");
static_assert(eastl::is_nothrow_invocable<decltype(&TestStruct::NoThrow), eastl::reference_wrapper<TestStruct>, int>::value, "incorrect value for is_nothrow_invocable");
static_assert(!eastl::is_nothrow_invocable<decltype(&TestStruct::Add), eastl::reference_wrapper<TestStruct>, int>::value, "incorrect value for is_nothrow_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");
static_assert(!eastl::is_nothrow_invocable<decltype(f)>::value, "incorrect value for is_nothrow_invocable");
}
{
TestFunctorNoThrow 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");
static_assert(eastl::is_nothrow_invocable<decltype(f)>::value, "incorrect value for is_nothrow_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");
}
{
struct TestInvokeConstAccess
{
void ConstMemberFunc(int i) const {}
void ConstVolatileMemberFunc(int i) const volatile {}
int mI;
};
static_assert(eastl::is_invocable<decltype(&TestInvokeConstAccess::ConstMemberFunc), const TestInvokeConstAccess*, int>::value, "incorrect value for is_invocable");
static_assert(eastl::is_invocable<decltype(&TestInvokeConstAccess::ConstVolatileMemberFunc), const volatile TestInvokeConstAccess*, int>::value, "incorrect value for is_invocable");
}
{
struct TestReferenceWrapperInvoke
{
int NonConstMemberFunc(int i) { return i; }
int ConstMemberFunc(int i) const { return i; }
int mI = 1;
const int mIC = 1;
};
TestReferenceWrapperInvoke testStruct;
int ret;
ret = eastl::invoke(&TestReferenceWrapperInvoke::NonConstMemberFunc, eastl::ref(testStruct), 1);
EATEST_VERIFY(ret == 1);
ret = eastl::invoke(&TestReferenceWrapperInvoke::ConstMemberFunc, eastl::ref(testStruct), 1);
EATEST_VERIFY(ret == 1);
ret = eastl::invoke(&TestReferenceWrapperInvoke::mI, eastl::ref(testStruct));
EATEST_VERIFY(ret == 1);
ret = eastl::invoke(&TestReferenceWrapperInvoke::mIC, eastl::ref(testStruct));
EATEST_VERIFY(ret == 1);
}
{
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 C : public A {};
struct TestStruct
{
A a() { return A(); };
B b() { return B(); };
C c() EA_NOEXCEPT { return C(); };
};
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");
static_assert(!eastl::is_nothrow_invocable_r<B, decltype(&TestStruct::b), TestStruct>::value, "incorrect value for is_nothrow_invocable_r");
static_assert(eastl::is_nothrow_invocable_r<C, decltype(&TestStruct::c), TestStruct>::value, "incorrect value for is_nothrow_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");