Sergey Kolesnik
Sergey Kolesnik

Reputation: 3640

Partial class template specialization based on a condition?

I generally don't see, how it could be implemented.

I have an incomplete class template used to "override" some methods.
This one is a working example:

template<typename...>
struct make_completion;

struct ServiceTraits
{
    using mode_type = ModeType;
    using warning_type = Waring;
    using error_type = Error;
    using iteration_result_type = IterationResult;

    using steering_type = Steering;

    using sync_tag_t = decltype(asio::use_future);
};

template<typename SignatureT>
struct make_completion<ServiceTraits, SignatureT>
{
    template<typename CompletionToken>
    auto operator()(ServiceTraits, CompletionToken &&completionToken)
    {
        using result_type = asio::async_result<std::decay_t<CompletionToken>, SignatureT>;
        using completion_handler = typename result_type::completion_handler_type;

        completion_handler handler{ std::forward<CompletionToken>(completionToken) };
        result_type result{ handler };

        return Completion<result_type, completion_handler>{ std::move(result), std::move(handler) };
    }
};

I want to assemble traits class via inheritance, so, for instance, I would have several traits classes and then arbitrarily combine them.

For example:

template<typename ResultT, typename HandlerT>
struct completion
{
   using result_type = ResultT;
   using handler_type = HandlerT;
   result_type result;
   handler_type handler;
};

// this would be inherited from
struct asio_traits
{
   using sync_tag_t = decltype(asio::use_future);
};

The problem is, I can not use something like std::enable_if in a specialization.
This will not compile:

template<typename DerivedTraits,
         typename SignatureT,
         typename = std::enable_if_t<std::is_base_of_v<asio_traits, DerivedTraits>>>
struct eos::method::make_completion<DerivedTraits, SignatureT>
{
   // impl here...
};

Is it possible to somehow use a condition for class template specialization?

Upvotes: 1

Views: 317

Answers (1)

Igor Tandetnik
Igor Tandetnik

Reputation: 52471

Something along these lines, perhaps:

template <typename...>
struct make_completion_tag;

template <typename Tag, typename...>
struct make_completion_impl;

template <typename... Ts>
struct make_completion : public make_completion_impl<
    typename make_completion_tag<Ts...>::type,
    Ts...>
{};

struct asio_traits_based_tag;
template <typename DerivedTraits, typename SignatureT>
struct make_completion_impl<asio_traits_based_tag, DerivedTraits, SignatureT> {
  // Your specialization here
};

Now it's just a matter of getting make_completion_tag::type to return the correct tag. E.g.

template <typename DerivedTraits, typename SignatureT>
struct make_completion_tag<DerivedTraits, SignatureT> {
  using type = std::conditional_t<
      std::is_base_of_v<asio_traits, DerivedTraits>,
      asio_traits_based_tag, void>;
};

EDIT: a slightly different approach that exposes an external customization point:

template <typename Tag, typename...>
struct make_completion_impl;

void make_completion_selector(...);

template <typename... Ts>
struct make_completion : public make_completion_impl<
    decltype(make_completion_selector(static_cast<Ts*>(nullptr)...)),
    Ts...>
{};

Now the user has two options. If the types allow, they could specialize make_completion directly:

template <AnotherType>
struct make_completion<SpecificType, AnotherType> {
  // Your specialization here
};

In a more complicated case, they can declare a suitably constrained overload of make_completion_selector returning a tag type, and specialize make_completion_impl on that tag:

struct asio_traits_based_tag;
template <typename DerivedTraits, typename SignatureT>
struct make_completion_impl<asio_traits_based_tag, DerivedTraits, SignatureT> {
  // Your specialization here
};

template <typename DerivedTraits, typename SignatureT>
std::enable_if_t<std::is_base_of_v<asio_traits, DerivedTraits>,
                 asio_traits_based_tag>
make_completion_selector(DerivedTraits*, SignatureT*);

Upvotes: 1

Related Questions