Detect.h

// Detect.h
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2022

#pragma once
#ifndef Ceda_cxUtils_Detect_H
#define Ceda_cxUtils_Detect_H

#include <type_traits>

// See https://people.eecs.berkeley.edu/~brock/blog/detection_idiom.php
//     https://en.cppreference.com/w/cpp/experimental/is_detected
//     https://stackoverflow.com/questions/66078754/c17-how-to-test-class-has-a-member-variable

namespace ceda
{
    // This is from a proposal for C++, made redundant by C++20 requires
    /*
    template <typename...>
    using void_t = void;

    // Primary template handles all types not supporting the operation.
    template <typename, template <typename> class, typename = void_t<>>
    struct detect : std::false_type {};

    // Specialization recognizes/validates only types supporting the archetype.
    template <typename T, template <typename> class Op>
    struct detect<T, Op, void_t<Op<T>>> : std::true_type {};
    */

    struct nonesuch 
    {
        ~nonesuch() = delete;
        nonesuch(nonesuch const&) = delete;
        void operator=(nonesuch const&) = delete;
    };    
    
    namespace detail 
    {
        template <class Default, class AlwaysVoid, template<class...> class Op, class... Args>
        struct detector 
        {
            using value_t = std::false_type;
            using type = Default;
        };
 
        template <class Default, template<class...> class Op, class... Args>
        struct detector<Default, std::void_t<Op<Args...>>, Op, Args...> 
        {
            using value_t = std::true_type;
            using type = Op<Args...>;
        };
    }
 
    template <template<class...> class Op, class... Args>
    using is_detected = typename detail::detector<nonesuch, void, Op, Args...>::value_t;
 
    template <template<class...> class Op, class... Args>
    using detected_t = typename detail::detector<nonesuch, void, Op, Args...>::type;
 
    template <class Default, template<class...> class Op, class... Args>
    using detected_or = detail::detector<Default, void, Op, Args...>;

    // test for existence of static_cast<To>(From)
    template <typename To, typename From> using has_static_cast_t = decltype(static_cast<To>(std::declval<From>()));
    template <typename To, typename From> using has_static_cast = is_detected<has_static_cast_t, To, From>;

    // implement implicit_cast<T>(x)
    namespace detail 
    {
        template<class T> struct icast_identity { using type = T; };
    }
    template <typename T>
    inline T implicit_cast (typename detail::icast_identity<T>::type x) 
    {
        return x;
    }
    
    // test for existence of implicit_cast<To>(From)
    template <typename To, typename From> using has_implicit_cast_t = decltype(implicit_cast<To>(std::declval<From>()));
    template <typename To, typename From> using has_implicit_cast = is_detected<has_implicit_cast_t, To, From>;

    // test for existence of T1==T2
    template <typename T1, typename T2> using compareEqual_t = decltype(std::declval<T1 const &>() == std::declval<T2 const &>());
    //template <typename T1, typename T2 = T1> using has_compare_equal = is_detected<compareEqual_t, T1, T2>;
    template <typename T1, typename T2 = T1>
    struct has_compare_equal
    {
        constexpr static bool value = is_detected<compareEqual_t, T1, T2>::value;
    };

    // test for existence of T1<T2
    template <typename T1, typename T2> using compareLess_t = decltype(std::declval<T1 const &>() < std::declval<T2 const &>());
    //template <typename T1, typename T2 = T1> using has_compare_less = is_detected<compareLess_t, T1, T2>;
    template <typename T1, typename T2 = T1>
    struct has_compare_less
    {
        constexpr static bool value = is_detected<compareLess_t, T1, T2>::value;
    };
}

#endif // include guard