Skip to content

[libc++] Refactor __split_buffer to eliminate code duplication #114138

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 4, 2024

Conversation

winner245
Copy link
Contributor

This PR refactors the __split_buffer class to eliminate code duplication in the push_back and push_front member functions.

Motivation:
The lvalue and rvalue reference overloads of push_back share identical logic, which coincides with that of emplace_back. Similarly, the overloads of push_front also share identical logic but lack an emplace_front member function, leading to an inconsistency. These identical internal logics lead to significant code duplication, making future maintenance more difficult.

Summary of Refactor:
This PR reduces code redundancy by:

  1. Modifying both overloads of push_back to call emplace_back.
  2. Introducing a new emplace_front member function that encapsulates the logic of push_front, allowing both overloads of push_front to call it (The addition of emplace_front also avoids the inconsistency regarding the absence of emplace_front).

The refactoring results in reduced code duplication, improved maintainability, and enhanced readability.

@winner245 winner245 requested a review from a team as a code owner October 29, 2024 22:23
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Oct 29, 2024
@llvmbot
Copy link
Member

llvmbot commented Oct 29, 2024

@llvm/pr-subscribers-libcxx

Author: Peng Liu (winner245)

Changes

This PR refactors the __split_buffer class to eliminate code duplication in the push_back and push_front member functions.

Motivation:
The lvalue and rvalue reference overloads of push_back share identical logic, which coincides with that of emplace_back. Similarly, the overloads of push_front also share identical logic but lack an emplace_front member function, leading to an inconsistency. These identical internal logics lead to significant code duplication, making future maintenance more difficult.

Summary of Refactor:
This PR reduces code redundancy by:

  1. Modifying both overloads of push_back to call emplace_back.
  2. Introducing a new emplace_front member function that encapsulates the logic of push_front, allowing both overloads of push_front to call it (The addition of emplace_front also avoids the inconsistency regarding the absence of emplace_front).

The refactoring results in reduced code duplication, improved maintainability, and enhanced readability.


Full diff: https://github.com/llvm/llvm-project/pull/114138.diff

1 Files Affected:

  • (modified) libcxx/include/__split_buffer (+12-55)
diff --git a/libcxx/include/__split_buffer b/libcxx/include/__split_buffer
index c4817601039f3b..99fe4059a0e15d 100644
--- a/libcxx/include/__split_buffer
+++ b/libcxx/include/__split_buffer
@@ -152,6 +152,8 @@ public:
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void push_front(value_type&& __x);
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void push_back(value_type&& __x);
 
+	template <class... _Args>
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void emplace_front(_Args&&... __args);
   template <class... _Args>
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void emplace_back(_Args&&... __args);
 
@@ -456,28 +458,17 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void __split_buffer<_Tp, _Allocator>::shrink_to_fi
 
 template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 void __split_buffer<_Tp, _Allocator>::push_front(const_reference __x) {
-  if (__begin_ == __first_) {
-    if (__end_ < __end_cap()) {
-      difference_type __d = __end_cap() - __end_;
-      __d                 = (__d + 1) / 2;
-      __begin_            = std::move_backward(__begin_, __end_, __end_ + __d);
-      __end_ += __d;
-    } else {
-      size_type __c = std::max<size_type>(2 * static_cast<size_t>(__end_cap() - __first_), 1);
-      __split_buffer<value_type, __alloc_rr&> __t(__c, (__c + 3) / 4, __alloc());
-      __t.__construct_at_end(move_iterator<pointer>(__begin_), move_iterator<pointer>(__end_));
-      std::swap(__first_, __t.__first_);
-      std::swap(__begin_, __t.__begin_);
-      std::swap(__end_, __t.__end_);
-      std::swap(__end_cap(), __t.__end_cap());
-    }
-  }
-  __alloc_traits::construct(__alloc(), std::__to_address(__begin_ - 1), __x);
-  --__begin_;
+  emplace_front(__x);
 }
 
 template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 void __split_buffer<_Tp, _Allocator>::push_front(value_type&& __x) {
+  emplace_front(std::move(__x));
+}
+
+template <class _Tp, class _Allocator>
+template <class... _Args>
+_LIBCPP_CONSTEXPR_SINCE_CXX20 void __split_buffer<_Tp, _Allocator>::emplace_front(_Args&&... __args) {
   if (__begin_ == __first_) {
     if (__end_ < __end_cap()) {
       difference_type __d = __end_cap() - __end_;
@@ -494,53 +485,19 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void __split_buffer<_Tp, _Allocator>::push_front(v
       std::swap(__end_cap(), __t.__end_cap());
     }
   }
-  __alloc_traits::construct(__alloc(), std::__to_address(__begin_ - 1), std::move(__x));
+  __alloc_traits::construct(__alloc(), std::__to_address(__begin_ - 1), std::forward<_Args>(__args)...);
   --__begin_;
 }
 
 template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 inline _LIBCPP_HIDE_FROM_ABI void
 __split_buffer<_Tp, _Allocator>::push_back(const_reference __x) {
-  if (__end_ == __end_cap()) {
-    if (__begin_ > __first_) {
-      difference_type __d = __begin_ - __first_;
-      __d                 = (__d + 1) / 2;
-      __end_              = std::move(__begin_, __end_, __begin_ - __d);
-      __begin_ -= __d;
-    } else {
-      size_type __c = std::max<size_type>(2 * static_cast<size_t>(__end_cap() - __first_), 1);
-      __split_buffer<value_type, __alloc_rr&> __t(__c, __c / 4, __alloc());
-      __t.__construct_at_end(move_iterator<pointer>(__begin_), move_iterator<pointer>(__end_));
-      std::swap(__first_, __t.__first_);
-      std::swap(__begin_, __t.__begin_);
-      std::swap(__end_, __t.__end_);
-      std::swap(__end_cap(), __t.__end_cap());
-    }
-  }
-  __alloc_traits::construct(__alloc(), std::__to_address(__end_), __x);
-  ++__end_;
+  emplace_back(__x);
 }
 
 template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 void __split_buffer<_Tp, _Allocator>::push_back(value_type&& __x) {
-  if (__end_ == __end_cap()) {
-    if (__begin_ > __first_) {
-      difference_type __d = __begin_ - __first_;
-      __d                 = (__d + 1) / 2;
-      __end_              = std::move(__begin_, __end_, __begin_ - __d);
-      __begin_ -= __d;
-    } else {
-      size_type __c = std::max<size_type>(2 * static_cast<size_t>(__end_cap() - __first_), 1);
-      __split_buffer<value_type, __alloc_rr&> __t(__c, __c / 4, __alloc());
-      __t.__construct_at_end(move_iterator<pointer>(__begin_), move_iterator<pointer>(__end_));
-      std::swap(__first_, __t.__first_);
-      std::swap(__begin_, __t.__begin_);
-      std::swap(__end_, __t.__end_);
-      std::swap(__end_cap(), __t.__end_cap());
-    }
-  }
-  __alloc_traits::construct(__alloc(), std::__to_address(__end_), std::move(__x));
-  ++__end_;
+  emplace_back(std::move(__x));
 }
 
 template <class _Tp, class _Allocator>

Copy link

github-actions bot commented Oct 29, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

@winner245 winner245 force-pushed the winner245/split_buffer branch from f65f8c2 to faae1ed Compare October 29, 2024 22:39
@frederick-vs-ja
Copy link
Contributor

This follows up #113481. CC @ldionne @philnik777.

Copy link
Contributor

@philnik777 philnik777 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The other patch removed some code duplication, but this introduces emplace_front and only uses it to implement push_front. What is the point of that?

@frederick-vs-ja
Copy link
Contributor

The other patch removed some code duplication, but this introduces emplace_front and only uses it to implement push_front. What is the point of that?

There're currently 2 push_front overloads, taking const value_type& and value_type&& respectively, containing almost identical code. The newly introduced emplace_front makes the code only written once.

@philnik777
Copy link
Contributor

Ah, I missed that. Given that we control all the uses of the type I think it'd be better to just replace any uses of push_front with emplace_front (and the same for push_back).

@winner245
Copy link
Contributor Author

Thank you, philnik777, for your insightful feedback. I also appreciate frederick-vs-ja's assistance in clarifying the purpose of my refactor, which effectively highlighted the code duplication issue with the current push_front implementation.

Considering philnik777's suggestion to replace any uses of __split_buffer::push_front and __split_buffer::push_back with their emplace_xxx equivalents, I reviewed the current usage of __split_buffer. It appears that it is primarily utilized by std::vector and std::deque. Therefore, the proposed changes based on philnik777's suggestion would be:

  • Remove the two overloads of __split_buffer::push_back and retain only __split_buffer::emplace_back.
  • Remove the two overloads of __split_buffer::push_front, while adding __split_buffer::emplace_front (noting that __split_buffer currently does not provide emplace_front).
  • Modify std::vector and std::deque to replace all instances of __split_buffer::push_front and __split_buffer::push_back with the emplace_xxx methods.

This approach seems to provide a more comprehensive refactor. However, I would like to hear @ldionne and @frederick-vs-ja's perspectives on this before proceeding. Thank you.

@frederick-vs-ja
Copy link
Contributor

Let's just replace all __split_buffer::push_{front,back}. The changes are still simple and clear to me.

It appears that it is primarily utilized by std::vector and std::deque.

That's all. It is not used elsewhere.

@winner245 winner245 force-pushed the winner245/split_buffer branch from faae1ed to c248816 Compare October 31, 2024 15:08
@frederick-vs-ja
Copy link
Contributor

CI failures are unrelated (due to missed inclusion in unrelated headers). You can try to rebase now.

@winner245 winner245 force-pushed the winner245/split_buffer branch from c248816 to 13eec16 Compare November 1, 2024 13:49
@winner245
Copy link
Contributor Author

Thanks. I'll proceed with rebase now.

@winner245 winner245 force-pushed the winner245/split_buffer branch from 13eec16 to 4a2ff6b Compare November 2, 2024 16:33
@philnik777 philnik777 merged commit f1c341c into llvm:main Nov 4, 2024
62 checks passed
PhilippRados pushed a commit to PhilippRados/llvm-project that referenced this pull request Nov 6, 2024
…114138)

This PR refactors the `__split_buffer` class to eliminate code
duplication in the `push_back` and `push_front` member functions.

**Motivation:**  
The lvalue and rvalue reference overloads of `push_back` share identical
logic, which coincides with that of `emplace_back`. Similarly, the
overloads of `push_front` also share identical logic but lack an
`emplace_front` member function, leading to an inconsistency. These
identical internal logics lead to significant code duplication, making
future maintenance more difficult.

**Summary of Refactor:**  
This PR reduces code redundancy by:
1. Modifying both overloads of `push_back` to call `emplace_back`.
2. Introducing a new `emplace_front` member function that encapsulates
the logic of `push_front`, allowing both overloads of `push_front` to
call it (The addition of `emplace_front` also avoids the inconsistency
regarding the absence of `emplace_front`).

The refactoring results in reduced code duplication, improved
maintainability, and enhanced readability.
@winner245 winner245 deleted the winner245/split_buffer branch November 8, 2024 15:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants