// Copyright (C) 2022 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#include <string>
#include <dlib/constexpr_if.h>
#include <dlib/functional.h>
#include <cstdio>
#include "tester.h"
namespace
{
using namespace test;
using namespace dlib;
logger dlog("test.constexpr_if");
struct A
{
int i;
};
struct B
{
float f;
};
struct C
{
std::string str;
};
struct D
{
int i;
void set_i(int j) {i = j;}
};
template<typename T>
auto handle_type_and_return1(T obj)
{
return switch_(types_<T>{},
[&](types_<A>, auto _) {
return _(obj).i;
},
[&](types_<B>, auto _) {
return _(obj).f;
},
[&](types_<C>, auto _) {
return _(obj).str;
},
[&](auto...) {
printf("Don't know what this type is\n");
}
);
}
template<typename T>
auto handle_type_and_return2(T obj)
{
return switch_(bools(std::is_same<T,A>{}, std::is_same<T,B>{}, std::is_same<T,C>{}),
[&](true_t, auto, auto, auto _) {
return _(obj).i;
},
[&](auto, true_t, auto, auto _) {
return _(obj).f;
},
[&](auto, auto, true_t, auto _) {
return _(obj).str;
},
[&](auto...) {
printf("Don't know what this type is\n");
}
);
}
void test_switch_type()
{
A a{1};
B b{2.5f};
C c{"hello there!"};
{
auto ret = handle_type_and_return1(a);
static_assert(std::is_same<decltype(ret), int>::value, "failed test");
DLIB_TEST(ret == a.i);
}
{
auto ret = handle_type_and_return2(a);
static_assert(std::is_same<decltype(ret), int>::value, "failed test");
DLIB_TEST(ret == a.i);
}
{
auto ret = handle_type_and_return1(b);
static_assert(std::is_same<decltype(ret), float>::value, "failed test");
DLIB_TEST(ret == b.f);
}
{
auto ret = handle_type_and_return2(b);
static_assert(std::is_same<decltype(ret), float>::value, "failed test");
DLIB_TEST(ret == b.f);
}
{
auto ret = handle_type_and_return1(c);
static_assert(std::is_same<decltype(ret), std::string>::value, "failed test");
DLIB_TEST(ret == c.str);
}
{
auto ret = handle_type_and_return2(c);
static_assert(std::is_same<decltype(ret), std::string>::value, "failed test");
DLIB_TEST(ret == c.str);
}
}
template <typename Func, typename... Args>
bool try_invoke(Func&& f, Args&&... args)
{
return switch_(bools(is_invocable<Func, Args...>{}),
[&](true_t, auto _) {
_(std::forward<Func>(f))(std::forward<Args>(args)...);
return true;
},
[](auto...) {
return false;
}
);
}
void test_try_invoke()
{
int value = 0;
auto foo = [&]{ value++; };
auto bar = [&](int i) { value += i; };
auto baz = [&](int i, int j) { value += (i+j); };
DLIB_TEST(try_invoke(foo));
DLIB_TEST(value == 1);
DLIB_TEST(!try_invoke(foo, 1));
DLIB_TEST(value == 1);
DLIB_TEST(!try_invoke(foo, 1, 2));
DLIB_TEST(value == 1);
DLIB_TEST(!try_invoke(bar));
DLIB_TEST(value == 1);
DLIB_TEST(try_invoke(bar, 1));
DLIB_TEST(value == 2);
DLIB_TEST(!try_invoke(bar, 1, 2));
DLIB_TEST(value == 2);
DLIB_TEST(!try_invoke(baz));
DLIB_TEST(value == 2);
DLIB_TEST(!try_invoke(baz, 1));
DLIB_TEST(value == 2);
DLIB_TEST(try_invoke(baz, 1, 2));
DLIB_TEST(value == 5);
}
template<typename T>
using set_i_pred = decltype(std::declval<T>().set_i(int{}));
template<typename T>
bool try_set_i(T& obj, int i)
{
return switch_(bools(is_detected<set_i_pred, T>{}),
[&](true_t, auto _) {
_(obj).set_i(i);
return true;
},
[](auto...){
return false;
}
);
}
void test_set_i()
{
A a{1};
D d{1};
DLIB_TEST(!try_set_i(a, 2));
DLIB_TEST(a.i == 1);
DLIB_TEST(try_set_i(d, 2));
DLIB_TEST(d.i == 2);
}
class constexpr_if_test : public tester
{
public:
constexpr_if_test (
) : tester ("test_constexpr_if",
"Runs tests on the C++14 approximation of C++17 if constexpr() statements but better.")
{}
void perform_test (
)
{
test_switch_type();
test_try_invoke();
test_set_i();
}
} a;
}