/*
 * Copyright (C) 2020-2021 Aloysius Indrayanto
 *
 * This file is part of the eCxx library, see LICENSE file for the license details.
 */


#ifndef ECXX_CTL_HAVE_MEMBER_CHECK_H
#define ECXX_CTL_HAVE_MEMBER_CHECK_H


//
// Check for the existence of member function/variable/constant
//
#define ECXX_GENERATE_HAVE_MEMBER(MN)                                               \
    template <typename T>                                                           \
    struct HasMember_##MN {                                                         \
        using Y = long;                                                             \
        using N = char;                                                             \
        struct B        { int MN; };                                                \
        struct D : T, B { };                                                        \
        template <typename U> static B& t( decltype(U::MN)* );                      \
        template <typename U> static Y& t( U* );                                    \
        static constexpr bool Value = ( sizeof( t<D>(nullptr) ) == sizeof(Y) );     \
    };                                                                              \
    template <typename T>                                                           \
    struct has_member_##MN : eCxx::integralConstant<bool, HasMember_##MN<T>::Value> \
    {}

#define ECXX_HAVE_MEMBER(CN, MN) static_assert(HasMember_##MN<CN>::Value, "class '" #CN "' does not have member function/variable/constant '" #MN "'")

#define ECXX_CHECK_HAVE_MEMBER(CN, MN) ECXX_GENERATE_HAVE_MEMBER(MN); ECXX_HAVE_MEMBER(CN, MN)


//
// Check for the existence of member type
//
#define ECXX_GENERATE_HAVE_MEMBER_TYPE(MN)                                                   \
    template <typename T>                                                                    \
    struct HasMemberType_##MN {                                                              \
        using Y = long;                                                                      \
        using N = char;                                                                      \
        struct B        { struct MN {}; };                                                   \
        struct D : T, B { };                                                                 \
        template <typename U> static N& t( typename U::MN* );                                \
        template <typename U> static Y& t( U* );                                             \
        static constexpr bool Value = ( sizeof( t<D>(nullptr) ) == sizeof(Y) );              \
    };                                                                                       \
                                                                                             \
    template <typename T>                                                                    \
    struct has_member_type_##MN : eCxx::integralConstant<bool, HasMemberType_##MN<T>::Value> \
    {}

#define ECXX_HAVE_MEMBER_TYPE(CN, MN) static_assert(HasMemberType_##MN<CN>::Value, "class '" #CN "' does not have member type '" #MN "'")

#define ECXX_CHECK_HAVE_MEMBER_TYPE(CN, MN) ECXX_GENERATE_HAVE_MEMBER_TYPE(MN); ECXX_HAVE_MEMBER_TYPE(CN, MN)


//
// Check for the value of member constant
//
#define ECXX_CHECK_MEMBER_CVL(CN, MN, RQ) static_assert(CN::MN RQ, "class '" #CN "' member constant '" #MN "' is not '" #RQ "'")


#endif
