Date: Thu, 5 Nov 2020 19:27:37 +0100
I understand the problem you are trying to solve, it is a quite
common pattern, being in an execution path in which we know the alternative
held by the variant. For instance:
if (has_alternative<alternative_type>(variant_object))
{
// it is now safe to get the alternative without checking.
}
but for this escenario, it is possible to do
if (auto *alternative = std::get_if<alternative_type>(&variant_object))
{
// it is now safe to dereference the alternative pointer
}
I know, it is ugly and doesn't communicate properly the intent.
This is the same case as avoiding the range check in std::vector::at() with
std::vector::operator[]() subscript operator in:
if (i < vector.size())
{
// it is now safe to do vector[i]
}
In this case, for historical reasons we have both methods. But in case of
requiring this optimized path for variants, I would suggest to not adding a
new function and pollute even more the std with different names for
different patterns/idioms but overloading the current std::get with an
overload discriminant object like std::nothrow:
auto& variant = std::get<variant_type>(variant, std::nothrow);
Or we could define a new std::nocheck constant to be reused in any other
unchecked overload. In this way, the intent is clear.
Invoking std::get with std::nothrow (or std::nocheck or whatever other
symbol we could propose) would be undefined behavior when calling it when
the variant holds a different alternative as subscript operator does when
performing an out of bounds access to a std::vector. It is elegant, clear,
safe and efficient.
I hope this proposal helps,
Jordi Vilar
Missatge de Avi Kivity via Std-Proposals <std-proposals_at_[hidden]>
del dia dj., 5 de nov. 2020 a les 18:10:
> How can the reader of *get_if() tell if the author intended this
> optimization, or was just lazy?
>
>
> Function names should communicate intent. "get_if()" communicates that you
> don't know if the variant holds the the type you are asking for or not.
> *get_if() communicates that you forgot to check.
>
>
> My preference would be static_variant_cast<>, following static_cast<> and
> static_pointer_cast<>. The latter two communicate that the author knows
> there is a safety violation if some other check is not performed, and
> confirms they are aware of it, enough to have typed such a long and ugly
> name.
>
>
> On 26/10/2020 03.25, Tony V E via Std-Proposals wrote:
>
> I don't think you need unreachable.
> *get_if should be enough. Unconditionally dereference the result.
>
> Sent from my BlackBerry portable Babbage Device
> *From: *Justin Bassett via Std-Proposals
> *Sent: *Sunday, October 25, 2020 9:16 PM
> *To: *Std-Proposals
> *Reply To: *std-proposals_at_[hidden]
> *Cc: *Justin Bassett
> *Subject: *Re: [std-proposals] std::as
>
> I'm glad to see that clang can also optimize this get_if access; it used
> to be unable to do so. MSVC is also unable to optimize this when using
> __assume (
> https://docs.microsoft.com/en-us/cpp/intrinsics/assume?view=vs-2019 )
> (which is the same as Clang's __builtin_assume()
> https://clang.llvm.org/docs/LanguageExtensions.html#builtin-assume )
> instead of __builtin_unreachable().
>
> In theory, a portable __builtin_unreachable() would be:
>
> [[noreturn]]
> inline void unreachable() {} // basically, unconditionally trigger
> undefined behavior
>
> In practice, only GCC seems to recognize this.
>
> --Justin Bassett
>
> On Sun, Oct 25, 2020 at 5:59 PM Ryan P. Nicholl via Std-Proposals <
> std-proposals_at_[hidden]> wrote:
>
>>
>> [build] C:\Users\Ryan\rpnx-core\private\sources\all\test3.cpp(119,5): error C3861: '__builtin_unreachable': identifier not found [C:\Users\Ryan\rpnx-core\build\rpnx-core-test3.vcxproj]
>>
>> Great idea, except that __builtin_unreachable() is a GCC specific
>> extension, and is not part of standard C++. (It would be nice to be able to
>> do this in a cross platform way though! But that is for another discussion.)
>>
>> --
>> Ryan P. Nicholl
>> Tel: (678)-358-7765
>> Personal Email: rnicholl_at_[hidden]
>> Work Email: ryan.nicholl_at_[hidden]
>> Tox:
>> 52B3A61CDAF88BCC854F568933F071E3530600CDBA3D5354AADC9AD179575D68021AE959719D
>>
>>
>> ‐‐‐‐‐‐‐ Original Message ‐‐‐‐‐‐‐
>> On Sunday, October 25, 2020 8:50 PM, Barry Revzin <barry.revzin_at_[hidden]>
>> wrote:
>>
>>
>>
>> On Sun, Oct 25, 2020 at 7:41 PM Ryan P. Nicholl via Std-Proposals <
>> std-proposals_at_[hidden]> wrote:
>>
>>> I decided to implement a new class based on std::variant. I call it
>>> "rpnx::derivator", but it's basically "allocating_variant". I tried to make
>>> it as similar to std::variant as possible. When looking at this, I noticed
>>> something weird about std::variant. There is no "zero overhead" way to get
>>> the element contained by the variant, as std::get<I> checks for invalid
>>> access and throws an exception if invalid. To solve this issue, I would
>>> like to propose std::as, which works the same as std::get, but accessing
>>> the wrong type is undefined behavior instead of throwing an exception.
>>>
>>> --
>>> Ryan P. Nicholl
>>> Tel: (678)-358-7765
>>> Personal Email: rnicholl_at_[hidden]
>>> Work Email: ryan.nicholl_at_[hidden]
>>> Tox:
>>> 52B3A61CDAF88BCC854F568933F071E3530600CDBA3D5354AADC9AD179575D68021AE959719D
>>>
>>
>> You can achieve this by using std::get_if() and marking the nullptr case
>> as unreachable. For example:
>>
>> auto f(std::variant<int, double>& v) -> int* {
>> return std::get_if<int>(&v);
>> }
>>
>> auto g(std::variant<int, double>& v) -> int* {
>> auto p = std::get_if<int>(&v);
>> if (not p) __builtin_unreachable();
>> return p;
>> }
>>
>> On -O1, f emits a check but g does not: https://godbolt.org/z/9G9fd5.
>>
>> Barry
>>
>>
>> --
>> Std-Proposals mailing list
>> Std-Proposals_at_[hidden]
>> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>>
>
>
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>
common pattern, being in an execution path in which we know the alternative
held by the variant. For instance:
if (has_alternative<alternative_type>(variant_object))
{
// it is now safe to get the alternative without checking.
}
but for this escenario, it is possible to do
if (auto *alternative = std::get_if<alternative_type>(&variant_object))
{
// it is now safe to dereference the alternative pointer
}
I know, it is ugly and doesn't communicate properly the intent.
This is the same case as avoiding the range check in std::vector::at() with
std::vector::operator[]() subscript operator in:
if (i < vector.size())
{
// it is now safe to do vector[i]
}
In this case, for historical reasons we have both methods. But in case of
requiring this optimized path for variants, I would suggest to not adding a
new function and pollute even more the std with different names for
different patterns/idioms but overloading the current std::get with an
overload discriminant object like std::nothrow:
auto& variant = std::get<variant_type>(variant, std::nothrow);
Or we could define a new std::nocheck constant to be reused in any other
unchecked overload. In this way, the intent is clear.
Invoking std::get with std::nothrow (or std::nocheck or whatever other
symbol we could propose) would be undefined behavior when calling it when
the variant holds a different alternative as subscript operator does when
performing an out of bounds access to a std::vector. It is elegant, clear,
safe and efficient.
I hope this proposal helps,
Jordi Vilar
Missatge de Avi Kivity via Std-Proposals <std-proposals_at_[hidden]>
del dia dj., 5 de nov. 2020 a les 18:10:
> How can the reader of *get_if() tell if the author intended this
> optimization, or was just lazy?
>
>
> Function names should communicate intent. "get_if()" communicates that you
> don't know if the variant holds the the type you are asking for or not.
> *get_if() communicates that you forgot to check.
>
>
> My preference would be static_variant_cast<>, following static_cast<> and
> static_pointer_cast<>. The latter two communicate that the author knows
> there is a safety violation if some other check is not performed, and
> confirms they are aware of it, enough to have typed such a long and ugly
> name.
>
>
> On 26/10/2020 03.25, Tony V E via Std-Proposals wrote:
>
> I don't think you need unreachable.
> *get_if should be enough. Unconditionally dereference the result.
>
> Sent from my BlackBerry portable Babbage Device
> *From: *Justin Bassett via Std-Proposals
> *Sent: *Sunday, October 25, 2020 9:16 PM
> *To: *Std-Proposals
> *Reply To: *std-proposals_at_[hidden]
> *Cc: *Justin Bassett
> *Subject: *Re: [std-proposals] std::as
>
> I'm glad to see that clang can also optimize this get_if access; it used
> to be unable to do so. MSVC is also unable to optimize this when using
> __assume (
> https://docs.microsoft.com/en-us/cpp/intrinsics/assume?view=vs-2019 )
> (which is the same as Clang's __builtin_assume()
> https://clang.llvm.org/docs/LanguageExtensions.html#builtin-assume )
> instead of __builtin_unreachable().
>
> In theory, a portable __builtin_unreachable() would be:
>
> [[noreturn]]
> inline void unreachable() {} // basically, unconditionally trigger
> undefined behavior
>
> In practice, only GCC seems to recognize this.
>
> --Justin Bassett
>
> On Sun, Oct 25, 2020 at 5:59 PM Ryan P. Nicholl via Std-Proposals <
> std-proposals_at_[hidden]> wrote:
>
>>
>> [build] C:\Users\Ryan\rpnx-core\private\sources\all\test3.cpp(119,5): error C3861: '__builtin_unreachable': identifier not found [C:\Users\Ryan\rpnx-core\build\rpnx-core-test3.vcxproj]
>>
>> Great idea, except that __builtin_unreachable() is a GCC specific
>> extension, and is not part of standard C++. (It would be nice to be able to
>> do this in a cross platform way though! But that is for another discussion.)
>>
>> --
>> Ryan P. Nicholl
>> Tel: (678)-358-7765
>> Personal Email: rnicholl_at_[hidden]
>> Work Email: ryan.nicholl_at_[hidden]
>> Tox:
>> 52B3A61CDAF88BCC854F568933F071E3530600CDBA3D5354AADC9AD179575D68021AE959719D
>>
>>
>> ‐‐‐‐‐‐‐ Original Message ‐‐‐‐‐‐‐
>> On Sunday, October 25, 2020 8:50 PM, Barry Revzin <barry.revzin_at_[hidden]>
>> wrote:
>>
>>
>>
>> On Sun, Oct 25, 2020 at 7:41 PM Ryan P. Nicholl via Std-Proposals <
>> std-proposals_at_[hidden]> wrote:
>>
>>> I decided to implement a new class based on std::variant. I call it
>>> "rpnx::derivator", but it's basically "allocating_variant". I tried to make
>>> it as similar to std::variant as possible. When looking at this, I noticed
>>> something weird about std::variant. There is no "zero overhead" way to get
>>> the element contained by the variant, as std::get<I> checks for invalid
>>> access and throws an exception if invalid. To solve this issue, I would
>>> like to propose std::as, which works the same as std::get, but accessing
>>> the wrong type is undefined behavior instead of throwing an exception.
>>>
>>> --
>>> Ryan P. Nicholl
>>> Tel: (678)-358-7765
>>> Personal Email: rnicholl_at_[hidden]
>>> Work Email: ryan.nicholl_at_[hidden]
>>> Tox:
>>> 52B3A61CDAF88BCC854F568933F071E3530600CDBA3D5354AADC9AD179575D68021AE959719D
>>>
>>
>> You can achieve this by using std::get_if() and marking the nullptr case
>> as unreachable. For example:
>>
>> auto f(std::variant<int, double>& v) -> int* {
>> return std::get_if<int>(&v);
>> }
>>
>> auto g(std::variant<int, double>& v) -> int* {
>> auto p = std::get_if<int>(&v);
>> if (not p) __builtin_unreachable();
>> return p;
>> }
>>
>> On -O1, f emits a check but g does not: https://godbolt.org/z/9G9fd5.
>>
>> Barry
>>
>>
>> --
>> Std-Proposals mailing list
>> Std-Proposals_at_[hidden]
>> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>>
>
>
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>
Received on 2020-11-05 12:27:54