// BEGIN TYPE_CHECK.H
// https://w...content-available-to-author-only...x.com/s/dkft2pcur5y76aq/type_check.h
#ifndef TYPE_CHECK_H
#define TYPE_CHECK_H
//------------------------------------------------------------------------------
// TYPE_CHECK()
/** Type Check.
* Test type `T_` for the presence or absence of type `T_::TYPE_`.
* This Provides a convenient frontend for a SFINAE (substitution failure is
* not an error) idiom.
*
* Defines a `struct NAME_` which is instantiated with `HAS_TYPE_` as its body
* for types `T_` when `T_::TYPE_` exists, and with `NO_TYPE_` as its body for
* types `T_` which don't have a `T_::TYPE_`. `HAS_TYPE_` and `NO_TYPE_` can
* contain multiple declarations, which should be seperated by `;` (as in a
* normal `struct` body).
*
* A trailing `;` is provided for the last declaration by the macro, so this can
* be omitted at the point of use. Unless the flag TYPE_CHECK_NO_VA_ARGS is
* #defined, then `HAS_TYPE_` and `NO_TYPE_` can be parenthesized if necessary
* (if they contain unparenthesized commas, for example).
* \sa TYPE_CHECK_FRIEND()
*/
#define TYPE_CHECK(NAME_, T_, TYPE_, HAS_TYPE_, NO_TYPE_) \
template<typename T_, typename = void>struct NAME_ { \
tc_STRIP_PARENS(NO_TYPE_); \
}; \
template<typename T_> \
struct NAME_<T_, \
typename ::tc_::HasType<typename T_::TYPE_>::Type> { \
tc_STRIP_PARENS(HAS_TYPE_); \
}
//------------------------------------------------------------------------------
// TYPE_CHECK_FRIEND()
/** Type Check Friend.
* This macro can be placed in a type declaration, to indicate that the body of
* the check defined as `NAME_` can acces this type's protected/private members.
* \sa TYPE_CHECK()
*/
#define TYPE_CHECK_FRIEND(NAME_) \
template<typename, typename>friend struct NAME_;
//------------------------------------------------------------------------------
// Internal details follow...
#ifndef TYPE_CHECK_NO_VA_ARGS
// Detect and strip parentheses from __VA_ARGS__.
#define tc_STRIP_PARENS(...) \
tc_IF( tc_HAS_PARENS(__VA_ARGS__), \
tc_PASSTHROUGH(tc_PASSTHROUGH __VA_ARGS__), \
tc_PASSTHROUGH(__VA_ARGS__) )
#define tc_IF(COND_, TRUE_, FALSE_) \
tc_JOIN2(tc_DO_IF_, tc_NEQ(COND_, 0)) \
(tc_PASSTHROUGH(TRUE_), tc_PASSTHROUGH(FALSE_))
#define tc_DO_IF_0(T_, F_) F_
#define tc_DO_IF_1(T_, F_) T_
#define tc_HAS_PARENS(...) \
tc_NEQ( tc_COMMA_COUNT(__VA_ARGS__), \
tc_COMMA_COUNT(tc_COMMA_IF_PARENS __VA_ARGS__) )
#define tc_NEQ(A_, B_) tc_DO_NEQ(A_, B_)
#define tc_DO_NEQ(A_, B_) tc_DO_NEQ_##A_##B_
#define tc_DO_NEQ_00 0
#define tc_DO_NEQ_01 1
#define tc_DO_NEQ_10 1
#define tc_DO_NEQ_11 0
#define tc_JOIN2(A_, B_) tc_DO_JOIN2(A_, B_)
#define tc_DO_JOIN2(A_, B_) A_##B_
#define tc_PASSTHROUGH(...) __VA_ARGS__
#define tc_COMMA_IF_PARENS(...) ,
// counts commas in (...)
#define tc_COMMA_COUNT(...) tc_LAST_ARG(__VA_ARGS__, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define tc_LAST_ARG(a_, b_, c_, d_, e_, f_, g_, h_, i_, j_, k_, l_, m_, n_, o_, p_, q_, r_, s_, t_, u_, v_, w_, x_, y_, z_, ...) z_
#else//TYPE_CHECK_NO_VA_ARGS
// Default case for compilers with no __VA_ARGS__ support
#define tc_STRIP_PARENS(X_) X_
#endif//TYPE_CHECK_NO_VA_ARGS
//------------------------------------------------------------------------------
//
namespace tc_ {
template<typename T> struct HasType { typedef void Type; };
} // namespace type_check_
#endif//TYPE_CHECK_H
// END TYPE_CHECK.H
// BEGIN MAIN.CPP
// https://w...content-available-to-author-only...x.com/s/5mg4190ao3o9tgd/main.cpp
//#include "type_check.h" // it's right above this!
#include <stdio.h>
#include <stdlib.h>
const char *strbool(bool b) { return b ? "true" : "false"; }
// testing structs
struct TestingF {}; // No TestingF::TypeToCheck exists
struct TestingT { typedef void TypeToCheck; };
//------------------------------------------------------------------------------
// Test 1
// Simple true/false check.
TYPE_CHECK(Test1Check, T, TypeToCheck,
static const bool VALUE = true, // Test1Check body when T::TypeToCheck exists
static const bool VALUE = false); //Test1Check body default
bool test1() {
bool f = Test1Check<TestingF>::VALUE; // -> false
bool t = Test1Check<TestingT>::VALUE; // -> true
printf("t = %s, f = %s\n", strbool(t), strbool(f));
return !f && t;
}
#ifndef TYPE_CHECK_NO_VA_ARGS
//------------------------------------------------------------------------------
// Test 2
// Parens around the 'body' clauses to handle the commas in the int declarations.
TYPE_CHECK( Test2Check, T, TypeToCheck,
(static const int a = 10, b = 20, c = 30),
(static const int a = 3, b = 4, c = 5) );
bool test2() {
typedef Test2Check<TestingF> T2F;
typedef Test2Check<TestingT> T2T;
int sum_f = T2F::a + T2F::b + T2F::c;
int sum_t = T2T::a + T2T::b + T2T::c;
printf("sum_f = %u, sum_t = %u\n", sum_f, sum_t);
return (12 == sum_f) && (60 == sum_t);
}
#endif//TYPE_CHECK_NO_VA_ARGS
//------------------------------------------------------------------------------
// Test 3
// Select functions.
TYPE_CHECK( Test3Check, T, TypeToCheck,
static int test(int &a) { return ++a; }, // trailing comma needed here!
static int test(int &a) { return --a; } );
bool test3() {
int q =10;
typedef Test3Check<TestingF> T3F;
typedef Test3Check<TestingT> T3T;
int u = T3F::test(q);
int v = T3F::test(q);
int w = T3F::test(q);
int x = T3T::test(q);
int y = T3T::test(q);
int z = T3T::test(q);
printf("%u, %u, %u, %u, %u, %u\n", u, v, w, x, y, z);
return
(9 == u) && (8 == v) && (7 == w) &&
(8 == x) && (9 == y) && (10 == z) &&
true;
}
//------------------------------------------------------------------------------
// Test 4
// private implementations and TYPE_CHECK_FRIEND()
class Test4ExampleF {};
class Test4ExampleT {
typedef int TypeToCheck;
TYPE_CHECK_FRIEND(Test4Check); // Give Test4Check access to internals
};
TYPE_CHECK(Test4Check, T, TypeToCheck,
static const bool VALUE = true, // Test4Check body when T::TypeToCheck exists
static const bool VALUE = false); //Test4Check body default
bool test4() {
bool f = Test4Check<Test4ExampleF>::VALUE; // -> false
bool t = Test4Check<Test4ExampleT>::VALUE; // -> true
printf("t = %s, f = %s\n", strbool(t), strbool(f));
return !f && t;
}
//------------------------------------------------------------------------------
// Test 5
// Something a bit more like 'real-world' usage.
// Use the HAS_SPECIAL_METHOD() macro in a type declaration to arrange for
// Test5Check<X>::result() to call the named method...
#define HAS_SPECIAL_METHOD(NAME_) \
typedef void HasSpecialMethod; /* the tag */\
int do_special_() { return NAME_(); } /* indirect call to NAME_() */
struct Test5Default {
Test5Default(int d): data(d) {}
int data;
};
struct Test5Special1 {
Test5Special1(int d): data(d) {}
int data;
int special1() { return data * 4; }
HAS_SPECIAL_METHOD(special1);
};
struct Test5Special2 {
Test5Special2(int d): data(d) {}
int data;
int special2() { return data - 100; }
HAS_SPECIAL_METHOD(special2);
};
// use X instead of T for fun...
TYPE_CHECK(Test5Check, X, HasSpecialMethod,
static int result(X &x) {
return x.do_special_();
},
static int result(X &x) { return x.data; } );
template<typename X>
int test5result(X &x) { return Test5Check<X>::result(x); }
bool test5() {
Test5Default def(100);
Test5Special1 sp1(100);
Test5Special2 sp2(100);
printf("%d, %d, %d\n",
test5result(def),
test5result(sp1),
test5result(sp2));
return
100 == test5result(def) &&
400 == test5result(sp1) &&
0 == test5result(sp2) &&
true;
}
//------------------------------------------------------------------------------
int main() {
bool ok =
test1() &&
#ifndef TYPE_CHECK_NO_VA_ARGS
test2() &&
#endif//TYPE_CHECK_NO_VA_ARGS
test3() &&
test4() &&
test5() &&
true;
printf("\nAll tests passed: %s\n", strbool(ok));
return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
// END MAIN.CPP