Last active
May 9, 2017 20:28
-
-
Save pyrrho/02df0fbb90c8acc776c2da43bd7628ad to your computer and use it in GitHub Desktop.
Subordinate Type Ctor Trait Test
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <type_traits> | |
#include <cstdint> | |
#include <cstdio> | |
#include <cassert> | |
// Compile with clang, I'd recommend | |
// /usr/bin/clang++ -fsanitize=address,undefined \ | |
// -fno-omit-frame-pointer \ | |
// -fno-optimize-sibling-calls \ | |
// -O0 \ | |
// -std=gnu++14 \ | |
// -o test \ | |
// test.cpp | |
/** UTILITIES --------------------------------------------------------------- */ | |
/** Define this as `true` either here or in as a -D to demo the constexpr-ness | |
* of this solution. */ | |
#if !defined(CONSTEXPR_GO) | |
#define CONSTEXPR_GO false | |
#endif | |
#if CONSTEXPR_GO | |
# define PRINT(...) printf("%s:%-4u -- ", __FILE__, __LINE__); printf(__VA_ARGS__) | |
# define S_PRINT(...) | |
# define MAYBE_CONSTEXPR constexpr | |
# define MAYBE_STATIC_ASSERT(...) static_assert(__VA_ARGS__) | |
#else | |
# define PRINT(...) printf("%s:%-4u -- ", __FILE__, __LINE__); printf(__VA_ARGS__) | |
# define S_PRINT(...) printf("%s:%-4u -- ", __FILE__, __LINE__); printf(__VA_ARGS__) | |
# define MAYBE_CONSTEXPR | |
# define MAYBE_STATIC_ASSERT(...) | |
#endif | |
/** Print the Name of a Type! | |
* Heavily inspired by http://stackoverflow.com/questions/35941045. */ | |
#define PRINT_TYPE_NAME(TYPE) TypeNamePrinter<TYPE>::length, TypeNamePrinter<TYPE>::name | |
#define PRINT_TYPE_NAME_OF(TYPE) PRINT_TYPE_NAME(decltype(TYPE)) | |
#define Ftype "%.*s" | |
template<typename Type> | |
struct TypeNamePrinter { | |
static constexpr char const * _getName() { | |
char const * p = __PRETTY_FUNCTION__; | |
while (*(++p) != '='); // Skip p to the first `=`. | |
while (*(++p) == ' '); // Skip p past any spaces. | |
return p; | |
} | |
static constexpr int32_t const _getLength() { | |
char const * p = _getName(); | |
char const * p2 = p; | |
int count = 1; | |
// In _getName, we will have skipped past the first `[` (which is why | |
// count == 1). We now increment p2 until the matching `]` is found. | |
while (count > 0) { | |
++p2; | |
switch (*p2) { | |
case '[': { ++count; } break; | |
case ']': { --count; } break; | |
} | |
} | |
return (int32_t)(p2 - p); | |
} | |
// Making these `static constexpr` members ensures compile-time resolution. | |
static constexpr char const * name = _getName(); | |
static constexpr int32_t const length = _getLength(); | |
}; | |
/** IMPORTANT CODE ---------------------------------------------------------- */ | |
/** Enum of constructor traits used to partially specialize the Test class based | |
* on `T`s type_traits. */ | |
enum CtorTraits { | |
CTOR_TRAITS_BASE_CLASS = 0, | |
CTOR_TRAITS_COPYABLE = 2, | |
CTOR_TRAITS_TRIVIALLY_COPYABLE = 4, | |
}; | |
/** Templatized `using` declarations to wrap enable_if clauses, and keep this | |
* code DRY. */ | |
template <typename T> | |
using void_t_if_copy_constructible = typename | |
std::enable_if< | |
std::is_copy_constructible<T>::value | |
&& !std::is_trivially_copy_constructible<T>::value>::type; | |
template <typename T> | |
using void_t_if_trivially_copy_constructible = typename | |
std::enable_if< | |
std::is_trivially_copy_constructible<T>::value>::type; | |
/** Alias to std::integral_constant for compile-time `CtorTraits` values. */ | |
template<CtorTraits C> | |
using ctor_trait_constant = std::integral_constant<CtorTraits, C>; | |
/** ctor_trait<T> struct | |
* Uses the above enable_if wrappers to provide a compile-tome `CtorTraits` | |
* value based on T's type traits. */ | |
template <typename T, typename SFINAE = void> | |
struct ctor_trait : ctor_trait_constant<CTOR_TRAITS_BASE_CLASS> { }; | |
template <typename T> | |
struct ctor_trait<T, void_t_if_copy_constructible<T>> | |
: ctor_trait_constant<CTOR_TRAITS_COPYABLE> { }; | |
template <typename T> | |
struct ctor_trait<T, void_t_if_trivially_copy_constructible<T>> | |
: ctor_trait_constant<CTOR_TRAITS_TRIVIALLY_COPYABLE> { }; | |
/** Test demo class. | |
* Base implementation has all special ctors marked as deleted. | |
* Partial specializations will modify the state of these ctors, based on T's | |
* type traits. */ | |
template <typename T, typename SFINAE = void, CtorTraits = ctor_trait<T>::value> | |
class Test { | |
public: | |
T value; | |
constexpr Test() : value ( T{0} ) { | |
S_PRINT("Root :: Default ctor -- " Ftype "\n", PRINT_TYPE_NAME_OF(this)); | |
} | |
Test(Test const & other) = delete; | |
Test(Test && other) = delete; | |
Test & operator= (Test const & other) = delete; | |
Test & operator= (Test && other) = delete; | |
constexpr Test(T const & _value) : value ( _value ) { | |
S_PRINT("Root :: Value copy ctor -- " Ftype "\n", PRINT_TYPE_NAME_OF(this)); | |
} | |
constexpr Test(T && _value) : value ( std::move(_value) ) { | |
S_PRINT("Root :: Value move ctor -- " Ftype "\n", PRINT_TYPE_NAME_OF(this)); | |
} | |
constexpr bool yes() const { return true; } | |
}; | |
/** Non-constexpr copy and move constructible */ | |
template <typename T> | |
class Test<T, void_t_if_copy_constructible<T>, CTOR_TRAITS_COPYABLE> | |
: public Test<T, void, CTOR_TRAITS_BASE_CLASS> { | |
public: | |
using Test<T, void, CTOR_TRAITS_BASE_CLASS>::Test; | |
Test(Test const & other) : Test ( other.value ) { | |
PRINT("Copyable :: Exact copy ctor -- " Ftype "\n", PRINT_TYPE_NAME_OF(this)); | |
} | |
Test(Test && other) : Test ( std::move(other.value) ) { | |
PRINT("Copyable :: Exact move ctor -- " Ftype "\n", PRINT_TYPE_NAME_OF(this)); | |
} | |
Test& operator= (Test const & other) { | |
PRINT("Copyable :: Exact copy asignment -- " Ftype "\n", PRINT_TYPE_NAME_OF(this)); | |
this->value = other.value; | |
return *this; | |
} | |
Test& operator= (Test && other ) { | |
PRINT("Copyable :: Exact move asignment -- " Ftype "\n", PRINT_TYPE_NAME_OF(this)); | |
this->value = std::move(other.value); | |
return *this; | |
} | |
}; | |
/** constexpr copy and move constructible */ | |
template <typename T> | |
class Test<T, void_t_if_trivially_copy_constructible<T>, CTOR_TRAITS_TRIVIALLY_COPYABLE> | |
: public Test<T, void, CTOR_TRAITS_BASE_CLASS> { | |
public: | |
using Test<T, void, CTOR_TRAITS_BASE_CLASS>::Test; | |
constexpr Test(Test const & other) : Test ( other.value ) { | |
S_PRINT("Constexpr :: Exact copy ctor -- " Ftype "\n", PRINT_TYPE_NAME_OF(this)); | |
} | |
constexpr Test(Test && other) : Test ( std::move(other.value) ) { | |
S_PRINT("Constexpr :: Exact move ctor -- " Ftype "\n", PRINT_TYPE_NAME_OF(this)); | |
} | |
constexpr Test& operator= (Test const & other) { | |
S_PRINT("Constexpr :: Exact copy asignment -- " Ftype "\n", PRINT_TYPE_NAME_OF(this)); | |
this->value = other.value; | |
return *this; | |
} | |
constexpr Test& operator= (Test && other ) { | |
S_PRINT("Constexpr :: Exact move asignment -- " Ftype "\n", PRINT_TYPE_NAME_OF(this)); | |
this->value = std::move(other.value); | |
return *this; | |
} | |
}; | |
/** DEMO TYPES -------------------------------------------------------------- */ | |
struct Trivial { | |
int a; | |
// constexpr Trivial() : a ( int{0} ) { } | |
// constexpr Trivial(int _a) : a ( _a ) { } | |
// constexpr Trivial(Trivial const & other) : a ( other.a ) { } | |
// constexpr Trivial(Trivial && other) : a ( std::move(other.a) ) { } | |
// constexpr Trivial& operator= (Trivial const & other) { a = other.a; return *this; } | |
// constexpr Trivial& operator= (Trivial && other) { a = std::move(other.a); return *this; } | |
}; | |
static_assert(std::is_trivially_constructible<Trivial>::value, "Yes good"); | |
static_assert(std::is_trivially_default_constructible<Trivial>::value, "Yes good"); | |
static_assert(std::is_trivially_copy_constructible<Trivial>::value, "Yes good"); | |
static_assert(std::is_trivially_move_constructible<Trivial>::value, "Yes good"); | |
struct Copyable { | |
int a; | |
constexpr Copyable(int _a) : a ( _a ) { } | |
constexpr Copyable(Copyable const & other) : a ( other.a ) { } | |
constexpr Copyable(Copyable && other) : a ( std::move(other.a) ) { } | |
constexpr Copyable& operator= (Copyable const & other) { a = other.a; return *this; } | |
constexpr Copyable& operator= (Copyable && other) { a = std::move(other.a); return *this; } | |
}; | |
static_assert(std::is_copy_constructible<Copyable>::value, "Yes good"); | |
static_assert(!std::is_trivially_copy_constructible<Copyable>::value, "Yes good"); | |
static_assert(std::is_move_constructible<Copyable>::value, "Yes good"); | |
static_assert(!std::is_trivially_move_constructible<Copyable>::value, "Yes good"); | |
static_assert(std::is_copy_assignable<Copyable>::value, "Yes good"); | |
static_assert(!std::is_trivially_copy_assignable<Copyable>::value, "Yes good"); | |
static_assert(std::is_move_assignable<Copyable>::value, "Yes good"); | |
static_assert(!std::is_trivially_move_assignable<Copyable>::value, "Yes good"); | |
struct NotCopyable { | |
int a; | |
NotCopyable(int _a) : a ( _a ) { } | |
NotCopyable(NotCopyable const & other) = delete; | |
NotCopyable(NotCopyable && other) : a ( std::move(other.a) ) { } | |
NotCopyable& operator= (NotCopyable const & other) = delete; | |
NotCopyable& operator= (NotCopyable && other) { a = std::move(other.a); return *this; } | |
}; | |
static_assert(!std::is_copy_constructible<NotCopyable>::value, "Yes good"); | |
static_assert(std::is_move_constructible<Copyable>::value, "Yes good"); | |
static_assert(!std::is_trivially_move_constructible<Copyable>::value, "Yes good"); | |
static_assert(std::is_copy_assignable<Copyable>::value, "wait... what?"); | |
static_assert(!std::is_trivially_copy_assignable<Copyable>::value, "Yes good"); | |
static_assert(std::is_move_assignable<Copyable>::value, "Yes good"); | |
static_assert(!std::is_trivially_move_assignable<Copyable>::value, "Yes good"); | |
/** MAIN =====--------------------------------------------------------------- */ | |
int main() { | |
PRINT("ctor_trait<NotCopyable>::value " Ftype "(%d)\n", PRINT_TYPE_NAME_OF(ctor_trait<NotCopyable>::value), ctor_trait<NotCopyable>::value); | |
PRINT("ctor_trait<Copyable>::value " Ftype "(%d)\n", PRINT_TYPE_NAME_OF(ctor_trait<Copyable>::value), ctor_trait<Copyable>::value); | |
PRINT("ctor_trait<Trivial>::value " Ftype "(%d)\n", PRINT_TYPE_NAME_OF(ctor_trait<Trivial>::value), ctor_trait<Trivial>::value); | |
PRINT("Test<NotCopyable> " Ftype "\n", PRINT_TYPE_NAME(Test<NotCopyable>)); | |
PRINT("Test<Copyable> " Ftype "\n", PRINT_TYPE_NAME(Test<Copyable>)); | |
PRINT("Test<Trivial> " Ftype "\n", PRINT_TYPE_NAME(Test<Trivial>)); | |
Test<NotCopyable> a {{ 4 }}; | |
// Test<NotCopyable> a2 { a }; // call to deleted constructor of 'Test<NotCopyable>' | |
Test<Copyable> b {{ 4 }}; | |
Test<Copyable> b2 { b }; | |
MAYBE_CONSTEXPR Test<Trivial> c {{ 4 }}; | |
MAYBE_CONSTEXPR Test<Trivial> c2 { c }; | |
assert(a.yes() == true); | |
// assert(a2.yes() == true); | |
assert(b.yes() == true); | |
assert(b2.yes() == true); | |
assert(c.yes() == true); | |
assert(c2.yes() == true); | |
MAYBE_STATIC_ASSERT(c.value.a == 4, "Yes, good."); | |
MAYBE_STATIC_ASSERT(c2.value.a == 4, "Yes, good."); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment