mirror of https://github.com/oxen-io/lokinet
Initial type trait work
This commit is contained in:
parent
b80ecfa4d6
commit
95a5c386fe
|
@ -27,6 +27,7 @@ set(LIB_UTIL_SRC
|
|||
util/threadpool.cpp
|
||||
util/time.cpp
|
||||
util/timer.cpp
|
||||
util/traits.cpp
|
||||
util/types.cpp
|
||||
)
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
#include <util/traits.hpp>
|
|
@ -0,0 +1,177 @@
|
|||
#ifndef LLARP_TRAITS_HPP
|
||||
#define LLARP_TRAITS_HPP
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
namespace traits
|
||||
{
|
||||
/// Represents the empty type
|
||||
struct Bottom
|
||||
{
|
||||
};
|
||||
|
||||
/// C++17 compatibility. template pack
|
||||
template < class... >
|
||||
using void_t = void;
|
||||
|
||||
/// Type trait representing whether a type is an STL-style container
|
||||
template < typename T, typename _ = void >
|
||||
struct is_container : public std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
// We take that the container has begin, end and size methods to be a
|
||||
// container.
|
||||
// clang-format off
|
||||
template < typename T >
|
||||
struct is_container<
|
||||
T,
|
||||
std::conditional_t<
|
||||
false,
|
||||
void_t< typename T::value_type,
|
||||
typename T::size_type,
|
||||
typename T::iterator,
|
||||
typename T::const_iterator,
|
||||
decltype(std::declval<T>().size()),
|
||||
decltype(std::declval<T>().begin()),
|
||||
decltype(std::declval<T>().end()),
|
||||
decltype(std::declval<T>().cbegin()),
|
||||
decltype(std::declval<T>().cend()) >,
|
||||
void > > : public std::true_type
|
||||
{
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
namespace Switch
|
||||
{
|
||||
template < size_t Selector, typename... Types >
|
||||
struct Switch
|
||||
{
|
||||
using Type = Bottom;
|
||||
};
|
||||
|
||||
template < typename T, typename... Types >
|
||||
struct Switch< 0u, T, Types... >
|
||||
{
|
||||
using Type = T;
|
||||
};
|
||||
|
||||
template < size_t Selector, typename Tn, typename... Types >
|
||||
struct Switch< Selector, Tn, Types... >
|
||||
{
|
||||
using Type = typename Switch< Selector - 1, Types... >::Type;
|
||||
};
|
||||
|
||||
} // namespace Switch
|
||||
|
||||
namespace select
|
||||
{
|
||||
/// This provides a way to do a compile-type dispatch based on type traits
|
||||
|
||||
/// meta function which always returns false
|
||||
template < typename >
|
||||
class False : public std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
/// a case in the selection
|
||||
template < template < typename... > class Trait = False >
|
||||
class Case
|
||||
{
|
||||
public:
|
||||
template < typename Type >
|
||||
struct Selector : public Trait< Type >::type
|
||||
{
|
||||
};
|
||||
|
||||
using Type = Case;
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
|
||||
/// implementation helper
|
||||
template < typename T,
|
||||
template < typename > class Trait1,
|
||||
template < typename > class Trait2,
|
||||
template < typename > class Trait3,
|
||||
template < typename > class Trait4,
|
||||
template < typename > class Trait5,
|
||||
template < typename > class Trait6,
|
||||
template < typename > class Trait7,
|
||||
template < typename > class Trait8,
|
||||
template < typename > class Trait9 >
|
||||
struct SelectHelper {
|
||||
enum {
|
||||
Selector = (
|
||||
Trait1<T>::value ? 1 :
|
||||
Trait2<T>::value ? 2 :
|
||||
Trait3<T>::value ? 3 :
|
||||
Trait4<T>::value ? 4 :
|
||||
Trait5<T>::value ? 5 :
|
||||
Trait6<T>::value ? 6 :
|
||||
Trait7<T>::value ? 7 :
|
||||
Trait8<T>::value ? 8 :
|
||||
Trait9<T>::value ? 9 : 0
|
||||
)
|
||||
};
|
||||
|
||||
using Type = typename Switch::Switch<
|
||||
Selector,
|
||||
Case<>,
|
||||
Case<Trait1>,
|
||||
Case<Trait2>,
|
||||
Case<Trait3>,
|
||||
Case<Trait4>,
|
||||
Case<Trait5>,
|
||||
Case<Trait6>,
|
||||
Case<Trait7>,
|
||||
Case<Trait8>,
|
||||
Case<Trait9>
|
||||
>::Type;
|
||||
};
|
||||
|
||||
template< typename Type,
|
||||
template < typename > class Trait1,
|
||||
template < typename > class Trait2 = False,
|
||||
template < typename > class Trait3 = False,
|
||||
template < typename > class Trait4 = False,
|
||||
template < typename > class Trait5 = False,
|
||||
template < typename > class Trait6 = False,
|
||||
template < typename > class Trait7 = False,
|
||||
template < typename > class Trait8 = False,
|
||||
template < typename > class Trait9 = False >
|
||||
struct Select : public SelectHelper< Type,
|
||||
Trait1,
|
||||
Trait2,
|
||||
Trait3,
|
||||
Trait4,
|
||||
Trait5,
|
||||
Trait6,
|
||||
Trait7,
|
||||
Trait8,
|
||||
Trait9 >::Type
|
||||
{
|
||||
enum {
|
||||
Selector = SelectHelper< Type,
|
||||
Trait1,
|
||||
Trait2,
|
||||
Trait3,
|
||||
Trait4,
|
||||
Trait5,
|
||||
Trait6,
|
||||
Trait7,
|
||||
Trait8,
|
||||
Trait9 >::Selector
|
||||
};
|
||||
|
||||
using SelectorType = std::integral_constant<int, Selector>;
|
||||
};
|
||||
|
||||
// clang-format on
|
||||
} // namespace select
|
||||
} // namespace traits
|
||||
} // namespace llarp
|
||||
|
||||
#endif
|
|
@ -37,6 +37,7 @@ list(APPEND TEST_SRC
|
|||
util/test_llarp_util_queue_manager.cpp
|
||||
util/test_llarp_util_queue.cpp
|
||||
util/test_llarp_util_thread_pool.cpp
|
||||
util/test_llarp_util_traits.cpp
|
||||
)
|
||||
|
||||
add_executable(${TEST_EXE}
|
||||
|
|
|
@ -0,0 +1,154 @@
|
|||
#include <util/traits.hpp>
|
||||
|
||||
#include <list>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
using namespace llarp;
|
||||
|
||||
TEST(traits_bottom, Smoke)
|
||||
{
|
||||
traits::Bottom bottom;
|
||||
(void)bottom;
|
||||
SUCCEED();
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
class IsContainer : public ::testing::Test
|
||||
{
|
||||
};
|
||||
|
||||
TYPED_TEST_SUITE_P(IsContainer);
|
||||
|
||||
TYPED_TEST_P(IsContainer, Smoke)
|
||||
{
|
||||
bool expected = std::tuple_element_t< 1, TypeParam >::value;
|
||||
bool result =
|
||||
traits::is_container< std::tuple_element_t< 0, TypeParam > >::value;
|
||||
ASSERT_EQ(expected, result);
|
||||
}
|
||||
|
||||
REGISTER_TYPED_TEST_SUITE_P(IsContainer, Smoke);
|
||||
|
||||
// clang-format off
|
||||
using ContainerTypes = ::testing::Types<
|
||||
std::tuple< std::vector< int >, std::integral_constant< bool, true > >,
|
||||
std::tuple< std::vector< std::string >, std::integral_constant< bool, true > >,
|
||||
std::tuple< std::list< std::string >, std::integral_constant< bool, true > >,
|
||||
std::tuple< std::string, std::integral_constant< bool, true > >,
|
||||
std::tuple< std::shared_ptr<std::string>, std::integral_constant< bool, false > >,
|
||||
std::tuple< std::tuple<std::string>, std::integral_constant< bool, false > >,
|
||||
std::tuple< int, std::integral_constant< bool, false > >
|
||||
>;
|
||||
INSTANTIATE_TYPED_TEST_SUITE_P(traits, IsContainer, ContainerTypes);
|
||||
|
||||
struct A { };
|
||||
struct B { };
|
||||
struct C { };
|
||||
struct D { };
|
||||
struct E { };
|
||||
struct F { };
|
||||
struct G { };
|
||||
struct H { };
|
||||
struct I { };
|
||||
struct J { };
|
||||
|
||||
char f(A) { return 'A'; }
|
||||
char f(B) { return 'B'; }
|
||||
char f(C) { return 'C'; }
|
||||
char f(D) { return 'D'; }
|
||||
char f(E) { return 'E'; }
|
||||
char f(F) { return 'F'; }
|
||||
char f(G) { return 'G'; }
|
||||
char f(H) { return 'H'; }
|
||||
char f(I) { return 'I'; }
|
||||
char f(J) { return 'J'; }
|
||||
char f(traits::Bottom) { return '0'; }
|
||||
|
||||
// clang-format on
|
||||
|
||||
template < typename T >
|
||||
class TestSwitch : public ::testing::Test
|
||||
{
|
||||
};
|
||||
|
||||
TYPED_TEST_SUITE_P(TestSwitch);
|
||||
|
||||
TYPED_TEST_P(TestSwitch, Smoke)
|
||||
{
|
||||
char expected = std::tuple_element_t< 0, TypeParam >::value;
|
||||
using InputType = typename std::tuple_element_t< 1, TypeParam >::Type;
|
||||
char result = f(InputType());
|
||||
ASSERT_EQ(expected, result);
|
||||
}
|
||||
|
||||
REGISTER_TYPED_TEST_SUITE_P(TestSwitch, Smoke);
|
||||
|
||||
// clang-format off
|
||||
using namespace traits::Switch;
|
||||
using SwitchTypes = ::testing::Types<
|
||||
std::tuple<std::integral_constant<char, 'A'>, Switch< 0, A, B, C, D, E, F, G, H, I, J > >,
|
||||
std::tuple<std::integral_constant<char, 'B'>, Switch< 1, A, B, C, D, E, F, G, H, I, J > >,
|
||||
std::tuple<std::integral_constant<char, 'C'>, Switch< 2, A, B, C, D, E, F, G, H, I, J > >,
|
||||
std::tuple<std::integral_constant<char, 'D'>, Switch< 3, A, B, C, D, E, F, G, H, I, J > >,
|
||||
std::tuple<std::integral_constant<char, 'E'>, Switch< 4, A, B, C, D, E, F, G, H, I, J > >,
|
||||
std::tuple<std::integral_constant<char, 'F'>, Switch< 5, A, B, C, D, E, F, G, H, I, J > >,
|
||||
std::tuple<std::integral_constant<char, 'G'>, Switch< 6, A, B, C, D, E, F, G, H, I, J > >,
|
||||
std::tuple<std::integral_constant<char, 'H'>, Switch< 7, A, B, C, D, E, F, G, H, I, J > >,
|
||||
std::tuple<std::integral_constant<char, 'I'>, Switch< 8, A, B, C, D, E, F, G, H, I, J > >,
|
||||
std::tuple<std::integral_constant<char, 'J'>, Switch< 9, A, B, C, D, E, F, G, H, I, J > >,
|
||||
std::tuple<std::integral_constant<char, 'J'>, Switch< 9, C, C, C, C, C, C, C, C, C, J > >,
|
||||
std::tuple<std::integral_constant<char, 'C'>, Switch< 6, C, C, C, C, C, C, C, C, C, J > >,
|
||||
std::tuple<std::integral_constant<char, '0'>, Switch< 10, A, B, C, D, E, F, G, H, I, J > >
|
||||
>;
|
||||
|
||||
INSTANTIATE_TYPED_TEST_SUITE_P(traits, TestSwitch, SwitchTypes);
|
||||
|
||||
template<typename T>
|
||||
using is_bool = std::is_same<T, bool>;
|
||||
template<typename T>
|
||||
using is_char = std::is_same<T, char>;
|
||||
template<typename T>
|
||||
using is_string = std::is_same<T, std::string>;
|
||||
|
||||
char dispatch(traits::select::Case<>) { return '0'; }
|
||||
char dispatch(traits::select::Case<is_bool>) { return 'b'; }
|
||||
char dispatch(traits::select::Case<is_char>) { return 'c'; }
|
||||
char dispatch(traits::select::Case<is_string>) { return 's'; }
|
||||
|
||||
template < typename Type >
|
||||
char
|
||||
selectCase()
|
||||
{
|
||||
using Selection = traits::select::Select<Type, is_bool, is_char, is_string >;
|
||||
|
||||
return dispatch(Selection());
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
class Select : public ::testing::Test
|
||||
{
|
||||
};
|
||||
|
||||
TYPED_TEST_SUITE_P(Select);
|
||||
|
||||
TYPED_TEST_P(Select, Smoke)
|
||||
{
|
||||
char expected = std::tuple_element_t< 0, TypeParam >::value;
|
||||
char result = selectCase<std::tuple_element_t< 1, TypeParam > >();
|
||||
ASSERT_EQ(expected, result);
|
||||
}
|
||||
|
||||
REGISTER_TYPED_TEST_SUITE_P(Select, Smoke);
|
||||
|
||||
using SelectTypes = ::testing::Types<
|
||||
std::tuple<std::integral_constant<char, '0'>, double >,
|
||||
std::tuple<std::integral_constant<char, 'b'>, bool >,
|
||||
std::tuple<std::integral_constant<char, 'c'>, char >,
|
||||
std::tuple<std::integral_constant<char, 's'>, std::string >
|
||||
>;
|
||||
|
||||
INSTANTIATE_TYPED_TEST_SUITE_P(traits, Select, SelectTypes);
|
||||
|
||||
// clang-format on
|
Loading…
Reference in New Issue