Date: Thu, 10 Sep 2020 11:30:05 +0200
Hi everybody.
I've been following the work on P1858
<http://open-std.org/JTC1/SC22/WG21/docs/papers/2020/p1858r2.html> and just
yesterday it occurred to me that it'd be useful to do something like this:
template <typename... Ts>
struct Interface
{
rtype<Ts> func(ptype<Ts>) *...* = 0;
};
template <typename... Ts>
struct Implementation: Interface<Ts...>
{
rtype<Ts> func(ptype<Ts> p) override
{
return impl<Ts>(p);
} *...*
};
where rtype<>, ptype<> and impl<> are fictional templates with
exposition-only purpose.
At the moment such a construct is possible via recursive inheritance, with
much added boilerplate and unneeded RTTI information being written out in
the resulting binary (for the intermediate, not used types). Following is
a possible implementation of the above, given the current tools at our
disposal.
template <typename... Ts>
struct Interface;
template <>
struct Interface<>
{ };
template <typename T>
struct Interface<T>
{
virtual rtype<T> func(ptype<T>) = 0;
};
template <typename T, typename... Ts>
struct Interface<T, Ts...>: Interface<Ts...>
{
using Interface<Ts...>::func;
virtual rtype<T> func(ptype<T>) = 0;
};
namespace detail {
template <typename Interface, typename... Ts>
struct Implementation;
template <typename Interface>
struct Implementation<Interface>: Interface
{ };
template <typename Interface, typename T>
struct Implementation<Interface, T>: Interface
{
using Interface::func;
rtype<T> func(ptype<T> p) override
{
return impl<T>(p);
}
};
template <typename Interface, typename T, typename... Ts>
struct Implementation<Interface, T, Ts...>:
Implementation<Interface, Ts...>
{
using Implementation<Interface, Ts...>::func;
rtype<T> func(ptype<T> p) override
{
return impl<T>(p);
}
};
}
template <typename... Ts>
using Implementation = detail::Implementation<Interface<Ts...>, Ts...>;
An alternative version of the above can be implemented with the help of
std::variant, but with the limitation that all rtype<Ts>... must be the
same, and with the speed penalty that a sub optimized variant can incur.
Here's a possible implementation:
template <typename... Ts>
struct Interface
{
using variant_type = std::variant<ptype<Ts>...>;
using return_type = std::tuple_element_t<0, std::tuple<rtype<Ts>...>>;
static_assert(std::conjunction_v<std::is_same<return_type,
rtype<Ts>>...>, "return types must be the same for all functions.
Sigh.");
virtual return_type func(variant_type const &) = 0;
};
template <typename... Ts>
struct Implementation: Interface<Ts...>
{
using Interface<Ts...>::variant_type;
using Interface<Ts...>::return_type;
return_type func(variant_type const &v) override
{
return std::visit([](auto p){ return impl<decltype(p)>(); }, v);
}
};
template <>
struct Interface<>
{ };
template <>
struct Implementation<>: Interface<>
{ };
At the moment I'm using the std::variant approach in some production code,
for it's less verbose than the recursive approach and I can cope with its
limitations, but I believe it'd be nice to use the generalized pack syntax
for that.
All the above considered, I'd therefore like to suggest an amendment to
P1858 <http://open-std.org/JTC1/SC22/WG21/docs/papers/2020/p1858r2.html> as
described in the first code snippet.
Fabio Alemagna
I've been following the work on P1858
<http://open-std.org/JTC1/SC22/WG21/docs/papers/2020/p1858r2.html> and just
yesterday it occurred to me that it'd be useful to do something like this:
template <typename... Ts>
struct Interface
{
rtype<Ts> func(ptype<Ts>) *...* = 0;
};
template <typename... Ts>
struct Implementation: Interface<Ts...>
{
rtype<Ts> func(ptype<Ts> p) override
{
return impl<Ts>(p);
} *...*
};
where rtype<>, ptype<> and impl<> are fictional templates with
exposition-only purpose.
At the moment such a construct is possible via recursive inheritance, with
much added boilerplate and unneeded RTTI information being written out in
the resulting binary (for the intermediate, not used types). Following is
a possible implementation of the above, given the current tools at our
disposal.
template <typename... Ts>
struct Interface;
template <>
struct Interface<>
{ };
template <typename T>
struct Interface<T>
{
virtual rtype<T> func(ptype<T>) = 0;
};
template <typename T, typename... Ts>
struct Interface<T, Ts...>: Interface<Ts...>
{
using Interface<Ts...>::func;
virtual rtype<T> func(ptype<T>) = 0;
};
namespace detail {
template <typename Interface, typename... Ts>
struct Implementation;
template <typename Interface>
struct Implementation<Interface>: Interface
{ };
template <typename Interface, typename T>
struct Implementation<Interface, T>: Interface
{
using Interface::func;
rtype<T> func(ptype<T> p) override
{
return impl<T>(p);
}
};
template <typename Interface, typename T, typename... Ts>
struct Implementation<Interface, T, Ts...>:
Implementation<Interface, Ts...>
{
using Implementation<Interface, Ts...>::func;
rtype<T> func(ptype<T> p) override
{
return impl<T>(p);
}
};
}
template <typename... Ts>
using Implementation = detail::Implementation<Interface<Ts...>, Ts...>;
An alternative version of the above can be implemented with the help of
std::variant, but with the limitation that all rtype<Ts>... must be the
same, and with the speed penalty that a sub optimized variant can incur.
Here's a possible implementation:
template <typename... Ts>
struct Interface
{
using variant_type = std::variant<ptype<Ts>...>;
using return_type = std::tuple_element_t<0, std::tuple<rtype<Ts>...>>;
static_assert(std::conjunction_v<std::is_same<return_type,
rtype<Ts>>...>, "return types must be the same for all functions.
Sigh.");
virtual return_type func(variant_type const &) = 0;
};
template <typename... Ts>
struct Implementation: Interface<Ts...>
{
using Interface<Ts...>::variant_type;
using Interface<Ts...>::return_type;
return_type func(variant_type const &v) override
{
return std::visit([](auto p){ return impl<decltype(p)>(); }, v);
}
};
template <>
struct Interface<>
{ };
template <>
struct Implementation<>: Interface<>
{ };
At the moment I'm using the std::variant approach in some production code,
for it's less verbose than the recursive approach and I can cope with its
limitations, but I believe it'd be nice to use the generalized pack syntax
for that.
All the above considered, I'd therefore like to suggest an amendment to
P1858 <http://open-std.org/JTC1/SC22/WG21/docs/papers/2020/p1858r2.html> as
described in the first code snippet.
Fabio Alemagna
Received on 2020-09-10 04:33:49