C++ Logo

std-proposals

Advanced search

Re: [std-proposals] [DRAFT PAPER] std::variant with std::specify_base

From: Bengt Gustafsson <bengt.gustafsson_at_[hidden]>
Date: Fri, 16 Sep 2022 14:47:44 +0200
I feel that it is a kludge to use variant for this purpose, where the
aim is to store an instance of a subclass in a potentially open set of
subclasses.

This is better served by something like polymorphic_value as described
here: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0201r1.pdf

Reference implementation is here: https://github.com/jbcoe/polymorphic_value

Furthermore I made a SBO enabled implementation as I think
polymorphic_value should have the small buffer optimization. This is
more of a demonstration than a real reference implementation (lack of
test cases, noexcept, etc). This is available in the zip file in this
issue: https://github.com/jbcoe/polymorphic_value/issues/124

A similar idea was posted here recently:
https://pvs-studio.com/en/blog/posts/cpp/0983/

Bengt


Den 2022-09-16 kl. 08:43, skrev std-proposals-request_at_[hidden]:
> Send Std-Proposals mailing list submissions to
> std-proposals_at_[hidden]
>
> To subscribe or unsubscribe via the World Wide Web, visit
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
> or, via email, send a message with subject or body 'help' to
> std-proposals-request_at_[hidden]
>
> You can reach the person managing the list at
> std-proposals-owner_at_[hidden]
>
> When replying, please edit your Subject line so it is more specific
> than "Re: Contents of Std-Proposals digest..."
>
>
> Today's Topics:
>
> 1. Re: [DRAFT PAPER] std::variant with std::specify_base
> (Zhihao Yuan)
> 2. Re: [DRAFT PAPER] std::variant with std::specify_base
> (Frederick Virchanza Gotham)
> 3. Re: [DRAFT PAPER] std::variant with std::specify_base
> (Arthur O'Dwyer)
> 4. Re: [DRAFT PAPER] std::variant with std::specify_base
> (Jason McKesson)
> 5. Re: [DRAFT PAPER] std::variant with std::specify_base
> (L?n?rd Szolnoki)
>
>
> ----------------------------------------------------------------------
>
> Message: 1
> Date: Thu, 15 Sep 2022 20:32:50 +0000
> From: Zhihao Yuan <zy_at_[hidden]>
> To: std-proposals_at_[hidden]
> Subject: Re: [std-proposals] [DRAFT PAPER] std::variant with
> std::specify_base
> Message-ID:
> <ImLrGtgk09qIhMYRSVLgL7cIUgHrJIzO4oDKjAzhp8BlFYKwlUC1iHWLo9bvPYzrc4dEBlXIt_5S75e>
>
> Content-Type: text/plain; charset="utf-8"
>
> On Wednesday, September 14th, 2022 at 2:57 PM, Frederick Virchanza Gotham via Std-Proposals <std-proposals_at_[hidden]> wrote:
>
>
>> On Wed, Sep 14, 2022 at 8:15 PM L?n?rd Szolnoki via Std-Proposals
>> std-proposals_at_[hidden] wrote:
>>
>>> There is no reason that variant<Cat, Dog> and variant<specify_base<Animal>, Cat, Dog> should be different types.
>> The former doesn't allow the variant object to have no object. The
>> second one is very similar to:
>>
>> variant< monostate, Cat, Dog >
>>
>
> I agree that variant<monostate, Cat, Dog>
> can be a model for
> variant<specify_base<Animal>, Cat, Dog>,
> but I also want variant<Cat, Dog> to
> implement an Animal interface.
> To summarize, I think
>
> nonnull
> nullable
> throwing
>
> All make sense.
>
> How about this:
>
> template<class T>
> struct implements
> {
> template<class... Ts>
> using model = std::variant<Ts...>;
> };
>
> template<class T>
> struct implements_nullable
> {
> template<class... Ts>
> using model = std::variant<std::monostate, Ts...>;
>
> static auto default_proxy() noexcept { return nullptr; }
> };
>
> template<class T>
> struct implements_or_throw
> {
> template<class... Ts>
> using model = std::variant<std::monostate, Ts...>;
> };
>
> --
> Zhihao Yuan, ID lichray
> The best way to predict the future is to invent it.
> _______________________________________________
> -------------- next part --------------
> A non-text attachment was scrubbed...
> Name: signature.asc
> Type: application/pgp-signature
> Size: 509 bytes
> Desc: OpenPGP digital signature
> URL: <https://lists.isocpp.org/std-proposals/attachments/20220915/0627b44f/attachment.sig>
>
> ------------------------------
>
> Message: 2
> Date: Thu, 15 Sep 2022 22:57:46 +0100
> From: Frederick Virchanza Gotham <cauldwell.thomas_at_[hidden]>
> To: std-proposals <std-proposals_at_[hidden]>
> Subject: Re: [std-proposals] [DRAFT PAPER] std::variant with
> std::specify_base
> Message-ID:
> <CALtZhhON1838bZZbw+OeMj5c6Ls+_c966RBEaEPoBPZgWdLXPQ_at_[hidden]>
> Content-Type: text/plain; charset="UTF-8"
>
> On Thu, Sep 15, 2022 at 9:33 PM Zhihao Yuan <zy_at_[hidden]> wrote:
>> I agree that variant<monostate, Cat, Dog>
>> can be a model for
>> variant<specify_base<Animal>, Cat, Dog>,
>> but I also want variant<Cat, Dog> to
>> implement an Animal interface.
>> To summarize, I think
>>
>> nonnull
>> nullable
>> throwing
>>
>> All make sense.
>>
>> How about this:
>>
>> template<class T>
>> struct implements
>> {
>> template<class... Ts>
>> using model = std::variant<Ts...>;
>> };
>>
>> template<class T>
>> struct implements_nullable
>> {
>> template<class... Ts>
>> using model = std::variant<std::monostate, Ts...>;
>>
>> static auto default_proxy() noexcept { return nullptr; }
>> };
>>
>> template<class T>
>> struct implements_or_throw
>> {
>> template<class... Ts>
>> using model = std::variant<std::monostate, Ts...>;
>> };
>
> It isn't immediately apparent to me how you would use those. Something
> like the following?
>
> implements<Animal>::model<Dog,Cat,Fish> obj;
>
> obj->Eat();
>
> The whole point of my proposal is to have this simple syntax:
> obj->BaseClassMethod();
>
> I want it to all be inside "std::variant" so that the object can be
> used in templates that expect an std::variant, and also so that the
> "std::variant" object can be copied and it will still retain all of
> its info (which wouldn't happen if we made a new class derived from
> "std::variant").
>
>
> ------------------------------
>
> Message: 3
> Date: Thu, 15 Sep 2022 15:56:38 -0700
> From: "Arthur O'Dwyer" <arthur.j.odwyer_at_[hidden]>
> To: std-proposals_at_[hidden]
> Subject: Re: [std-proposals] [DRAFT PAPER] std::variant with
> std::specify_base
> Message-ID:
> <CADvuK0JMNNcW8JSzr-rxQGQ_4j6v0COpLGk76tUihdkt4OUcww_at_[hidden]>
> Content-Type: text/plain; charset="utf-8"
>
> I still don't understand why Frederick wants any changes to the
> existing std::variant. You can already do all of this today: given a
> `variant<Cat, Dog>`, it's trivial to get an `Animal*` out of it using
> `std::visit`. And you can even make your visitor return nullptr in the case
> that you're given a `variant<Cat, Dog, Tree>` where the active alternative
> happens to be `Tree`. Here's the code:
>
> // https://godbolt.org/z/nbGda8h6K
>
> template<class Base, class... Ts>
> Base *specify_base(std::variant<Ts...>& v) {
> return std::visit([](auto& t) -> Base* {
> if constexpr (std::is_base_of_v<Base, std::decay_t<decltype(t)>>) {
> return &t;
> } else {
> return nullptr;
> }
> }, v);
> }
>
> void test(std::variant<Cat, Dog, Tree> v) {
> if (Animal *a = specify_base<Animal>(v)) {
> a->speak();
> } else {
> std::cout << "trees don't speak\n";
> }
> }
>
> On Thu, Sep 15, 2022 at 2:58 PM Frederick Virchanza Gotham via
> Std-Proposals <std-proposals_at_[hidden]> wrote:
>
>> On Thu, Sep 15, 2022 at 9:33 PM Zhihao Yuan <zy_at_[hidden]> wrote:
>>> I agree that variant<monostate, Cat, Dog>
>>> can be a model for
>>> variant<specify_base<Animal>, Cat, Dog>,
>>> but I also want variant<Cat, Dog> to
>>> implement an Animal interface.
>>> To summarize, I think
>>>
>>> nonnull
>>> nullable
>>> throwing
>>>
>>> All make sense.
>>>
>>> How about this:
>>>
>>> template<class T>
>>> struct implements
>>> {
>>> template<class... Ts>
>>> using model = std::variant<Ts...>;
>>> };
>>>
>>> template<class T>
>>> struct implements_nullable
>>> {
>>> template<class... Ts>
>>> using model = std::variant<std::monostate, Ts...>;
>>>
>>> static auto default_proxy() noexcept { return nullptr; }
>>> };
>>>
>>> template<class T>
>>> struct implements_or_throw
>>> {
>>> template<class... Ts>
>>> using model = std::variant<std::monostate, Ts...>;
>>> };
>>
>> It isn't immediately apparent to me how you would use those. Something
>> like the following?
>>
>> implements<Animal>::model<Dog,Cat,Fish> obj;
>>
>> obj->Eat();
>>
>> The whole point of my proposal is to have this simple syntax:
>> obj->BaseClassMethod();
>>
>> I want it to all be inside "std::variant" so that the object can be
>> used in templates that expect an std::variant, and also so that the
>> "std::variant" object can be copied and it will still retain all of
>> its info (which wouldn't happen if we made a new class derived from
>> "std::variant").
>> --
>> Std-Proposals mailing list
>> Std-Proposals_at_[hidden]
>> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>>
> -------------- next part --------------
> HTML attachment scrubbed and removed
>
> ------------------------------
>
> Message: 4
> Date: Thu, 15 Sep 2022 21:23:02 -0400
> From: Jason McKesson <jmckesson_at_[hidden]>
> To: std-proposals_at_[hidden]
> Subject: Re: [std-proposals] [DRAFT PAPER] std::variant with
> std::specify_base
> Message-ID:
> <CANLtd3V+fTbHkRfE8+fesQiFkVCjw09o_Txkj8FKYPageQR2fg_at_[hidden]>
> Content-Type: text/plain; charset="UTF-8"
>
> On Thu, Sep 15, 2022 at 5:58 PM Frederick Virchanza Gotham via
> Std-Proposals <std-proposals_at_[hidden]> wrote:
>> On Thu, Sep 15, 2022 at 9:33 PM Zhihao Yuan <zy_at_[hidden]> wrote:
>>> I agree that variant<monostate, Cat, Dog>
>>> can be a model for
>>> variant<specify_base<Animal>, Cat, Dog>,
>>> but I also want variant<Cat, Dog> to
>>> implement an Animal interface.
>>> To summarize, I think
>>>
>>> nonnull
>>> nullable
>>> throwing
>>>
>>> All make sense.
>>>
>>> How about this:
>>>
>>> template<class T>
>>> struct implements
>>> {
>>> template<class... Ts>
>>> using model = std::variant<Ts...>;
>>> };
>>>
>>> template<class T>
>>> struct implements_nullable
>>> {
>>> template<class... Ts>
>>> using model = std::variant<std::monostate, Ts...>;
>>>
>>> static auto default_proxy() noexcept { return nullptr; }
>>> };
>>>
>>> template<class T>
>>> struct implements_or_throw
>>> {
>>> template<class... Ts>
>>> using model = std::variant<std::monostate, Ts...>;
>>> };
>>
>> It isn't immediately apparent to me how you would use those. Something
>> like the following?
>>
>> implements<Animal>::model<Dog,Cat,Fish> obj;
>>
>> obj->Eat();
>>
>> The whole point of my proposal is to have this simple syntax:
>> obj->BaseClassMethod();
> My question, which isn't explained in the proposal, is this: why do we
> want it to be that easy?
>
> If a standard library type supports a particular operation, then that
> means that we're saying (in theory) that this is a good and useful
> thing for C++ programmers to have and use. So... is it?
>
> This feels a bit like you want the benefits of variant-based
> polymorphism (ability to use value types directly, no type-erasure
> like `any`, etc), but you don't want to use interfaces based on
> `visit` or helper functions that aren't member functions of some type.
> That is, you really want to use member functions to define your
> interface, but the only way to do that is with a base class, but that
> comes with a bunch of problems, so you want to make `variant` solve
> those problems.
>
>
> ------------------------------
>
> Message: 5
> Date: Fri, 16 Sep 2022 07:43:00 +0100
> From: L?n?rd Szolnoki <cpp_at_[hidden]>
> To: std-proposals_at_[hidden]
> Subject: Re: [std-proposals] [DRAFT PAPER] std::variant with
> std::specify_base
> Message-ID: <A4656815-1B4F-464E-9652-14BBE974DF15_at_[hidden]>
> Content-Type: text/plain; charset=utf-8
>
>
>
> On 15 September 2022 22:57:46 BST, Frederick Virchanza Gotham via Std-Proposals <std-proposals_at_[hidden]> wrote:
>> On Thu, Sep 15, 2022 at 9:33 PM Zhihao Yuan <zy_at_[hidden]> wrote:
>>> I agree that variant<monostate, Cat, Dog>
>>> can be a model for
>>> variant<specify_base<Animal>, Cat, Dog>,
>>> but I also want variant<Cat, Dog> to
>>> implement an Animal interface.
>>> To summarize, I think
>>>
>>> nonnull
>>> nullable
>>> throwing
>>>
>>> All make sense.
>>>
>>> How about this:
>>>
>>> template<class T>
>>> struct implements
>>> {
>>> template<class... Ts>
>>> using model = std::variant<Ts...>;
>>> };
>>>
>>> template<class T>
>>> struct implements_nullable
>>> {
>>> template<class... Ts>
>>> using model = std::variant<std::monostate, Ts...>;
>>>
>>> static auto default_proxy() noexcept { return nullptr; }
>>> };
>>>
>>> template<class T>
>>> struct implements_or_throw
>>> {
>>> template<class... Ts>
>>> using model = std::variant<std::monostate, Ts...>;
>>> };
>>
>> It isn't immediately apparent to me how you would use those. Something
>> like the following?
>>
>> implements<Animal>::model<Dog,Cat,Fish> obj;
>>
>> obj->Eat();
>>
>> The whole point of my proposal is to have this simple syntax:
>> obj->BaseClassMethod();
>>
>> I want it to all be inside "std::variant" so that the object can be
>> used in templates that expect an std::variant,
> I would prefer a "variant-like" concept for this, that defines the common interface between variant and your type.
>
>> and also so that the
>> "std::variant" object can be copied and it will still retain all of
>> its info (which wouldn't happen if we made a new class derived from
>> "std::variant").
> I didn't propose deriving from variant, bet even so, all the data members are in the variant, slicing wouldn't lose information here.
>
> Cheers,
> L?n?rd
>
>
> ------------------------------
>
> Subject: Digest Footer
>
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>
>
> ------------------------------
>
> End of Std-Proposals Digest, Vol 42, Issue 43
> *********************************************

Received on 2022-09-16 12:47:59