Reputation: 3640
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
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