Check if there is a specialized method specialization

Say I have a template class similar to this:

template<typename TRequest, typename TResponse = void> class handler { private: static void validate_core(const TRequest& request); static TResponse process_core(const TRequest& request); public: static TResponse process(const TRequest& request) { if (validate_core is implemented) { log("begin validate"); validate_core(request); } return process_core(request); } }; 

process_core must be implemented for different types of TRequest / TResponse, and validate_core is optional, I want to call it if it has been implemented.

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.

+5
source share
2 answers

Cannot be checked at compile time if template specification is implemented. At best, you can create a link time error by declaring and instantiating an extern.

I realized that you want process implement a template template of a so-called template. If so, you can find an interesting use of CRTP. (Nota bene: I have a modified function signature to make this code compile)

 template<class Derived, typename TRequest, typename TResponse> class handler_base { private: //caution trickery static constexpr bool validate_core=false; public: static TResponse* process(const TRequest& request) { if constexpr (Derived::validate_core) { //log("begin validate"); Derived::validate_core(request); } return Derived::process_core(request); } }; template<typename TRequest, typename TResponse=void> class handler; class Req1; class Req2; class Resp1; class Resp2; template<> class handler<Req1,Resp1> : handler_base<handler<Req1,Resp1>,Req1,Resp1> { static void validate_core(const Req1& request); static Resp1* process_core(const Req1& request); friend handler_base; }; template<> class handler<Req2,Resp2> : handler_base<handler<Req2,Resp2>,Req2,Resp2> { friend handler_base; static Resp2* process_core(const Req2& request); }; //just to force compilation template Resp2* handler_base<handler<Req2,Resp2>,Req2,Resp2>::process(const Req2&); template Resp1* handler_base<handler<Req1,Resp1>,Req1,Resp1>::process(const Req1&); 
0
source

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 &) { /* do something */ } static void validate_core (long const &) { /* do something */ } // ... 

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 } 
-1
source

Source: https://habr.com/ru/post/1273969/


All Articles