[PATCH] Remove lambdas from _Rb_tree
François Dumont
frs.dumont@gmail.com
Thu Nov 19 06:46:29 GMT 2020
On 18/11/20 12:50 am, Jonathan Wakely wrote:
> On 17/11/20 21:51 +0100, François Dumont via Libstdc++ wrote:
>> This is a change that has been done to _Hashtable and that I forgot
>> to propose for _Rb_tree.
>>
>> The _GLIBCXX_XREF macro can be easily removed of course.
>>
>> Â Â Â libstdc++: _Rb_tree code cleanup, remove lambdas.
>>
>> Â Â Â Use an additional template parameter on the clone method to
>> propagate if the values must be
>> Â Â Â copy or move rather than lambdas.
>>
>> Â Â Â libstdc++-v3/ChangeLog:
>>
>> Â Â Â Â Â Â Â Â Â Â Â * include/bits/move.h (_GLIBCXX_XREF): New.
>> Â Â Â Â Â Â Â Â Â Â Â * include/bits/stl_tree.h: Adapt to use latter.
>> Â Â Â Â Â Â Â Â Â Â Â (_Rb_tree<>::_S_fwd_value_for): New.
>> Â Â Â Â Â Â Â Â Â Â Â (_Rb_tree<>::_M_clone_node): Add _Tree
>> template parameter.
>> Â Â Â Â Â Â Â Â Â Â Â Use _S_fwd_value_for.
>> Â Â Â Â Â Â Â Â Â Â Â (_Rb_tree<>::_M_cbegin): New.
>> Â Â Â Â Â Â Â Â Â Â Â (_Rb_tree<>::_M_begin): Use latter.
>> Â Â Â Â Â Â Â Â Â Â Â (_Rb_tree<>::_M_copy): Add _Tree template
>> parameter.
>> Â Â Â Â Â Â Â Â Â Â Â (_Rb_tree<>::_M_move_data): Use rvalue
>> reference for _Rb_tree parameter.
>> Â Â Â Â Â Â Â Â Â Â Â (_Rb_tree<>::_M_move_assign): Likewise.
>>
>> Tested under Linux x86_64.
>>
>> Ok to commit ?
>
> GCC is in stage 3 now, so this should have been posted last week
> really.
Ok, no problem, it can wait.
Still, following your advises here is what I come up with, much simpler
indeed.
I just run a few tests for the moment but so far so good.
Thanks
>
>
>> diff --git a/libstdc++-v3/include/bits/move.h
>> b/libstdc++-v3/include/bits/move.h
>> index 5a4dbdc823c..e0d68ca9108 100644
>> --- a/libstdc++-v3/include/bits/move.h
>> +++ b/libstdc++-v3/include/bits/move.h
>> @@ -158,9 +158,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>
>> /// @} group utilities
>>
>> +#define _GLIBCXX_XREF(_Tp) _Tp&&
>
> I think this does improve the code that uses this. But the correct
> name for this is forwarding reference, so I think FWDREF would be
> better than XREF. XREF doesn't tell me anything about what it's for.
>
>> #define _GLIBCXX_MOVE(__val) std::move(__val)
>> #define _GLIBCXX_FORWARD(_Tp, __val) std::forward<_Tp>(__val)
>> #else
>> +#define _GLIBCXX_XREF(_Tp) const _Tp&
>> #define _GLIBCXX_MOVE(__val) (__val)
>> #define _GLIBCXX_FORWARD(_Tp, __val) (__val)
>> #endif
>> diff --git a/libstdc++-v3/include/bits/stl_tree.h
>> b/libstdc++-v3/include/bits/stl_tree.h
>> index ec141ea01c7..128c7e2c892 100644
>> --- a/libstdc++-v3/include/bits/stl_tree.h
>> +++ b/libstdc++-v3/include/bits/stl_tree.h
>> @@ -478,11 +478,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>
>> template<typename _Arg>
>> _Link_type
>> -#if __cplusplus < 201103L
>> - operator()(const _Arg& __arg)
>> -#else
>> - operator()(_Arg&& __arg)
>> -#endif
>> + operator()(_GLIBCXX_XREF(_Arg) __arg)
>> {
>> _Link_type __node = static_cast<_Link_type>(_M_extract());
>> if (__node)
>> @@ -544,11 +540,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>
>> template<typename _Arg>
>> _Link_type
>> -#if __cplusplus < 201103L
>> - operator()(const _Arg& __arg) const
>> -#else
>> - operator()(_Arg&& __arg) const
>> -#endif
>> + operator()(_GLIBCXX_XREF(_Arg) __arg) const
>> { return _M_t._M_create_node(_GLIBCXX_FORWARD(_Arg, __arg)); }
>>
>> private:
>> @@ -655,11 +647,27 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> _M_put_node(__p);
>> }
>>
>> - template<typename _NodeGen>
>> +#if __cplusplus >= 201103L
>> + template<typename _Tree>
>> + static constexpr
>> + typename conditional<std::is_lvalue_reference<_Tree>::value,
>> + const value_type&, value_type&&>::type
>> + _S_fwd_value_for(value_type& __val) noexcept
>> + { return std::move(__val); }
>> +#else
>> + template<typename _Tree>
>> + static const value_type&
>> + _S_fwd_value_for(value_type& __val)
>> + { return __val; }
>> +#endif
>> +
>> + template<typename _Tree, typename _NodeGen>
>> _Link_type
>> - _M_clone_node(_Const_Link_type __x, _NodeGen& __node_gen)
>> + _M_clone_node(_GLIBCXX_XREF(_Tree),
>
> Since the _Tree type is only used to decide whether to copy or move,
> could it just be a bool instead?
>
> template<bool _Move, typename _NodeGen>
> _Link_type
> _M_clone_node(_Link_type __x, _NodeGen& __node_gen)
>
> Then it would be called as _M_clone_node<_Move>(__x, __node_gen)
> instead of _M_clone_node(_GLIBCXX_FORWARD(_Tree, __t), __x, __node_gen).
> That seems easier to read.
>
>> + _Link_type __x, _NodeGen& __node_gen)
>> {
>> - _Link_type __tmp = __node_gen(*__x->_M_valptr());
>> + _Link_type __tmp
>> + = __node_gen(_S_fwd_value_for<_Tree>(*__x->_M_valptr()));
>
> Is _S_fwd_value_for necessary? This would work:
>
> #if __cplusplus >= 201103L
> using _Vp = typename
> conditional<is_lvalue_reference<_Tree>::value,
> const value_type&,
> value_type&&>::type;
> #else
> typedef const value_type& _Vp;
> #endif
> _Link_type __tmp
> = __node_gen(_GLIBCXX_FORWARD(_Vp, *__x->_M_valptr()));
>
> Or with the suggestion above, the typedef would be:
>
> using _Vp = typename conditional<_Move, value_type&&,
> const value_type&>::type;
>
>
>> __tmp->_M_color = __x->_M_color;
>> __tmp->_M_left = 0;
>> __tmp->_M_right = 0;
>> @@ -748,9 +756,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> { return this->_M_impl._M_header._M_right; }
>>
>> _Link_type
>> - _M_begin() _GLIBCXX_NOEXCEPT
>> + _M_cbegin() const _GLIBCXX_NOEXCEPT
>
> It's confusing that this is called cbegin but returns a non-const
> link. The standard cbegin() functions return a const_iterator. I would
> expect this to return a _Const_Link_type, based on the name.
>
>
>> { return
>> static_cast<_Link_type>(this->_M_impl._M_header._M_parent); }
>>
>> + _Link_type
>> + _M_begin() _GLIBCXX_NOEXCEPT
>> + { return _M_cbegin(); }
>> +
>> _Const_Link_type
>> _M_begin() const _GLIBCXX_NOEXCEPT
>> {
>> @@ -889,15 +901,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> _M_insert_equal_lower(const value_type& __x);
>> #endif
>>
>> - template<typename _NodeGen>
>> + template<typename _Tree, typename _NodeGen>
>> _Link_type
>> - _M_copy(_Const_Link_type __x, _Base_ptr __p, _NodeGen&);
>> + _M_copy(_GLIBCXX_XREF(_Tree), _Link_type, _Base_ptr, _NodeGen&);
>
> This could use 'bool _Move' instead of 'typename _Tree'.
>
>> - template<typename _NodeGen>
>> + template<typename _Tree, typename _NodeGen>
>> _Link_type
>> - _M_copy(const _Rb_tree& __x, _NodeGen& __gen)
>> + _M_copy(_GLIBCXX_XREF(_Tree) __x, _NodeGen& __gen)
>
> This one gets called from _M_copy(const _Rb_tree&) with const _Rb_tree
> argument, so I think using the XREF macro (or FWDREF) does make sense
> here.
>
>> {
>> - _Link_type __root = _M_copy(__x._M_begin(), _M_end(), __gen);
>> + _Link_type __root = _M_copy(_GLIBCXX_FORWARD(_Tree, __x),
>> + __x._M_cbegin(), _M_end(), __gen);
>
> This would have to be:
>
> #if __cplusplus >= 201103L
> constexpr bool __move = !is_reference<_Tree>::value;
> #else
> const bool __move = false;
> #endif
> _Link_type __root = _M_copy<__move>(__x._M_cbegin(),
> _M_end(), __gen);
>
> Would doing it this way make sense?
>
>
>> _M_leftmost() = _S_minimum(__root);
>> _M_rightmost() = _S_maximum(__root);
>> _M_impl._M_node_count = __x._M_impl._M_node_count;
>> @@ -1426,22 +1439,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> private:
>> // Move elements from container with equal allocator.
>> void
>> - _M_move_data(_Rb_tree& __x, true_type)
>> + _M_move_data(_Rb_tree&& __x, true_type)
>> { _M_impl._M_move_data(__x._M_impl); }
>>
>> // Move elements from container with possibly non-equal allocator,
>> // which might result in a copy not a move.
>> void
>> - _M_move_data(_Rb_tree&, false_type);
>> + _M_move_data(_Rb_tree&&, false_type);
>>
>> // Move assignment from container with equal allocator.
>> void
>> - _M_move_assign(_Rb_tree&, true_type);
>> + _M_move_assign(_Rb_tree&&, true_type);
>>
>> // Move assignment from container with possibly non-equal
>> allocator,
>> // which might result in a copy not a move.
>> void
>> - _M_move_assign(_Rb_tree&, false_type);
>> + _M_move_assign(_Rb_tree&&, false_type);
>
> These changes don't seem necessary. While they might be preferable if
> we were writing this from scratch, changing them now means that
> binaries built with more than one version of GCC will be larger.
> Objects built with older versions of GCC will have instantiations of
> the old versions and objects built with newer versions will have
> instantiations of the new versions. The increase in executable size
> (and icache pressure) doesn't seem worthwhile.
>
> The other changes (to remove the lambdas) also have this consequence,
> but the benefits of simplifying the code do seem worthwhile. Just
> changing _Rb_tree& to _Rb_tree&& (and having to use std::move to call
> those functions) doesn't simplify anything. The functions already have
> "move" in the name, so it's pretty obvious they perform moves.
>
>
>
>
>> @@ -1859,29 +1864,34 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>
>> template<typename _Key, typename _Val, typename _KoV,
>> typename _Compare, typename _Alloc>
>> - template<typename _NodeGen>
>> + template<typename _Tree, typename _NodeGen>
>> typename _Rb_tree<_Key, _Val, _KoV, _Compare, _Alloc>::_Link_type
>> _Rb_tree<_Key, _Val, _KoV, _Compare, _Alloc>::
>> - _M_copy(_Const_Link_type __x, _Base_ptr __p, _NodeGen&
>> __node_gen)
>> + _M_copy(_GLIBCXX_XREF(_Tree) __t,
>
> The __t parameter is only needed below to be forwarded, so its type
> can be checked later on. Using bool _Move instead would work fine, and
> avoid having to pass an extra parameter on the stack which never gets
> used (only its type).
>
>> + _Link_type __x, _Base_ptr __p, _NodeGen& __node_gen)
>> {
>> // Structural copy. __x and __p must be non-null.
>> - _Link_type __top = _M_clone_node(__x, __node_gen);
>> + _Link_type __top = _M_clone_node(_GLIBCXX_FORWARD(_Tree, __t),
>> + __x, __node_gen);
>> __top->_M_parent = __p;
>>
>> __try
>> {
>> if (__x->_M_right)
>> - __top->_M_right = _M_copy(_S_right(__x), __top, __node_gen);
>> + __top->_M_right = _M_copy(_GLIBCXX_FORWARD(_Tree, __t),
>> + _S_right(__x), __top, __node_gen);
>> __p = __top;
>> __x = _S_left(__x);
>>
>> while (__x != 0)
>> {
>> - _Link_type __y = _M_clone_node(__x, __node_gen);
>> + _Link_type __y = _M_clone_node(_GLIBCXX_FORWARD(_Tree, __t),
>> + __x, __node_gen);
>> __p->_M_left = __y;
>> __y->_M_parent = __p;
>> if (__x->_M_right)
>> - __y->_M_right = _M_copy(_S_right(__x), __y, __node_gen);
>> + __y->_M_right = _M_copy(_GLIBCXX_FORWARD(_Tree, __t),
>> + _S_right(__x), __y, __node_gen);
>> __p = __y;
>> __x = _S_left(__x);
>> }
>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: tree.patch
Type: text/x-patch
Size: 5946 bytes
Desc: not available
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20201119/846a12e5/attachment.bin>
More information about the Libstdc++
mailing list