Currently, my workaround provides an empty default implementation for validate_core, if it does not specialize, then an empty method is called. I want to know if there is a better way.
It seems to me in a very good way.
Remember to understand your requirements, but I suppose you could remove the validate_core() template version
template <typename TRq = TRequest> static void validate_core (TRequest const &) = delete;
and include one or more non-standard specializations inside the body of the handler
static void validate_core (int const &) { } static void validate_core (long const &) { }
You can then add type attributes to the handler body to determine if it has a specialization for validate_core()
template <typename, typename = void> struct withValidateCore : public std::false_type { }; template <typename T> struct withValidateCore<T, decltype(validate_core(std::declval<T>()))> : public std::true_type { };
If you can use C ++ 17, this method can be interesting because it is available if constexpr () , so something like [maybe sorry not tested] should be possible
static TResponse process (TRequest const & request) { if constexpr ( withValidateCore<TRequest>::value ) { log("begin validate"); validate_core(request); } return process_core(request); }
But you noted C ++ 14, so if constexpr () not available, and the best way I can see withValidateCore is to implement two different versions (disabled / disabled SFINAE) process() ; sort of
template <typename TRq = TRequest> static std::enable_if_t<(true == withValidateCore<TRq>{}) && (true == std::is_same<TRq, TRequest>{}), TResponse> process (TRequest const & request) { validate_core(request); return process_core(request); } template <typename TRq = TRequest> static std::enable_if_t<(false == withValidateCore<TRq>{}) && (true == std::is_same<TRq, TRequest>{}), TResponse> process (TRequest const & request) { return process_core(request); }
Otherwise (maybe a little better) something based on a method
private: static TResponse process (TRequest const & request, std::true_type const &) { validate_core(request); return process_core(request); } static TResponse process (TRequest const & request, std::false_type const &) { return process_core(request); } public: static TResponse process (TRequest const & request) { return process(request, withValidateCore<TRequest>{}); }
IMHO this is the best (in C ++ 14) solution based on an empty generic version of validate_core() .
Anyway, a complete working example is given
#include <iostream> #include <type_traits> template <typename TRequest, typename TResponse = void> class handler { private: template <typename TRq = TRequest> static void validate_core (TRequest const &) = delete; static void validate_core (int const &) { std::cout << "- validate_core() int case" << std::endl; } static void validate_core (long const &) { std::cout << "- validate_core() long case" << std::endl; } static TResponse process_core (const TRequest &) { return TResponse(); } template <typename, typename = void> struct withValidateCore : public std::false_type { }; template <typename T> struct withValidateCore<T, decltype(validate_core(std::declval<T>()))> : public std::true_type { }; public: template <typename TRq = TRequest> static std::enable_if_t<(true == withValidateCore<TRq>{}) && (true == std::is_same<TRq, TRequest>{}), TResponse> process (TRequest const & request) { validate_core(request); return process_core(request); } template <typename TRq = TRequest> static std::enable_if_t<(false == withValidateCore<TRq>{}) && (true == std::is_same<TRq, TRequest>{}), TResponse> process (TRequest const & request) { return process_core(request); } }; int main() { handler<int>::process(0); // print - validate_core() int case handler<int, long>::process(0); // print - validate_core() int case handler<long>::process(0); // print - validate_core() long case handler<char>::process(0); // no print }