C++ Logo

std-proposals

Advanced search

Pack of functions - amendment to P1858

From: Fabio Alemagna <falemagn_at_[hidden]>
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

Received on 2020-09-10 04:33:49