Хитрость с шаблонами заключается в том, что заменой должно быть имя или значение реального типа, а не membername
. Таким образом, этот подход всегда будет иметь некоторый шаблон.
Макросы не имеют этого ограничения, но они плохо подходят, потому что вариационные макросы не могут быть рекурсивными. НО, как и в старых шаблонах C ++ 98, вы все равно можете принимать решения «до N». Я взял трещину при создании до 4 членов проверки. Вы можете растянуть этот шаблон, если вам нужно больше.
#include <type_traits>
#include <utility>
#define CHECK_MEMBER( type, member, member_type ) \
template <typename _T, typename = void> \
struct __has_##type##member : std::false_type { }; \
template <typename _T> \
struct __has_##type##member<_T, std::enable_if_t< std::is_same_v<member_type*, decltype( &_T::member )>>> : std::true_type { }; \
static_assert(!__has_##type##member<type>(), "forbidden " #member_type " member '" #member "' in type '" #type "'");
#define _CHECK_MEMBER_1( _1 ) static_assert(false, "Expected at least 3 arguments, found 1")
#define _CHECK_MEMBER_2( _1, _2 ) static_assert(false, "Expected at least 3 arguments, found 2")
#define _CHECK_MEMBER_3( _1, _2, _3 ) CHECK_MEMBER(_1, _2, _3)
#define _CHECK_MEMBER_4( _1, _2, _3, _4 ) static_assert(false, "Expected odd number of arguments, found 4")
#define _CHECK_MEMBER_5( _1, _2, _3, _4, _5 ) CHECK_MEMBER(_1, _2, _3) CHECK_MEMBER(_1, _4, _5)
#define _CHECK_MEMBER_6( _1, _2, _3, _4, _5, _6 ) static_assert(false, "Expected odd number of arguments, found 6")
#define _CHECK_MEMBER_7( _1, _2, _3, _4, _5, _6, _7 ) CHECK_MEMBER(_1, _2, _3) CHECK_MEMBER(_1, _4, _5) CHECK_MEMBER(_1, _6, _7)
#define _CHECK_MEMBER_8( _1, _2, _3, _4, _5, _6, _7, _8 ) static_assert(false, "Expected odd number of arguments, found 8")
#define _CHECK_MEMBER_9( _1, _2, _3, _4, _5, _6, _7, _8, _9 ) CHECK_MEMBER(_1, _2, _3) CHECK_MEMBER(_1, _4, _5) CHECK_MEMBER(_1, _6, _7) CHECK_MEMBER(_1, _8, _9)
#define _GET_CHECK_MEMBER(_1,_2,_3,_4,_5,_6,_7,_8,_9,NAME,...) NAME
#define CHECK_MEMBERS(...) _GET_CHECK_MEMBER(__VA_ARGS__, _CHECK_MEMBER_9, _CHECK_MEMBER_8, _CHECK_MEMBER_7, _CHECK_MEMBER_6, _CHECK_MEMBER_5, _CHECK_MEMBER_4, _CHECK_MEMBER_3, _CHECK_MEMBER_2, _CHECK_MEMBER_1)(__VA_ARGS__)
Использование:
struct BadType { static int bad1; static bool* bad2; static double** bad3; static double bad4; };
struct OkNotStaticType { int bad1; bool* bad2; double** bad3; double bad4; };
struct OkDifferentType { double bad1; void* bad2; char* bad3; float bad4; };
struct OkUnrelatedType { bool good; };
#define CHECK_FORBIDDEN(T) CHECK_MEMBERS(T, bad1, int, bad2, bool*, bad3, double**, bad4, double)
CHECK_FORBIDDEN(BadType) // "forbidden int member 'bad1' in type 'BadType'"
// "forbidden bool* member 'bad2' in type 'BadType'"
// "forbidden double** member 'bad3' in type 'BadType'"
// "forbidden double member 'bad4' in type 'BadType'"
CHECK_FORBIDDEN(OkNotStaticType)
CHECK_FORBIDDEN(OkDifferentType)
CHECK_FORBIDDEN(OkUnrelatedType)
Пример:
https://godbolt.org/z/-bpbTg