JASL  [1.2.0]-2018-09-11
jasl_string.hpp
Go to the documentation of this file.
1 // JASL: For more information see https://github.com/matepek/jasl
2 //
3 // Copyright (c) 2018 Mate Pek
4 //
5 // This code is licensed under the MIT License (MIT).
6 
9 #pragma once
10 
11 #include <functional>
12 #include <memory>
13 #include <string>
14 
16 #include "jasl/jasl_internal/jasl_feature_test_macro.hpp"
17 #include "jasl/jasl_internal/jasl_string_view_bridge.hpp"
20 
21 namespace jasl {
22 
32 template <typename CharT,
33  typename Traits = std::char_traits<CharT>,
34  typename AllocatorT = std::allocator<CharT>>
36  : public inner::string_view_bridge<basic_string_view<CharT, Traits>> {
37  public:
40  typedef typename bridge_type::bridge_to_type base_type;
41  typedef AllocatorT allocator_type;
42 
43  private:
44  typedef std::allocator_traits<AllocatorT> alloc_traits;
45 
46  // https://stackoverflow.com/questions/12332772/why-arent-container-move-assignment-operators-noexcept
47  constexpr static bool is_noexcept_assign_rvalue =
48  alloc_traits::propagate_on_container_move_assignment::value &&
49  std::is_nothrow_move_assignable<AllocatorT>::value &&
50  JASL_is_nothrow_swappable_value(bridge_type);
51 
52  private:
53  allocator_type _alloc;
54  size_t _cap; // capacity
55 
56  private:
57  struct CStr {
58  size_t size;
59  const CharT* ptr;
60 
61 // constexpr: http://en.cppreference.com/w/cpp/string/char_traits/length
62 // char_traits:
63 // https://docs.microsoft.com/en-us/cpp/visual-cpp-language-conformance
64 #ifdef _MSC_VER
65 # define JASL_TEMP_CONSTEXPR_FOR_PW
66 #else
67 # define JASL_TEMP_CONSTEXPR_FOR_PW constexpr
68 #endif
69  JASL_TEMP_CONSTEXPR_FOR_PW CStr(const CharT* p) noexcept
70  : size(Traits::length(p)), ptr(p) {}
71 #undef JASL_TEMP_CONSTEXPR_FOR_PW
72  };
73 
74  inline void init(const CharT* ptr, size_t size) {
75  JASL_ASSERT(_cap == 0, "_cap == 0");
76  static_assert(
77  std::is_standard_layout<CharT>::value && std::is_trivial<CharT>::value,
78  "Unsupported CharT");
79  if (size == 0) {
80  bridge_type::set(nullptr, 0);
81  return;
82  }
83  std::unique_ptr<CharT, std::function<void(CharT*)>> begin(
84  alloc_traits::allocate(_alloc, size), [&](CharT * ptd) noexcept {
85  alloc_traits::deallocate(_alloc, ptd, size);
86  });
87  bridge_type::set(begin.get(), size);
88  _cap = size;
89  auto raw_begin = begin.release();
90  Traits::copy(raw_begin, ptr, size);
91  }
92 
93  inline void dispose() noexcept {
94  if (_cap > 0) {
95  const auto begin = const_cast<CharT*>(bridge_type::data());
96  alloc_traits::deallocate(_alloc, begin, _cap);
97  _cap = 0;
98  }
99  }
100 
101  basic_string(const bridge_type& other, const AllocatorT& alloc)
102  : bridge_type(), _alloc(alloc), _cap(0) {
103  init(other.data(), other.size());
104  }
105 
106  public:
107  ~basic_string() {
108  if (_cap > 0) {
109  alloc_traits::deallocate(_alloc, const_cast<CharT*>(bridge_type::data()),
110  _cap);
111  }
112  }
113 
114  basic_string() noexcept(
115  std::is_nothrow_constructible<bridge_type>::value&&
116  std::is_nothrow_default_constructible<AllocatorT>::value)
117  : basic_string(AllocatorT()) {}
118 
119  basic_string(const AllocatorT& a) noexcept(
120  std::is_nothrow_constructible<bridge_type>::value&&
121  std::is_nothrow_copy_constructible<AllocatorT>::value)
122  : bridge_type(), _alloc(a), _cap(0) {}
123 
124  basic_string(const CStr& cstr, const AllocatorT& alloc = AllocatorT())
125  : bridge_type(), _alloc(alloc), _cap(0) {
126  JASL_ASSERT(cstr.ptr != nullptr, "cstr != nullptr");
127  init(cstr.ptr, cstr.size);
128  }
129 
130  basic_string(const CharT* ptr,
131  size_t size,
132  const AllocatorT& alloc = AllocatorT())
133  : bridge_type(), _alloc(alloc), _cap(0) {
134  JASL_ASSERT(ptr != nullptr, "ptr != nullptr");
135  init(ptr, size);
136  }
137 
138  template <size_t N>
139  basic_string(const CharT (&str)[N]) noexcept(
140  std::is_nothrow_constructible<bridge_type, const CharT*, size_t>::value&&
141  std::is_nothrow_default_constructible<AllocatorT>::value)
142  : bridge_type(str, str[N - 1] == 0 ? N - 1 : N), _alloc(), _cap(0) {}
143 
144  template <size_t N>
145  basic_string(const CharT (&str)[N], const AllocatorT& alloc) noexcept(
146  std::is_nothrow_constructible<bridge_type, const CharT*, size_t>::value&&
147  std::is_nothrow_copy_constructible<AllocatorT>::value)
148  : bridge_type(str, str[N - 1] == 0 ? N - 1 : N), _alloc(alloc), _cap(0) {}
149 
150  basic_string(const basic_string& other)
151  : basic_string(
152  other,
153  alloc_traits::select_on_container_copy_construction(other._alloc)) {
154  }
155 
156  basic_string(const basic_string& other, const AllocatorT& alloc)
157  : bridge_type(other), _alloc(alloc), _cap(0) {
158  if (other._cap > 0) {
159  init(other.data(), other.size());
160  }
161  }
162 
163  basic_string(basic_string&& other) noexcept(
164  std::is_nothrow_move_constructible<bridge_type>::value&&
165  std::is_nothrow_move_constructible<AllocatorT>::value)
166  : bridge_type(std::move(other)),
167  _alloc(std::move(other._alloc)),
168  _cap(other._cap) {
169  other._cap = 0;
170  }
171 
172  basic_string(basic_string&& other, const AllocatorT& alloc)
173  : bridge_type(), _alloc(alloc), _cap(0) {
174  if (_alloc == other._alloc) {
175  bridge_type::set(other.data(), other.size());
176  std::swap(_cap, other._cap);
177  } else {
178  init(other.data(), other.size());
179  }
180  }
181 
183  std::is_nothrow_constructible<bridge_type, const CharT*, size_t>::value&&
184  std::is_nothrow_default_constructible<AllocatorT>::value)
185  : bridge_type(ss.data(), ss.size()), _alloc(), _cap(0) {}
186 
187  basic_string(
189  const AllocatorT&
190  alloc) noexcept(std::is_nothrow_constructible<bridge_type,
191  const CharT*,
192  size_t>::value&& std::
193  is_nothrow_copy_constructible<AllocatorT>::value)
194  : bridge_type(ss.data(), ss.size()), _alloc(alloc), _cap(0) {}
195 
196 #if defined(JASL_SUPPORT_STD_TO_JASL)
197 # if defined(JASL_cpp_lib_string_view)
198  template <
199  typename T,
200  typename = typename std::enable_if<
201  std::is_convertible<const T&,
202  std::basic_string_view<CharT, Traits>>::value &&
203  !std::is_convertible<const T&, const CharT*>::value>::type>
204  explicit basic_string(const T& s, const AllocatorT& alloc = AllocatorT())
205  : bridge_type(), _alloc(alloc), _cap(0) {
206  std::basic_string_view<CharT, Traits> sv(s);
207  init(sv.data(), sv.size());
208  }
209 
210  template <
211  typename T,
212  typename = typename std::enable_if<
213  std::is_convertible<const T&,
214  std::basic_string_view<CharT, Traits>>::value &&
215  !std::is_convertible<const T&, const CharT*>::value>::type>
216  basic_string& operator=(const T& s) {
217  dispose();
218  std::basic_string_view<CharT, Traits> sv(s);
219  init(sv.data(), sv.size());
220  return *this;
221  }
222 # else
223  template <
224  typename T,
225  typename = typename std::enable_if<std::is_same<
226  const T&,
227  const std::basic_string<CharT, Traits, AllocatorT>&>::value>::type>
228  explicit basic_string(const T& s, const AllocatorT& alloc = AllocatorT())
229  : bridge_type(), _alloc(alloc), _cap(0) {
230  init(s.data(), s.size());
231  }
232 
233  template <
234  typename T,
235  typename = typename std::enable_if<std::is_same<
236  const T&,
237  const std::basic_string<CharT, Traits, AllocatorT>&>::value>::type>
238  basic_string& operator=(const T& s) {
239  dispose();
240  init(s.data(), s.size());
241  return *this;
242  }
243 # endif
244 #endif
245 
246 #if defined(JASL_SUPPORT_JASL_TO_STD)
247 # if defined(JASL_cpp_lib_string_view)
248  operator std::basic_string_view<CharT, Traits>() const noexcept(
249  std::is_nothrow_constructible<std::basic_string_view<CharT, Traits>,
250  const CharT*,
251  size_t>::value) {
252  return std::basic_string_view<CharT, Traits>(bridge_type::data(),
253  bridge_type::size());
254  }
255 # else
256  operator std::basic_string<CharT, Traits, AllocatorT>() const
257  noexcept(std::is_nothrow_constructible<
258  std::basic_string<CharT, Traits, AllocatorT>,
259  const CharT*,
260  size_t>::value) {
261  return std::basic_string<CharT, Traits, AllocatorT>(bridge_type::data(),
262  bridge_type::size());
263  }
264 # endif
265 #endif
266 
267  template <size_t N>
268  basic_string& assign(const CharT (&str)[N]) noexcept(
269  bridge_type::is_nothrow_settable) {
270  dispose();
271  bridge_type::set(str, str[N - 1] == 0 ? N - 1 : N);
272  return *this;
273  }
274 
275  basic_string& assign(const CStr& cstr) {
276  dispose();
277  init(cstr.ptr, cstr.size);
278  return *this;
279  }
280 
281  basic_string& assign(const basic_string& other) {
282  if (this == &other) {
283  return *this;
284  }
285  /*propagate_on_container_copy_assignment
286  * true if the allocator of type A needs to be copied when the container
287  * that uses it is copy-assigned. Note that if the allocators of the source
288  * and the target containers do not compare equal, copy assignment has to
289  * deallocate the target's memory using the old allocator and then allocate
290  * it using the new allocator before copying the elements (and the
291  * allocator).
292  */
293  dispose();
294  // https://en.cppreference.com/w/cpp/concept/Allocator
295  if (alloc_traits::propagate_on_container_copy_assignment::value ||
296  _alloc != other._alloc) {
297  _alloc = other._alloc;
298  }
299  if (other._cap == 0) {
300  bridge_type::operator=(other);
301  } else {
302  init(other.data(), other.size());
303  }
304  return *this;
305  }
306 
307  basic_string& assign(basic_string&& other) noexcept(
308  is_noexcept_assign_rvalue) {
309  if (this == &other) {
310  return *this;
311  }
312  /*propagate_on_container_move_assignment:
313  * true if the allocator of type A needs to be moved when the container that
314  * uses it is move-assigned. If this member is false and the allocators of
315  * the source and the target containers do not compare equal, move
316  * assignment cannot take ownership of the source memory and must
317  * move-assign or move-construct the elements individually, resizing its own
318  * memory as needed.
319  */
320  dispose();
321  if (alloc_traits::propagate_on_container_move_assignment::value) {
322  _alloc = std::move(other._alloc);
323  } else if (_alloc != other._alloc) {
324  init(other.data(), other.size());
325  return *this;
326  }
327  bridge_type::swap(other);
328  std::swap(_cap, other._cap);
329  return *this;
330  }
331 
332  basic_string& assing(const basic_static_string<CharT, Traits>& ss) noexcept(
333  bridge_type::is_nothrow_settable) {
334  dispose();
335  bridge_type::set(ss.data(), ss.size());
336  return *this;
337  }
338 
339  template <size_t N>
340  basic_string& operator=(const CharT (&str)[N]) noexcept {
341  return assign<N>(str);
342  }
343 
344  basic_string& operator=(const basic_string& other) { return assign(other); }
345 
346  basic_string& operator=(basic_string&& other) noexcept(
347  is_noexcept_assign_rvalue) {
348  return assign(std::move(other));
349  }
350 
351  basic_string&
352  operator=(const basic_static_string<CharT, Traits>& other) noexcept(
353  bridge_type::is_nothrow_settable) {
354  return assign(other);
355  }
356 
357  constexpr bool is_static() const noexcept { return _cap == 0; }
358  AllocatorT get_alloc() const
359  noexcept(std::is_nothrow_copy_constructible<AllocatorT>::value) {
360  return _alloc;
361  }
362 
363  void swap(basic_string& other) noexcept(
364  (!alloc_traits::propagate_on_container_swap::value ||
365  JASL_is_nothrow_swappable_value(AllocatorT)) &&
366  JASL_is_nothrow_swappable_value(bridge_type)) {
367  using std::swap;
368  /*propagate_on_container_swap
369  * true if the allocators of type A need to be swapped when two containers
370  * that use them are swapped. If this member is false and the allocators of
371  * the two containers do not compare equal, the behavior of container swap
372  * is undefined.
373  */
374  if (alloc_traits::propagate_on_container_swap::value) {
375  swap(_alloc, other._alloc);
376  } else if (_alloc != other._alloc) {
377  JASL_ASSERT(false, "Undefined behaviour");
378  std::terminate();
379  }
380  bridge_type::swap(other);
381  swap(_cap, other._cap);
382  }
383 
384  basic_string substr(typename bridge_type::size_type pos) const {
385  if (_cap == 0) {
386  basic_string cpy(*this, _alloc);
387  cpy.bridge_type::operator=(bridge_type::substr(pos));
388  return cpy;
389  } else {
390  return basic_string(bridge_type::substr(pos), _alloc);
391  }
392  }
393 
394  basic_string substr(typename bridge_type::size_type pos,
395  typename bridge_type::size_type count) const {
396  if (_cap == 0) {
397  basic_string cpy(*this);
398  cpy.bridge_type::operator=(bridge_type::substr(pos, count));
399  return cpy;
400  } else {
401  return basic_string(bridge_type::substr(pos, count), _alloc);
402  }
403  }
404 };
405 
406 template <typename CharT, typename Traits>
407 void swap(basic_string<CharT, Traits>& lhs,
408  basic_string<CharT, Traits>& rhs) noexcept(noexcept(lhs.swap(rhs))) {
409  lhs.swap(rhs);
410 }
411 
412 typedef basic_string<char> string;
413 typedef basic_string<wchar_t> wstring;
414 typedef basic_string<char16_t> u16string;
415 typedef basic_string<char32_t> u32string;
416 
417 } // namespace jasl
Definition: jasl_static_string.hpp:18
Definition: jasl_string_view_bridge.hpp:17
Definition: jasl_string_view.hpp:401
Definition: jasl_string.hpp:35
Definition: jasl_static_string.hpp:36
Definition: jasl_string_view.hpp:24