// Copyright (C) 2008 Davis E. King (davis@dlib.net) // License: Boost Software License See LICENSE.txt for the full license. #include <string> #include <memory> #include <array> #include <tuple> #include <utility> #include <dlib/functional.h> #include "tester.h" namespace { using namespace test; using namespace dlib; logger dlog("test.invoke"); // ---------------------------------------------------------------------------------------- namespace test_swap_traits { struct some_struct {int i{0};}; void swap(some_struct& a, some_struct& b) = delete; static_assert(!dlib::is_swappable<some_struct>::value, "oops"); static_assert(!dlib::is_nothrow_swappable<some_struct>::value, "oops"); struct some_other_struct {int i{0};}; void swap(some_other_struct& a, some_other_struct& b) { std::swap(a.i, b.i); throw std::runtime_error("toy example"); } static_assert(dlib::is_swappable<some_other_struct>::value, "oops"); static_assert(!dlib::is_nothrow_swappable<some_other_struct>::value, "oops"); } // ---------------------------------------------------------------------------------------- static const std::string run1_str1 = "hello there 1"; static const std::string run1_str2 = "hello there 2"; static const std::string run1_str3 = "hello there 3"; static const std::string run1_str4 = "hello there 4"; static const std::string run1_str5 = "hello there 5"; void func_testargs(int i, std::string ref1, const std::string& ref2, const std::string& ref3, std::string& ref4) { DLIB_TEST(i > 0); DLIB_TEST(ref1 == run1_str1); DLIB_TEST(ref2 == run1_str2); DLIB_TEST(ref3 == run1_str3); DLIB_TEST(ref4 == run1_str4); ref4 = run1_str5; } int func_return_addition(int i, int j) { return i + j; } void test_functions() { static_assert(dlib::is_invocable<decltype(func_testargs), int, std::string, const std::string&, const std::string&, std::string&>::value, "should be invocable!"); static_assert(dlib::is_invocable<decltype(func_testargs), int, std::string, std::string, const std::string&, std::string&>::value, "should be invocable!"); static_assert(dlib::is_invocable<decltype(func_testargs), int, std::string, std::string, std::string, std::string&>::value, "should be invocable!"); static_assert(dlib::is_invocable<decltype(func_testargs), int, std::string, std::string, std::string, std::reference_wrapper<std::string>>::value, "should be invocable!"); static_assert(dlib::is_invocable<decltype(func_return_addition), int, int>::value, "should be invocable!"); static_assert(dlib::is_invocable_r<float, decltype(func_return_addition), int, int>::value, "should be invocable_r!"); static_assert(!dlib::is_invocable_r<std::string, decltype(func_return_addition), int, int>::value, "should be invocable_r!"); static_assert(std::is_same<dlib::callable_args<decltype(func_testargs)>, dlib::types_<int, std::string, const std::string&, const std::string&, std::string&> >::value, "make this correct"); static_assert(dlib::callable_nargs<decltype(func_testargs)>::value == 5, "bad"); static_assert(std::is_same<dlib::callable_arg<0, decltype(func_testargs)>, int>::value, "make this correct"); static_assert(std::is_same<dlib::callable_arg<1, decltype(func_testargs)>, std::string>::value, "make this correct"); static_assert(std::is_same<dlib::callable_arg<2, decltype(func_testargs)>, const std::string&>::value, "make this correct"); static_assert(std::is_same<dlib::callable_arg<3, decltype(func_testargs)>, const std::string&>::value, "make this correct"); static_assert(std::is_same<dlib::callable_arg<4, decltype(func_testargs)>, std::string&>::value, "make this correct"); static_assert(std::is_same<dlib::callable_return<decltype(func_testargs)>, void>::value, "make this correct"); static_assert(is_callable<decltype(func_testargs)>::value, "bad"); static_assert(std::is_same<dlib::callable_args<decltype(func_return_addition)>, dlib::types_<int, int> >::value, "make this correct"); static_assert(dlib::callable_nargs<decltype(func_return_addition)>::value == 2, "bad"); static_assert(std::is_same<dlib::callable_arg<0, decltype(func_return_addition)>, int >::value, "make this correct"); static_assert(std::is_same<dlib::callable_arg<1, decltype(func_return_addition)>, int >::value, "make this correct"); static_assert(std::is_same<dlib::callable_return<decltype(func_return_addition)>, int>::value, "make this correct"); static_assert(is_callable<decltype(func_return_addition)>::value, "bad"); { std::string str = run1_str4; dlib::invoke(func_testargs, 1, run1_str1, run1_str2, std::cref(run1_str3), std::ref(str)); DLIB_TEST(str == run1_str5); } { std::string str = run1_str4; dlib::apply(func_testargs, std::make_tuple(1, run1_str1, run1_str2, std::cref(run1_str3), std::ref(str))); DLIB_TEST(str == run1_str5); } { for (int i = -10 ; i <= 10 ; i++) { for (int j = -10 ; j <= 10 ; j++) { DLIB_TEST(dlib::invoke(func_return_addition, i, j) == (i+j)); DLIB_TEST(dlib::apply(func_return_addition, std::make_tuple(i, j)) == (i+j)); } } } } // ---------------------------------------------------------------------------------------- void test_lambdas() { { const auto f = [](int, float*, std::string&) -> long {return 1;}; static_assert(std::is_same<dlib::callable_args<decltype(f)>, dlib::types_<int, float*, std::string&> >::value, "make this correct"); static_assert(dlib::callable_nargs<decltype(f)>::value == 3, "bad"); static_assert(std::is_same<dlib::callable_arg<0, decltype(f)>, int>::value, "make this correct"); static_assert(std::is_same<dlib::callable_arg<1, decltype(f)>, float*>::value, "make this correct"); static_assert(std::is_same<dlib::callable_arg<2, decltype(f)>, std::string&>::value, "make this correct"); static_assert(std::is_same<dlib::callable_return<decltype(f)>, long>::value, "make this correct"); static_assert(is_callable<decltype(f)>::value, "bad"); } { std::string str = run1_str4; dlib::invoke([](int i, std::string ref1, const std::string& ref2, const std::string& ref3, std::string& ref4) { DLIB_TEST(i > 0); DLIB_TEST(ref1 == run1_str1); DLIB_TEST(ref2 == run1_str2); DLIB_TEST(ref3 == run1_str3); DLIB_TEST(ref4 == run1_str4); ref4 = run1_str5; }, 1, run1_str1, run1_str2, std::cref(run1_str3), std::ref(str)); DLIB_TEST(str == run1_str5); } { std::string str = run1_str4; dlib::apply([](int i, std::string ref1, const std::string& ref2, const std::string& ref3, std::string& ref4) { DLIB_TEST(i > 0); DLIB_TEST(ref1 == run1_str1); DLIB_TEST(ref2 == run1_str2); DLIB_TEST(ref3 == run1_str3); DLIB_TEST(ref4 == run1_str4); ref4 = run1_str5; }, std::make_tuple(1, run1_str1, run1_str2, std::cref(run1_str3), std::ref(str))); DLIB_TEST(str == run1_str5); } { for (int i = -10 ; i <= 10 ; i++) { for (int j = -10 ; j <= 10 ; j++) { DLIB_TEST(dlib::invoke([](int i, int j) {return i + j;}, i, j) == (i+j)); DLIB_TEST(dlib::apply([](int i, int j) {return i + j;}, std::make_tuple(i,j)) == (i+j)); } } } } // ---------------------------------------------------------------------------------------- struct example_struct { example_struct(int i_ = 0) : i(i_) {} example_struct(const example_struct&) = delete; example_struct& operator=(const example_struct&) = delete; example_struct(example_struct&& other) : i(other.i) {other.i = 0;} example_struct& operator=(example_struct&& other) {i = other.i; other.i = 0; return *this;} int get_i() const {return i;} float operator()(const char*) {return 1.0f;} int i = 0; }; static_assert(std::is_same<dlib::callable_args<example_struct>, dlib::types_<const char*>>::value, "make this correct"); static_assert(dlib::callable_nargs<example_struct>::value == 1, "bad"); static_assert(std::is_same<dlib::callable_arg<0, example_struct>, const char*>::value, "make this correct"); static_assert(std::is_same<dlib::callable_return<example_struct>, float>::value, "make this correct"); static_assert(is_callable<example_struct>::value, "bad"); void test_member_functions_and_data() { example_struct obj1(10); std::unique_ptr<example_struct> obj2(new example_struct(11)); std::shared_ptr<example_struct> obj3(new example_struct(12)); DLIB_TEST(dlib::invoke(&example_struct::get_i, obj1) == 10); DLIB_TEST(dlib::invoke(&example_struct::i, obj1) == 10); DLIB_TEST(dlib::invoke(&example_struct::get_i, &obj1) == 10); DLIB_TEST(dlib::invoke(&example_struct::i, &obj1) == 10); DLIB_TEST(dlib::invoke(&example_struct::get_i, obj2) == 11); DLIB_TEST(dlib::invoke(&example_struct::i, obj2) == 11); DLIB_TEST(dlib::invoke(&example_struct::get_i, obj3) == 12); DLIB_TEST(dlib::invoke(&example_struct::i, obj3) == 12); } // ---------------------------------------------------------------------------------------- int return_int() { return 0; } int& return_int_ref() { static int i = 0; return i; } const int& return_int_const_ref() { static const int i = 0; return i; } int* return_int_pointer() { static int i = 0; return &i; } const int* return_int_const_pointer() { static const int i = 0; return &i; } void test_return_types() { static_assert(std::is_same<int, dlib::invoke_result_t<decltype(return_int)>>::value, "bad type"); static_assert(std::is_same<int&, dlib::invoke_result_t<decltype(return_int_ref)>>::value, "bad type"); static_assert(std::is_same<const int&, dlib::invoke_result_t<decltype(return_int_const_ref)>>::value, "bad type"); static_assert(std::is_same<int*, dlib::invoke_result_t<decltype(return_int_pointer)>>::value, "bad type"); static_assert(std::is_same<const int*, dlib::invoke_result_t<decltype(return_int_const_pointer)>>::value, "bad type"); static_assert(std::is_same<int, dlib::invoke_result_t<decltype(&example_struct::get_i), const example_struct&>>::value, "bad type"); static_assert(std::is_same<int, dlib::invoke_result_t<decltype(&example_struct::get_i), example_struct&>>::value, "bad type"); static_assert(std::is_same<int, dlib::invoke_result_t<decltype(&example_struct::get_i), const example_struct*>>::value, "bad type"); static_assert(std::is_same<int, dlib::invoke_result_t<decltype(&example_struct::get_i), example_struct*>>::value, "bad type"); static_assert(std::is_same<int, dlib::invoke_result_t<decltype(&example_struct::get_i), std::unique_ptr<example_struct>>>::value, "bad type"); static_assert(std::is_same<int, dlib::invoke_result_t<decltype(&example_struct::get_i), std::shared_ptr<example_struct>>>::value, "bad type"); static_assert(std::is_same<const int&, dlib::invoke_result_t<decltype(&example_struct::i), const example_struct&>>::value, "bad type"); static_assert(std::is_same<int&, dlib::invoke_result_t<decltype(&example_struct::i), example_struct&>>::value, "bad type"); static_assert(std::is_same<const int&, dlib::invoke_result_t<decltype(&example_struct::i), const example_struct*>>::value, "bad type"); static_assert(std::is_same<int&, dlib::invoke_result_t<decltype(&example_struct::i), example_struct*>>::value, "bad type"); static_assert(std::is_same<int&, dlib::invoke_result_t<decltype(&example_struct::i), std::unique_ptr<example_struct>>>::value, "bad type"); static_assert(std::is_same<int&, dlib::invoke_result_t<decltype(&example_struct::i), std::shared_ptr<example_struct>>>::value, "bad type"); auto lambda_func_return_int = []() -> int {return 0;}; static_assert(std::is_same<int, dlib::invoke_result_t<decltype(lambda_func_return_int)>>::value, "bad type"); } // ---------------------------------------------------------------------------------------- void test_make_from_tuple() { struct multi_args_object { multi_args_object(int i_, int j_) : i(i_), j(j_) {} int i = 0; int j = 0; }; { auto obj = dlib::make_from_tuple<multi_args_object>(std::make_tuple(1, 2)); static_assert(std::is_same<decltype(obj), multi_args_object>::value, "bad type"); DLIB_TEST(obj.i == 1); DLIB_TEST(obj.j == 2); } { std::array<int,2> a = {3, 4}; auto obj = dlib::make_from_tuple<multi_args_object>(a); static_assert(std::is_same<decltype(obj), multi_args_object>::value, "bad type"); DLIB_TEST(obj.i == 3); DLIB_TEST(obj.j == 4); } { auto obj = dlib::make_from_tuple<multi_args_object>(std::make_pair(5, 6)); static_assert(std::is_same<decltype(obj), multi_args_object>::value, "bad type"); DLIB_TEST(obj.i == 5); DLIB_TEST(obj.j == 6); } } // ---------------------------------------------------------------------------------------- const char* func_return_c_string() { return "hello darkness my old friend"; } struct obj_return_c_string { obj_return_c_string() = default; obj_return_c_string(const obj_return_c_string& rhs) = delete; obj_return_c_string(obj_return_c_string&& rhs) = delete; const char* run() { return "i've come to talk with you again"; } }; void test_invoke_r() { { static_assert(dlib::is_invocable_r<std::string, decltype(func_return_c_string)>::value, "should be invocable"); auto str = dlib::invoke_r<std::string>(func_return_c_string); static_assert(std::is_same<decltype(str), std::string>::value, "bad return type"); DLIB_TEST(str == "hello darkness my old friend"); } { obj_return_c_string obj; static_assert(dlib::is_invocable_r<std::string, decltype(&obj_return_c_string::run), decltype(obj)>::value, "should be invocable"); auto str = dlib::invoke_r<std::string>(&obj_return_c_string::run, obj); static_assert(std::is_same<decltype(str), std::string>::value, "bad return type"); DLIB_TEST(str == "i've come to talk with you again"); } { obj_return_c_string obj; static_assert(dlib::is_invocable_r<std::string, decltype(&obj_return_c_string::run), decltype(&obj)>::value, "should be invocable"); auto str = dlib::invoke_r<std::string>(&obj_return_c_string::run, &obj); static_assert(std::is_same<decltype(str), std::string>::value, "bad return type"); DLIB_TEST(str == "i've come to talk with you again"); } { auto obj = std::make_shared<obj_return_c_string>(); static_assert(dlib::is_invocable_r<std::string, decltype(&obj_return_c_string::run), decltype(obj)>::value, "should be invocable"); auto str = dlib::invoke_r<std::string>(&obj_return_c_string::run, obj); static_assert(std::is_same<decltype(str), std::string>::value, "bad return type"); DLIB_TEST(str == "i've come to talk with you again"); } { std::unique_ptr<obj_return_c_string> obj(new obj_return_c_string()); static_assert(dlib::is_invocable_r<std::string, decltype(&obj_return_c_string::run), decltype(obj)>::value, "should be invocable"); auto str = dlib::invoke_r<std::string>(&obj_return_c_string::run, obj); static_assert(std::is_same<decltype(str), std::string>::value, "bad return type"); DLIB_TEST(str == "i've come to talk with you again"); } { auto lambda_return_c_string = [] { return "because a vision softly creeping"; }; static_assert(dlib::is_invocable_r<std::string, decltype(lambda_return_c_string)>::value, "should be invocable"); auto str = dlib::invoke_r<std::string>(lambda_return_c_string); static_assert(std::is_same<decltype(str), std::string>::value, "bad return type"); DLIB_TEST(str == "because a vision softly creeping"); } } // ---------------------------------------------------------------------------------------- constexpr int multiply_ints(int i, int j) { return i*j; } struct constexpr_object { constexpr int multiply_ints(int i, int j) const { return i*j; } }; void test_constexpr() { static_assert(dlib::invoke(multiply_ints, 2, 5) == 10, "this should be constexpr"); static_assert(dlib::invoke_r<long>(multiply_ints, 2, 5) == 10, "this should be constexpr"); constexpr constexpr_object constexpr_obj; #if defined (_MSC_VER) constexpr_obj; // avoid warning C4101: 'constexpr_obj': unreferenced local variable #endif static_assert(dlib::invoke(&constexpr_object::multiply_ints, constexpr_obj, 2, 5) == 10, "this should be constexpr"); static_assert(dlib::invoke_r<long>(&constexpr_object::multiply_ints, constexpr_obj, 2, 5) == 10, "this should be constexpr"); } // ---------------------------------------------------------------------------------------- constexpr int test_bind_func(int a, int b) { return a - b; } struct test_bind_func_class { constexpr test_bind_func_class(int v) : val{v} {} int val; constexpr int minus(int arg) const noexcept { return val - arg; } }; void test_bind_front() { // Pure function static_assert(dlib::bind_front(test_bind_func, 50)(3) == 47, "this should be constexpr"); DLIB_TEST(dlib::bind_front(test_bind_func, 50)(3) == 47); // Member function static_assert(dlib::bind_front(&test_bind_func_class::minus, test_bind_func_class{50})(3) == 47, "this should be constexpr"); DLIB_TEST(dlib::bind_front(&test_bind_func_class::minus, test_bind_func_class{50})(3) == 47); DLIB_TEST(dlib::bind_front(&test_bind_func_class::minus, std::make_shared<test_bind_func_class>(50))(3) == 47); // Lambda DLIB_TEST(dlib::bind_front([](int a, int b) {return a - b;}, 50)(3) == 47); } void test_bind_back() { // Pure function static_assert(dlib::bind_back(test_bind_func, 50)(3) == -47, "this should be constexpr"); DLIB_TEST(dlib::bind_back(test_bind_func, 50)(3) == -47); // Member function static_assert(dlib::bind_back(&test_bind_func_class::minus, 50)(test_bind_func_class{3}) == -47, "this should be constexpr"); DLIB_TEST(dlib::bind_back(&test_bind_func_class::minus, 50)(test_bind_func_class{3}) == -47); DLIB_TEST(dlib::bind_back(&test_bind_func_class::minus, 50)(std::make_shared<test_bind_func_class>(3)) == -47); // Lambda DLIB_TEST(dlib::bind_back([](int a, int b) {return a - b;}, 50)(3) == -47); } // ---------------------------------------------------------------------------------------- namespace test_callable_traits { template < class Callable, std::enable_if_t<is_callable<Callable>::value, bool> = true, std::enable_if_t<callable_nargs<Callable>::value >= 1, bool> = true, std::enable_if_t<std::is_floating_point<callable_arg<0, Callable>>::value, bool> = true > auto wrap ( Callable&& clb, int& i ) { i = 1; return [pclb = std::forward<Callable>(clb)](float a, int b, long c) { pclb(a, b, c); }; } template < class Callable, std::enable_if_t<is_callable<Callable>::value, bool> = true, std::enable_if_t<callable_nargs<Callable>::value >= 1, bool> = true, std::enable_if_t<std::is_integral<callable_arg<0, Callable>>::value, bool> = true > auto wrap ( Callable&& clb, int& i ) { i = 2; return [pclb = std::forward<Callable>(clb)](int a, float b, double c) { pclb(a, b, c); }; } template < class Callable, std::enable_if_t<is_callable<Callable>::value, bool> = true, std::enable_if_t<callable_nargs<Callable>::value < 1, bool> = true > auto wrap ( Callable&& clb, int& i ) { i = 3; return [pclb = std::forward<Callable>(clb)] { pclb(); }; } void test() { const auto f1 = [](float a, int b, long c) { return a + b + c;}; const auto f2 = [](int a, float b, double c) { return a + b + c;}; const auto f3 = [] {}; int i{0}; const auto f4 = wrap(f1, i); DLIB_TEST(i == 1); const auto f5 = wrap(f2, i); DLIB_TEST(i == 2); const auto f6 = wrap(f3, i); DLIB_TEST(i == 3); } static_assert(!is_callable<int>::value, "bad"); } class invoke_tester : public tester { public: invoke_tester( ) : tester("test_invoke", "Runs tests on dlib::invoke and dlib::apply") {} void perform_test( ) { test_functions(); test_lambdas(); test_member_functions_and_data(); test_return_types(); test_make_from_tuple(); test_invoke_r(); test_constexpr(); test_bind_front(); test_bind_back(); test_callable_traits::test(); } } a; }