On Fri, 28 Jun 2024 at 07:53, Maciej Cencora <m.cenc...@gmail.com> wrote:
>
> But constexpr-ness of bit_cast has additional limitations and e.g. providing 
> an union as _Tp would be a hard-error. So we have two options:
>  - before bitcasting check if type can be bitcast-ed at compile-time,
>  - change the 'if constexpr' to regular 'if'.
>
> If we go with the second solution then we will include classes with pointers, 
> and unions.

I don't think we want to add runtime comparisons, the point is to
optimize the code not do more work :-)

> Additionally we could also include types with padding by passing 
> zero-initialized object (like a class-scope static constexpr or global) into 
> bit_cast... but then such a variable would be ODR-used and most-likely won't 
> be optimized out.
>
> I guess the best option would be to introduce in C++ language a new 
> compiler-backed type trait like: 
> std::zero_initialized_object_has_all_zeros_object_representation<T>.

Yes, I think a new built-in is the only approach that will work for
class types. I'll just limit the optimization to scalars (excluding
member pointers).



>
> Regards,
> Maciej
>
> pt., 28 cze 2024 o 00:25 Jonathan Wakely <jwak...@redhat.com> napisaƂ(a):
>>
>> On Thu, 27 Jun 2024 at 14:27, Maciej Cencora <m.cenc...@gmail.com> wrote:
>> >
>> > I think going the bit_cast way would be the best because it enables the 
>> > optimization for many more classes including common wrappers like 
>> > optional, variant, pair, tuple and std::array.
>>
>> This isn't tested but seems to work on simple cases. But for large
>> objects the loop hits the constexpr iteration limit and compilation
>> fails, so it needs a sizeof(_Tp) < 64 or something.
>>
>>   using _ValueType
>>     = typename iterator_traits<_ForwardIterator>::value_type;
>>   using _Tp = remove_all_extents_t<_ValueType>;
>>   // Need value-init to be equivalent to zero-init.
>>   if constexpr (is_member_pointer<_Tp>::value)
>>     return nullptr;
>>   else if constexpr (!is_scalar<_Tp>::value)
>>     {
>>       using __trivial
>>         = __and_<is_trivial<_ValueType>,
>>                  is_trivially_constructible<_ValueType>>;
>>       if constexpr (__trivial::value)
>>         {
>>           struct _Bytes
>>           {
>>             unsigned char __b[sizeof(_Tp)];
>>
>> #if __cpp_constexpr >= 201304
>>             constexpr bool _M_nonzero() const
>>             {
>>               for (auto __c : __b)
>>                 if (__c)
>>                   return true;
>>               return false;
>>             }
>> #else
>>             constexpr bool _M_nonzero(size_t __n = 0) const
>>             {
>>               return __n < sizeof(_Tp)
>>                              && (__b[__n] || _M_nonzero(__n + 1));
>>             }
>> #endif
>>           };
>>           if constexpr (__builtin_bit_cast(_Bytes, _Tp())._M_nonzero())
>>             return nullptr;
>>         }
>>     }
>>   using _Ptr = decltype(std::__to_address(__first));
>>   // Cannot use memset if _Ptr is cv-qualified.
>>   if constexpr (is_convertible<_Ptr, void*>::value)
>>     return std::__to_address(__first);
>>

Reply via email to