Skip to content

Instantly share code, notes, and snippets.

@pyrrho
Last active May 9, 2017 20:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pyrrho/02df0fbb90c8acc776c2da43bd7628ad to your computer and use it in GitHub Desktop.
Save pyrrho/02df0fbb90c8acc776c2da43bd7628ad to your computer and use it in GitHub Desktop.
Subordinate Type Ctor Trait Test
#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