├── README.md
├── all_in_one.hpp
├── error.cpp
├── include
├── bit_cast.hpp
├── compiler.hpp
├── error.hpp
├── intrusive_ptr.hpp
├── launder.hpp
├── string_ref.hpp
└── type_traits.hpp
└── test.cpp
/README.md:
--------------------------------------------------------------------------------
1 | # std_error
2 | Implementation of `std::error` as proposed by Herb Sutter in [Zero-Overhead Deterministic Exceptions](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0709r0.pdf)
3 |
4 | Requires at least C++14. Tested on GCC-4.9.2 to GCC 10, Clang 4 to Clang 10, and MSVC 19.14 to 19.24
5 |
6 | All-in-one header-only link: https://github.com/charles-salvia/std_error/blob/master/all_in_one.hpp
7 | Example usage/godbolt link: https://godbolt.org/z/8o1Yg6
8 | Full unit tests on godbolt: https://godbolt.org/z/TbuRHh
9 |
10 | Associated paper: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2170r0.html
11 |
12 | **Example**
13 | ```c++
14 | // store an std::exception_ptr in an error
15 | stdx::error e = std::make_exception_ptr(std::invalid_argument{"missing pants"});
16 | assert(e == std::errc::invalid_argument);
17 | assert(e == stdx::dynamic_exception_errc::invalid_argument);
18 | assert(e.message() == "missing pants");
19 | assert(e.domain() == stdx::dynamic_exception_domain);
20 |
21 | try { e.throw_exception(); }
22 | catch (const std::invalid_argument& a) { assert(stdx::string_ref{a.what()} == "missing pants"); };
23 |
24 | e = std::make_exception_ptr(std::logic_error{"abc"});
25 | assert(e == stdx::dynamic_exception_errc::logic_error);
26 | assert(e.message() == "abc");
27 |
28 | // store an std::errc enum
29 | e = std::errc::bad_file_descriptor;
30 | assert(e == std::errc::bad_file_descriptor);
31 | assert(e.message() == std::make_error_code(std::errc::bad_file_descriptor).message().c_str());
32 | assert(e.domain() == stdx::generic_domain);
33 |
34 | // store an std::error_code
35 | e = std::make_error_code(std::io_errc::stream);
36 | assert(e == std::make_error_code(std::io_errc::stream));
37 | assert(e.domain() == stdx::error_code_domain);
38 |
39 | // store an std::exception_ptr, weak-equality comparison with std::errc enum
40 | e = std::make_exception_ptr(std::system_error{std::make_error_code(std::errc::host_unreachable)});
41 | assert(e.domain() == stdx::dynamic_exception_domain);
42 | assert(e == std::errc::host_unreachable);
43 | assert(e.message() == std::make_error_code(std::errc::host_unreachable).message().c_str());
44 | ```
45 |
--------------------------------------------------------------------------------
/all_in_one.hpp:
--------------------------------------------------------------------------------
1 | #ifndef STDX_COMPILER_HPP
2 | #define STDX_COMPILER_HPP
3 |
4 | // Check compiler macros. Note that Clang defines __GNUC__ and other GNU macros as well,
5 | // but GNU does not define Clang macros, so we must check for Clang first.
6 |
7 | #if defined(__llvm__) || defined(__clang__)
8 |
9 | // -------- LLVM/Clang
10 |
11 | #define STDX_CLANG_COMPILER 1
12 |
13 | #if defined(__cpp_variable_templates) && (__cplusplus >= 201703L)
14 | #define STDX_VARIABLE_TEMPLATES 1
15 | #endif
16 |
17 | #elif defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__)
18 |
19 | // -------- GNU G++
20 |
21 | #include
22 |
23 | #define STDX_GCC_COMPILER 1
24 |
25 | #if (__GNUC__ >= 5) && (__cplusplus >= 201703L)
26 | #define STDX_VARIABLE_TEMPLATES 1
27 | #endif
28 |
29 | #if ((__GNUC__ > 7) || ((__GNUC__ == 7) && (__GNUC_MINOR__ >= 1)))
30 | #define STDX_TRIVIALLY_MOVE_CONSTRUCTIBLE 1
31 | #endif
32 |
33 | #endif
34 |
35 | #if defined(STDX_GCC_COMPILER)
36 | #if (__GNUC__ == 7) && ((__GNUC_MINOR__ >= 1) && (__GNUC_MINOR__ <= 3))
37 | #define STDX_GCC7_WORKAROUND_CONSTEXPR
38 | #else
39 | #define STDX_GCC7_WORKAROUND_CONSTEXPR constexpr
40 | #endif
41 | #else
42 | #define STDX_GCC7_WORKAROUND_CONSTEXPR constexpr
43 | #endif
44 |
45 | // Add a legacy constexpr macro for cases where GCC < 5 incorrectly applies the const
46 | // qualifier to constexpr member functions or does not support relaxed constexpr functions
47 | //
48 | #if defined(STDX_GCC_COMPILER) && (__GNUC__ < 5)
49 | #define STDX_LEGACY_CONSTEXPR
50 | #else
51 | #define STDX_LEGACY_CONSTEXPR constexpr
52 | #endif
53 |
54 | #if defined(_MSC_VER) && (_MSC_VER >= 1910)
55 | #define STDX_MSVC_EMPTY_BASE_CLASSES __declspec(empty_bases)
56 | #else
57 | #define STDX_MSVC_EMPTY_BASE_CLASSES
58 | #endif
59 |
60 | #if defined(__cpp_impl_trivially_relocatable)
61 | #define STDX_TRIVIALLY_RELOCATABLE [[trivially_relocatable]]
62 | #else
63 | #define STDX_TRIVIALLY_RELOCATABLE
64 | #endif
65 |
66 | #endif // STDX_COMPILER_HPP
67 |
68 |
69 | #ifndef STDX_TYPE_TRAITS_HPP
70 | #define STDX_TYPE_TRAITS_HPP
71 |
72 | #include
73 |
74 | namespace stdx {
75 |
76 | // Implementation of std::void_t for use with pre-C++17 compilers.
77 | //
78 | namespace detail {
79 |
80 | template
81 | struct void_t_impl
82 | {
83 | using type = void;
84 | };
85 |
86 | } // end namespace detail
87 |
88 | template
89 | using void_t = typename detail::void_t_impl::type;
90 |
91 | template
92 | struct dependent_type
93 | {
94 | using type = U;
95 | };
96 |
97 | template
98 | using dependent_type_t = typename dependent_type::type;
99 |
100 | template
101 | struct disjunction : std::false_type
102 | { };
103 |
104 | template
105 | struct disjunction : B1
106 | { };
107 |
108 | template
109 | struct disjunction
110 | :
111 | std::conditional_t<
112 | bool(B1::value),
113 | B1,
114 | disjunction
115 | >
116 | { };
117 |
118 | struct sentinel_type
119 | {
120 | static constexpr bool value = true;
121 | using type = void;
122 | };
123 |
124 | template
125 | struct bool_constant : std::integral_constant
126 | { };
127 |
128 | // Implementation of std::remove_cvref for use with pre-C++20 compilers.
129 | //
130 | template
131 | struct remove_cvref
132 | {
133 | using type = std::remove_cv_t>;
134 | };
135 |
136 | template
137 | using remove_cvref_t = typename remove_cvref::type;
138 |
139 | #if defined(STDX_GCC_COMPILER)
140 | // Support missing functionality on older compilers (<= gcc 4.7)
141 | //
142 | #if (__GNUC__ == 4) && (__GNUC_MINOR__ <= 7)
143 | // Map the old incorrect type-trait names to the newer correct ones
144 | template
145 | using is_trivially_copyable = std::is_trivial;
146 |
147 | template
148 | using is_trivially_copy_constructible = std::has_trivial_copy_constructor;
149 |
150 | template
151 | using is_trivially_destructible = std::has_trivial_destructor;
152 | #elif (__GNUC__ < 5)
153 | template
154 | using is_trivially_copyable = std::is_trivial;
155 |
156 | template
157 | using is_trivially_copy_constructible = std::has_trivial_copy_constructor;
158 |
159 | template
160 | using is_trivially_destructible = std::is_trivially_destructible;
161 | #else
162 | using std::is_trivially_destructible;
163 | using std::is_trivially_copyable;
164 | using std::is_trivially_copy_constructible;
165 | #endif
166 | #else
167 | using std::is_trivially_destructible;
168 | using std::is_trivially_copyable;
169 | using std::is_trivially_copy_constructible;
170 | #endif
171 |
172 | #if defined(STDX_TRIVIALLY_MOVE_CONSTRUCTIBLE)
173 | template
174 | using is_trivially_move_constructible = std::is_trivially_move_constructible;
175 | #else
176 | template
177 | using is_trivially_move_constructible = is_trivially_copyable;
178 | #endif
179 |
180 | #if defined(__cpp_lib_trivially_relocatable)
181 | using std::is_trivially_relocatable;
182 | #elif defined(__has_builtin)
183 | #if __has_builtin(__is_trivially_relocatable)
184 | template
185 | struct is_trivially_relocatable : std::bool_constant<__is_trivially_relocatable(T)> { };
186 | #define STDX_MUST_SPECIALIZE_IS_TRIVIALLY_RELOCATABLE
187 | #else
188 | template
189 | struct is_trivially_relocatable : is_trivially_copyable { };
190 | #define STDX_MUST_SPECIALIZE_IS_TRIVIALLY_RELOCATABLE
191 | #endif
192 | #else
193 | template
194 | struct is_trivially_relocatable : is_trivially_copyable { };
195 | #define STDX_MUST_SPECIALIZE_IS_TRIVIALLY_RELOCATABLE
196 | #endif
197 |
198 | #if __cplusplus >= 201703L
199 | #define STDX_LEGACY_INLINE_CONSTEXPR inline constexpr
200 | #else
201 | #define STDX_LEGACY_INLINE_CONSTEXPR constexpr
202 | #endif
203 |
204 | } // end namespace stdx
205 |
206 | #endif
207 |
208 |
209 |
210 | #ifndef STDX_BIT_CAST_HPP
211 | #define STDX_BIT_CAST_HPP
212 |
213 | #include
214 | #include
215 |
216 |
217 | namespace stdx {
218 |
219 | namespace detail {
220 |
221 | template
222 | using use_static_cast = bool_constant<
223 | ((std::is_integral::value || std::is_enum::value)
224 | && (std::is_integral::value || std::is_enum::value))
225 | || (std::is_same::value && std::is_copy_constructible::value)
226 | >;
227 |
228 | template
229 | using is_integral_ptr_t = bool_constant<
230 | std::is_same::value
231 | || std::is_same::value
232 | >;
233 |
234 | template
235 | using use_reinterpret_cast = bool_constant<
236 | !std::is_same::value
237 | && ((
238 | std::is_pointer::value
239 | && std::is_pointer::value
240 | && std::is_convertible::value
241 | )
242 | || (std::is_pointer::value && is_integral_ptr_t::value)
243 | || (std::is_pointer::value && is_integral_ptr_t::value)
244 | )
245 | >;
246 |
247 | #if defined(STDX_GCC_COMPILER)
248 | template
249 | using use_union_type_punning = bool_constant<
250 | !use_static_cast::value
251 | && !use_reinterpret_cast::value
252 | && !std::is_array::value
253 | && !std::is_array::value
254 | >;
255 |
256 | template
257 | union bit_cast_union
258 | {
259 | From from;
260 | To to;
261 | };
262 | #else
263 | template
264 | using use_union_type_punning = std::false_type;
265 | #endif
266 |
267 | template
268 | using can_bit_cast = bool_constant<
269 | (sizeof(To) == sizeof(From))
270 | && is_trivially_copyable::value
271 | && is_trivially_copyable::value
272 | >;
273 |
274 | } // end namespace detail
275 |
276 | template <
277 | class To,
278 | class From,
279 | class = std::enable_if_t<
280 | detail::can_bit_cast::value
281 | && detail::use_static_cast::value
282 | >
283 | >
284 | constexpr To bit_cast(const From& from) noexcept
285 | {
286 | return static_cast(from);
287 | }
288 |
289 | template <
290 | class To,
291 | class From,
292 | class = std::enable_if_t<
293 | detail::can_bit_cast::value
294 | && detail::use_reinterpret_cast::value
295 | >,
296 | int = 0
297 | >
298 | constexpr To bit_cast(const From& from) noexcept
299 | {
300 | return reinterpret_cast(from);
301 | }
302 |
303 | #if defined(STDX_GCC_COMPILER) // GCC allows union type punning
304 | template <
305 | class To,
306 | class From,
307 | class = std::enable_if_t<
308 | detail::can_bit_cast::value
309 | && detail::use_union_type_punning::value
310 | >,
311 | class = void
312 | >
313 | constexpr To bit_cast(const From& from) noexcept
314 | {
315 | return detail::bit_cast_union{from}.to;
316 | }
317 | #elif defined(STDX_CLANG_COMPILER)
318 | #if __has_builtin(__builtin_bit_cast)
319 | template <
320 | class To,
321 | class From,
322 | class = std::enable_if_t<
323 | detail::can_bit_cast::value
324 | && !detail::use_static_cast::value
325 | && !detail::use_reinterpret_cast::value
326 | >,
327 | class = void
328 | >
329 | constexpr To bit_cast(const From& from) noexcept
330 | {
331 | return __builtin_bit_cast(To, from);
332 | }
333 | #else
334 | template <
335 | class To,
336 | class From,
337 | class = std::enable_if_t<
338 | detail::can_bit_cast::value
339 | && !detail::use_static_cast::value
340 | && !detail::use_reinterpret_cast::value
341 | >,
342 | class = void
343 | >
344 | To bit_cast(const From& from) noexcept
345 | {
346 | To to;
347 | std::memcpy(&to, &from, sizeof(To));
348 | return to;
349 | }
350 | #endif
351 | #endif // STDX_CLANG_COMPILER
352 |
353 | template
354 | struct is_bit_castable
355 | :
356 | bool_constant<
357 | (sizeof(To) == sizeof(From))
358 | && is_trivially_copyable::value
359 | && is_trivially_copyable::value
360 | >
361 | { };
362 |
363 | } // end namespace stdx
364 |
365 | #endif
366 |
367 |
368 |
369 | #ifndef STDX_INTRUSIVE_POINTER_HPP
370 | #define STDX_INTRUSIVE_POINTER_HPP
371 |
372 |
373 | #include
374 | #include
375 | #include
376 |
377 | namespace stdx {
378 |
379 | class default_intrusive_reference_count;
380 | class default_intrusive_reference_control;
381 |
382 | template <
383 | class T,
384 | class RefCountAccessor = default_intrusive_reference_count,
385 | class Deleter = std::default_delete,
386 | class Pointer = T*
387 | >
388 | class intrusive_ptr;
389 |
390 | using ref_count_t = std::size_t;
391 |
392 | struct enable_reference_count
393 | {
394 | protected:
395 |
396 | constexpr enable_reference_count() noexcept : m_reference_count(1)
397 | { }
398 |
399 | public:
400 |
401 | std::atomic& shared_reference_count() noexcept
402 | {
403 | return m_reference_count;
404 | }
405 |
406 | private:
407 |
408 | std::atomic m_reference_count;
409 | };
410 |
411 | struct default_intrusive_reference_count
412 | {
413 | template
414 | std::atomic& operator()(Pointer p) const noexcept
415 | {
416 | return p->shared_reference_count();
417 | }
418 | };
419 |
420 | namespace detail {
421 |
422 | template
423 | struct pointer_wrapper
424 | {
425 | constexpr pointer_wrapper() noexcept : shared_object(nullptr)
426 | { }
427 |
428 | constexpr explicit pointer_wrapper(Pointer p) noexcept : shared_object(p)
429 | { }
430 |
431 | constexpr pointer_wrapper(const pointer_wrapper& p) noexcept = default;
432 |
433 | pointer_wrapper(pointer_wrapper&& p) noexcept
434 | : shared_object(p.shared_object)
435 | {
436 | p.shared_object = nullptr;
437 | }
438 |
439 | pointer_wrapper& operator = (const pointer_wrapper&) noexcept = default;
440 |
441 | pointer_wrapper& operator = (pointer_wrapper&& p) noexcept
442 | {
443 | shared_object = p.shared_object;
444 | p.shared_object = nullptr;
445 | return *this;
446 | }
447 |
448 | void assign(Pointer ptr) noexcept
449 | {
450 | shared_object = ptr;
451 | }
452 |
453 | Pointer shared_object;
454 | };
455 |
456 | template <
457 | class T,
458 | class RefCountAccessor,
459 | class Deleter,
460 | class Pointer,
461 | class PointerImplementation
462 | >
463 | class intrusive_ptr_base
464 | {
465 | protected:
466 |
467 | using pointer = Pointer;
468 | using count_type = ref_count_t;
469 |
470 | template
471 | friend class reference_count_base;
472 |
473 | constexpr intrusive_ptr_base() = default;
474 |
475 | constexpr intrusive_ptr_base(pointer p) noexcept
476 | : m_impl(p)
477 | { }
478 |
479 | template
480 | constexpr explicit intrusive_ptr_base(
481 | RefCountAccessorForwardingReference&& f,
482 | std::enable_if_t<
483 | std::is_constructible<
484 | RefCountAccessor,
485 | RefCountAccessorForwardingReference&&
486 | >::value
487 | >* = nullptr
488 | )
489 | : m_impl(std::forward(f))
490 | { }
491 |
492 | template
493 | explicit intrusive_ptr_base(
494 | pointer ptr,
495 | RefCountAccessorForwardingReference&& f,
496 | std::enable_if_t<
497 | std::is_constructible<
498 | RefCountAccessor,
499 | RefCountAccessorForwardingReference&&
500 | >::value
501 | >* = nullptr
502 | )
503 | : m_impl(ptr, std::forward(f))
504 | { }
505 |
506 | template <
507 | class RefCountAccessorForwardingReference,
508 | class DeleterForwardingReference
509 | >
510 | constexpr explicit intrusive_ptr_base(
511 | RefCountAccessorForwardingReference&& f,
512 | DeleterForwardingReference&& d,
513 | typename std::enable_if<
514 | std::is_constructible<
515 | RefCountAccessor,
516 | RefCountAccessorForwardingReference&&
517 | >::value
518 | && std::is_constructible<
519 | Deleter,
520 | DeleterForwardingReference&&
521 | >::value
522 | >::type* = nullptr
523 | )
524 | :
525 | m_impl(
526 | std::forward(f),
527 | std::forward(d)
528 | )
529 | { }
530 |
531 | template <
532 | class RefCountAccessorForwardingReference,
533 | class DeleterForwardingReference
534 | >
535 | explicit intrusive_ptr_base(
536 | pointer ptr,
537 | RefCountAccessorForwardingReference&& f,
538 | DeleterForwardingReference&& d,
539 | std::enable_if_t<
540 | std::is_constructible<
541 | RefCountAccessor,
542 | RefCountAccessorForwardingReference&&
543 | >::value
544 | && std::is_constructible<
545 | Deleter,
546 | DeleterForwardingReference&&
547 | >::value
548 | >* = nullptr
549 | )
550 | :
551 | m_impl(
552 | ptr,
553 | std::forward(f),
554 | std::forward(d)
555 | )
556 | { }
557 |
558 | void assign(pointer ptr) noexcept
559 | {
560 | m_impl.assign(ptr);
561 | }
562 |
563 | template <
564 | class RefCountAccessorForwardingReference,
565 | class DeleterForwardingReference
566 | >
567 | void assign(
568 | pointer ptr,
569 | RefCountAccessorForwardingReference&& f,
570 | DeleterForwardingReference&& d
571 | )
572 | {
573 | m_impl.assign(
574 | ptr,
575 | std::forward(f),
576 | std::forward(d)
577 | );
578 | }
579 |
580 | void swap(intrusive_ptr_base& other)
581 | {
582 | m_impl.swap(other.m_impl);
583 | }
584 |
585 | struct STDX_MSVC_EMPTY_BASE_CLASSES impl
586 | :
587 | PointerImplementation,
588 | RefCountAccessor,
589 | Deleter
590 | {
591 | constexpr impl() = default;
592 |
593 | explicit impl(pointer ptr) noexcept : PointerImplementation(ptr)
594 | { }
595 |
596 | template <
597 | class RefCountAccess,
598 | class = std::enable_if_t<
599 | std::is_constructible::value
600 | >
601 | >
602 | constexpr explicit impl(RefCountAccess&& f)
603 | : RefCountAccessor(std::forward(f))
604 | { }
605 |
606 | template <
607 | class RefCountAccess,
608 | class = std::enable_if_t<
609 | std::is_constructible::value
610 | >
611 | >
612 | impl(pointer ptr, RefCountAccess&& f)
613 | :
614 | PointerImplementation(ptr),
615 | RefCountAccessor(std::forward(f))
616 | { }
617 |
618 | template <
619 | class RefCountAccess,
620 | class D,
621 | class = std::enable_if_t<
622 | std::is_constructible::value
623 | && std::is_constructible::value
624 | >
625 | >
626 | constexpr impl(RefCountAccess&& f, D&& d)
627 | :
628 | RefCountAccessor(std::forward(f)),
629 | Deleter(std::forward(d))
630 | { }
631 |
632 | template <
633 | class RefCountAccess,
634 | class D,
635 | class = std::enable_if_t<
636 | std::is_constructible::value
637 | && std::is_constructible::value
638 | >
639 | >
640 | impl(pointer ptr, RefCountAccess&& f, D&& d)
641 | :
642 | PointerImplementation(ptr),
643 | RefCountAccessor(std::forward(f)),
644 | Deleter(std::forward(d))
645 | { }
646 |
647 | impl(const impl&) = default;
648 | impl& operator = (const impl&) = default;
649 | impl(impl&&) = default;
650 | impl& operator = (impl&&) = default;
651 |
652 | Deleter& get_deleter() noexcept
653 | {
654 | return static_cast(*this);
655 | }
656 |
657 | const Deleter& get_deleter() const noexcept
658 | {
659 | return static_cast(*this);
660 | }
661 |
662 | void assign(pointer ptr) noexcept
663 | {
664 | static_cast(*this).assign(ptr);
665 | }
666 |
667 | void assign(std::nullptr_t) noexcept
668 | {
669 | static_cast(*this).assign(nullptr);
670 | }
671 |
672 | void swap(impl& other)
673 | {
674 | std::swap(
675 | static_cast(*this),
676 | static_cast(other)
677 | );
678 |
679 | std::swap(static_cast(*this), static_cast(other));
680 | std::swap(
681 | static_cast(this->get_deleter()),
682 | static_cast(other.get_deleter())
683 | );
684 | }
685 | };
686 |
687 | impl m_impl;
688 |
689 | void increment_shared_reference_count(
690 | std::memory_order order = std::memory_order_relaxed
691 | ) const noexcept
692 | {
693 | if (ptr()) ref_count_func()(ptr()).fetch_add(1, order);
694 | }
695 |
696 | void decrement_shared_reference_count() noexcept
697 | {
698 | if (ptr())
699 | {
700 | if (ref_count_func()(ptr()).fetch_sub(1, std::memory_order_release) == 1)
701 | {
702 | std::atomic_thread_fence(std::memory_order_acquire);
703 | invoke_deleter(ptr());
704 | }
705 | }
706 | }
707 |
708 | // ----- accessors and modifiers
709 |
710 | pointer& ptr() noexcept
711 | {
712 | return static_cast(m_impl).shared_object;
713 | }
714 |
715 | constexpr pointer ptr() const noexcept
716 | {
717 | return static_cast(m_impl).shared_object;
718 | }
719 |
720 | RefCountAccessor& ref_count_func() noexcept
721 | {
722 | return static_cast(m_impl);
723 | }
724 |
725 | const RefCountAccessor& ref_count_func() const noexcept
726 | {
727 | return static_cast(m_impl);
728 | }
729 |
730 | Deleter& deleter() noexcept
731 | {
732 | return m_impl.get_deleter();
733 | }
734 |
735 | const Deleter& deleter() const noexcept
736 | {
737 | return m_impl.get_deleter();
738 | }
739 |
740 | intrusive_ptr<
741 | T,
742 | RefCountAccessor,
743 | Deleter,
744 | Pointer
745 | > make_intrusive_pointer(pointer p) const noexcept
746 | {
747 | return intrusive_ptr{
748 | p,
749 | ref_count_func(),
750 | deleter()
751 | };
752 | }
753 |
754 | private:
755 |
756 | void invoke_deleter(pointer p)
757 | {
758 | m_impl.get_deleter()(p);
759 | }
760 |
761 | void invoke_deleter(pointer p) const
762 | {
763 | m_impl.get_deleter()(p);
764 | }
765 |
766 | template
767 | void invoke_deleter(pointer p, WeakReferenceCountDescriptor* d)
768 | {
769 | maybe_delete_shared_object(p, m_impl.get_deleter(), d);
770 | }
771 |
772 | template
773 | void invoke_deleter(pointer p, WeakReferenceCountDescriptor* d) const
774 | {
775 | maybe_delete_shared_object(p, m_impl.get_deleter(), d);
776 | }
777 | };
778 |
779 | } // end namespace detail
780 |
781 | template <
782 | class T,
783 | class RefCountAccessor,
784 | class Deleter,
785 | class Pointer
786 | >
787 | class STDX_TRIVIALLY_RELOCATABLE intrusive_ptr
788 | :
789 | public detail::intrusive_ptr_base<
790 | T,
791 | RefCountAccessor,
792 | Deleter,
793 | Pointer,
794 | detail::pointer_wrapper
795 | >
796 | {
797 | using base_type = detail::intrusive_ptr_base<
798 | T,
799 | RefCountAccessor,
800 | Deleter,
801 | Pointer,
802 | detail::pointer_wrapper
803 | >;
804 |
805 | public:
806 |
807 | using pointer = Pointer;
808 | using element_type = T;
809 | using ref_count_accessor = RefCountAccessor;
810 | using deleter_type = Deleter;
811 | using count_type = typename base_type::count_type;
812 |
813 | constexpr intrusive_ptr() noexcept : base_type()
814 | { }
815 |
816 | constexpr intrusive_ptr(std::nullptr_t) noexcept : base_type()
817 | { }
818 |
819 | template
820 | constexpr intrusive_ptr(std::nullptr_t, RefCountAccess&& f)
821 | : base_type(std::forward(f))
822 | { }
823 |
824 | template
825 | constexpr intrusive_ptr(std::nullptr_t, RefCountAccess&& f, D&& d)
826 | : base_type(std::forward(f), std::forward(d))
827 | { }
828 |
829 | constexpr explicit intrusive_ptr(Pointer ptr) noexcept
830 | : base_type(ptr)
831 | {
832 | // reference count must initially be >= 1
833 | }
834 |
835 | template
836 | intrusive_ptr(Pointer ptr, RefCountAccess&& f) noexcept
837 | : base_type(ptr, std::forward(f))
838 | {
839 | // reference count must initially be >= 1
840 | }
841 |
842 | template
843 | intrusive_ptr(Pointer ptr, RefCountAccess&& f, D&& d) noexcept
844 | :
845 | base_type(
846 | ptr,
847 | std::forward(f),
848 | std::forward(d)
849 | )
850 | {
851 | // reference count must initially be >= 1
852 | }
853 |
854 | // Copy constructor
855 | //
856 | intrusive_ptr(const intrusive_ptr& rhs) noexcept
857 | : base_type(rhs)
858 | {
859 | this->increment_shared_reference_count();
860 | }
861 |
862 | // Converting copy-constructor
863 | //
864 | template <
865 | class Y,
866 | class Ptr,
867 | class = std::enable_if_t::value>
868 | >
869 | intrusive_ptr(const intrusive_ptr& rhs) noexcept
870 | :
871 | base_type(
872 | rhs.get(),
873 | rhs.ref_count_func(),
874 | rhs.get_deleter()
875 | )
876 | {
877 | this->increment_shared_reference_count();
878 | }
879 |
880 | // Move constructor
881 | //
882 | intrusive_ptr(intrusive_ptr&& rhs) noexcept
883 | : base_type(std::move(rhs))
884 | { }
885 |
886 | // Copy assignment
887 | //
888 | intrusive_ptr& operator = (const intrusive_ptr& rhs) noexcept
889 | {
890 | rhs.increment_shared_reference_count();
891 | this->decrement_shared_reference_count();
892 |
893 | static_cast(*this) = static_cast(rhs);
894 | return *this;
895 | }
896 |
897 | // Move assignment
898 | //
899 | intrusive_ptr& operator = (intrusive_ptr&& rhs) noexcept
900 | {
901 | if (this != std::addressof(rhs))
902 | {
903 | this->decrement_shared_reference_count();
904 | static_cast(*this) = std::move(static_cast(rhs));
905 | }
906 |
907 | return *this;
908 | }
909 |
910 | ~intrusive_ptr() noexcept
911 | {
912 | this->decrement_shared_reference_count();
913 | }
914 |
915 | void reset() noexcept
916 | {
917 | this->decrement_shared_reference_count();
918 | this->assign(nullptr);
919 | }
920 |
921 | void reset(std::nullptr_t) noexcept
922 | {
923 | reset();
924 | }
925 |
926 | void reset(Pointer ptr) noexcept
927 | {
928 | if (this->ptr() != ptr)
929 | {
930 | this->decrement_shared_reference_count();
931 | this->assign(ptr);
932 | this->increment_shared_reference_count();
933 | }
934 | }
935 |
936 | void swap(intrusive_ptr& other) noexcept
937 | {
938 | if (this->get() != other.get())
939 | {
940 | base_type::swap(other);
941 | }
942 | }
943 |
944 | pointer get() const noexcept
945 | {
946 | return this->ptr();
947 | }
948 |
949 | element_type& operator * () const noexcept
950 | {
951 | return *this->get();
952 | }
953 |
954 | pointer operator -> () const noexcept
955 | {
956 | return this->get();
957 | }
958 |
959 | count_type use_count() const noexcept
960 | {
961 | return this->ref_count_func()(get()).load(std::memory_order_acquire);
962 | }
963 |
964 | explicit operator bool() const noexcept
965 | {
966 | return static_cast(this->get());
967 | }
968 |
969 | Deleter get_deleter() noexcept
970 | {
971 | return this->deleter();
972 | }
973 |
974 | const Deleter& get_deleter() const noexcept
975 | {
976 | return this->deleter();
977 | }
978 |
979 | RefCountAccessor ref_count_access() noexcept
980 | {
981 | return this->ref_count_func();
982 | }
983 |
984 | const RefCountAccessor& ref_count_access() const noexcept
985 | {
986 | return this->ref_count_func();
987 | }
988 |
989 | private:
990 |
991 | template
992 | friend class intrusive_ptr;
993 | };
994 |
995 | // -------------- Global equality operators
996 | //
997 | template
998 | bool operator == (
999 | const intrusive_ptr& lhs,
1000 | const intrusive_ptr& rhs
1001 | ) noexcept
1002 | {
1003 | return lhs.get() == rhs.get();
1004 | }
1005 |
1006 | template
1007 | bool operator != (
1008 | const intrusive_ptr& lhs,
1009 | const intrusive_ptr& rhs
1010 | ) noexcept
1011 | {
1012 | return !(lhs == rhs);
1013 | }
1014 |
1015 | template
1016 | bool operator < (
1017 | const intrusive_ptr& lhs,
1018 | const intrusive_ptr& rhs
1019 | ) noexcept
1020 | {
1021 | using pointer1 = typename intrusive_ptr::pointer;
1022 | using pointer2 = typename intrusive_ptr::pointer;
1023 | using common_type = typename std::common_type::type;
1024 |
1025 | return std::less{}(lhs.get(), rhs.get());
1026 | }
1027 |
1028 | template
1029 | bool operator > (
1030 | const intrusive_ptr& lhs,
1031 | const intrusive_ptr& rhs
1032 | ) noexcept
1033 | {
1034 | return rhs < lhs;
1035 | }
1036 |
1037 | template
1038 | bool operator <= (
1039 | const intrusive_ptr& lhs,
1040 | const intrusive_ptr& rhs
1041 | ) noexcept
1042 | {
1043 | return !(rhs < lhs);
1044 | }
1045 |
1046 | template
1047 | bool operator >= (
1048 | const intrusive_ptr& lhs,
1049 | const intrusive_ptr& rhs
1050 | ) noexcept
1051 | {
1052 | return !(lhs < rhs);
1053 | }
1054 |
1055 | template
1056 | bool operator == (const intrusive_ptr& lhs, std::nullptr_t) noexcept
1057 | {
1058 | return !lhs;
1059 | }
1060 |
1061 | template
1062 | bool operator == (std::nullptr_t, const intrusive_ptr& rhs) noexcept
1063 | {
1064 | return !rhs;
1065 | }
1066 |
1067 | template
1068 | bool operator != (const intrusive_ptr& lhs, std::nullptr_t) noexcept
1069 | {
1070 | return bool(lhs);
1071 | }
1072 |
1073 | template
1074 | bool operator != (std::nullptr_t, const intrusive_ptr& rhs) noexcept
1075 | {
1076 | return bool(rhs);
1077 | }
1078 |
1079 | template
1080 | bool operator < (const intrusive_ptr& lhs, std::nullptr_t) noexcept
1081 | {
1082 | using pointer = typename intrusive_ptr::pointer;
1083 | return std::less{}(lhs.get(), nullptr);
1084 | }
1085 |
1086 | template
1087 | bool operator < (std::nullptr_t, const intrusive_ptr& rhs) noexcept
1088 | {
1089 | using pointer = typename intrusive_ptr::pointer;
1090 | return std::less{}(nullptr, rhs.get());
1091 | }
1092 |
1093 | template
1094 | bool operator > (const intrusive_ptr& lhs, std::nullptr_t) noexcept
1095 | {
1096 | return (nullptr < lhs);
1097 | }
1098 |
1099 | template
1100 | bool operator > (std::nullptr_t lhs, const intrusive_ptr& rhs) noexcept
1101 | {
1102 | return (rhs < nullptr);
1103 | }
1104 |
1105 | template
1106 | bool operator <= (const intrusive_ptr& lhs, std::nullptr_t rhs) noexcept
1107 | {
1108 | return !(nullptr < lhs);
1109 | }
1110 |
1111 | template
1112 | bool operator <= (std::nullptr_t lhs, const intrusive_ptr& rhs) noexcept
1113 | {
1114 | return !(rhs < nullptr);
1115 | }
1116 |
1117 | template
1118 | bool operator >= (const intrusive_ptr& lhs, std::nullptr_t rhs) noexcept
1119 | {
1120 | return !(lhs < nullptr);
1121 | }
1122 |
1123 | template
1124 | bool operator >= (std::nullptr_t lhs, const intrusive_ptr& rhs) noexcept
1125 | {
1126 | return !(nullptr < rhs);
1127 | }
1128 |
1129 | template
1130 | void swap(intrusive_ptr& lhs, intrusive_ptr& rhs) noexcept
1131 | {
1132 | lhs.swap(rhs);
1133 | }
1134 |
1135 | #ifdef STDX_MUST_SPECIALIZE_IS_TRIVIALLY_RELOCATABLE
1136 | template
1137 | struct is_trivially_relocatable> : std::true_type
1138 | { };
1139 | #endif
1140 |
1141 | } // end namespace stdx
1142 |
1143 | #endif // include guard
1144 |
1145 |
1146 |
1147 | #ifndef STDX_STRING_REF_HPP
1148 | #define STDX_STRING_REF_HPP
1149 |
1150 | #include
1151 | #include
1152 | #include
1153 |
1154 | namespace stdx {
1155 |
1156 | class string_ref;
1157 |
1158 | namespace detail {
1159 |
1160 | constexpr const char* cstring_null_scan(const char* s) noexcept
1161 | {
1162 | return *s ? cstring_null_scan(s + 1) : s;
1163 | }
1164 |
1165 | } // end namespace detail
1166 |
1167 | class string_ref
1168 | {
1169 | protected:
1170 |
1171 | class state_type;
1172 |
1173 | public:
1174 |
1175 | using value_type = const char;
1176 | using size_type = std::size_t;
1177 | using pointer = const char*;
1178 | using const_pointer = const char*;
1179 | using iterator = const char*;
1180 | using const_iterator = const char*;
1181 |
1182 | struct resource_management
1183 | {
1184 | using copy_constructor = state_type(*)(const string_ref&);
1185 | using move_constructor = state_type(*)(string_ref&&);
1186 | using destructor = void(*)(string_ref&);
1187 |
1188 | constexpr resource_management() noexcept
1189 | : copy{nullptr}, move{nullptr}, destroy{nullptr}
1190 | { }
1191 |
1192 | constexpr resource_management(
1193 | copy_constructor cctor,
1194 | move_constructor mctor,
1195 | destructor dtor
1196 | ) noexcept
1197 | : copy{cctor}, move{mctor}, destroy{dtor}
1198 | { }
1199 |
1200 | copy_constructor copy;
1201 | move_constructor move;
1202 | destructor destroy;
1203 | };
1204 |
1205 | constexpr string_ref() noexcept : m_begin(nullptr), m_end(nullptr), context{}
1206 | { }
1207 |
1208 | constexpr string_ref(const char* beg) noexcept
1209 | : m_begin(beg), m_end(detail::cstring_null_scan(beg)), context{}
1210 | { }
1211 |
1212 | constexpr string_ref(const char* beg, const char* e) noexcept
1213 | : m_begin(beg), m_end(e), context{}
1214 | { }
1215 |
1216 | constexpr string_ref(const char* beg, resource_management rm) noexcept
1217 | :
1218 | m_begin(beg),
1219 | m_end(detail::cstring_null_scan(beg)),
1220 | m_resource_management(rm),
1221 | context{}
1222 | { }
1223 |
1224 | constexpr string_ref(const char* beg, const char* e, resource_management rm) noexcept
1225 | : m_begin(beg), m_end(e), m_resource_management(rm), context{}
1226 | { }
1227 |
1228 | constexpr string_ref(
1229 | const char* beg,
1230 | const char* e,
1231 | resource_management rm,
1232 | void* ctx
1233 | ) noexcept
1234 | : m_begin(beg), m_end(e), m_resource_management(rm), context{ctx}
1235 | { }
1236 |
1237 | STDX_GCC7_WORKAROUND_CONSTEXPR string_ref(const string_ref& s)
1238 | : string_ref{s.m_resource_management.copy ? s.m_resource_management.copy(s) : s.state()}
1239 | { }
1240 |
1241 | STDX_GCC7_WORKAROUND_CONSTEXPR string_ref(string_ref&& s)
1242 | :
1243 | string_ref{
1244 | s.m_resource_management.move ?
1245 | s.m_resource_management.move(std::move(s)) : s.state()
1246 | }
1247 | { }
1248 |
1249 | string_ref& operator = (const string_ref& s)
1250 | {
1251 | string_ref tmp = s;
1252 | *this = std::move(tmp);
1253 | return *this;
1254 | }
1255 |
1256 | string_ref& operator = (string_ref&& s)
1257 | {
1258 | if (this != &s)
1259 | {
1260 | if (m_resource_management.destroy) m_resource_management.destroy(*this);
1261 |
1262 | // This is legal because of the common initial sequence and the fact
1263 | // that any type erased object must be trivially relocatable.
1264 | *this = string_ref_state_union{std::move(s)}.state;
1265 | }
1266 |
1267 | return *this;
1268 | }
1269 |
1270 | ~string_ref() noexcept
1271 | {
1272 | if (m_resource_management.destroy) m_resource_management.destroy(*this);
1273 | }
1274 |
1275 | bool empty() const noexcept { return m_begin == m_end; }
1276 |
1277 | size_type size() const noexcept { return m_end - m_begin; }
1278 |
1279 | const_pointer data() const noexcept { return m_begin; }
1280 |
1281 | iterator begin() noexcept { return m_begin; }
1282 |
1283 | iterator end() noexcept { return m_end; }
1284 |
1285 | const_iterator begin() const noexcept { return m_begin; }
1286 |
1287 | const_iterator end() const noexcept { return m_end; }
1288 |
1289 | const_iterator cbegin() const noexcept { return m_begin; }
1290 |
1291 | const_iterator cend() const noexcept { return m_end; }
1292 |
1293 | protected:
1294 |
1295 | struct state_type
1296 | {
1297 | pointer m_begin;
1298 | pointer m_end;
1299 | resource_management m_resource_management;
1300 | void* context;
1301 | };
1302 |
1303 | state_type state() const noexcept
1304 | {
1305 | return state_type{m_begin, m_end, m_resource_management, context};
1306 | }
1307 |
1308 | constexpr explicit string_ref(const state_type& s) noexcept
1309 | :
1310 | m_begin(s.m_begin),
1311 | m_end(s.m_end),
1312 | m_resource_management(s.m_resource_management),
1313 | context(s.context)
1314 | { }
1315 |
1316 | void clear() noexcept
1317 | {
1318 | m_begin = nullptr;
1319 | m_end = nullptr;
1320 | }
1321 |
1322 | template
1323 | union string_ref_state_union_type
1324 | {
1325 | explicit string_ref_state_union_type(StringRef&& s) : str(std::move(s))
1326 | { }
1327 |
1328 | ~string_ref_state_union_type() noexcept {}
1329 |
1330 | StringRef str;
1331 | state_type state;
1332 | };
1333 |
1334 | using string_ref_state_union = string_ref_state_union_type;
1335 |
1336 | void operator = (const state_type& s) noexcept
1337 | {
1338 | m_begin = s.m_begin;
1339 | m_end = s.m_end;
1340 | m_resource_management = s.m_resource_management;
1341 | context = s.context;
1342 | }
1343 |
1344 | pointer m_begin;
1345 | pointer m_end;
1346 | resource_management m_resource_management;
1347 | void* context;
1348 | };
1349 |
1350 | inline bool operator == (const string_ref& lhs, const string_ref& rhs) noexcept
1351 | {
1352 | return (lhs.size() == rhs.size()) && (std::memcmp(lhs.data(), rhs.data(), lhs.size()) == 0);
1353 | }
1354 |
1355 | inline bool operator != (const string_ref& lhs, const string_ref& rhs) noexcept
1356 | {
1357 | return !(lhs == rhs);
1358 | }
1359 |
1360 | inline bool operator < (const string_ref& lhs, const string_ref& rhs) noexcept
1361 | {
1362 | const std::size_t sz = (lhs.size() < rhs.size()) ? lhs.size() : rhs.size();
1363 | int result = std::memcmp(lhs.data(), rhs.data(), sz);
1364 | if (result == 0) return lhs.size() < rhs.size();
1365 | return result < 0;
1366 | }
1367 |
1368 | inline bool operator > (const string_ref& lhs, const string_ref& rhs) noexcept
1369 | {
1370 | return rhs < lhs;
1371 | }
1372 |
1373 | inline bool operator <= (const string_ref& lhs, const string_ref& rhs) noexcept
1374 | {
1375 | return !(lhs > rhs);
1376 | }
1377 |
1378 | inline bool operator >= (const string_ref& lhs, const string_ref& rhs) noexcept
1379 | {
1380 | return !(lhs < rhs);
1381 | }
1382 |
1383 | // Reference-counted allocated string
1384 | //
1385 | class shared_string_ref : public string_ref
1386 | {
1387 | struct string_arena_base
1388 | {
1389 | mutable std::atomic ref_count;
1390 | std::size_t length;
1391 | };
1392 |
1393 | struct string_arena : string_arena_base
1394 | {
1395 | constexpr explicit string_arena(std::size_t length) noexcept
1396 | : string_arena_base{{1}, length}
1397 | { }
1398 |
1399 | constexpr static std::size_t header_size() noexcept
1400 | {
1401 | return sizeof(string_arena);
1402 | }
1403 |
1404 | char* data() noexcept
1405 | {
1406 | return reinterpret_cast(this) + header_size();
1407 | }
1408 |
1409 | const char* data() const noexcept
1410 | {
1411 | return reinterpret_cast(this) + header_size();
1412 | }
1413 |
1414 | const char* begin() const noexcept { return data(); }
1415 |
1416 | const char* end() const noexcept { return data() + length; }
1417 | };
1418 |
1419 | string_ref allocate_string_ref(const char* s, std::size_t length)
1420 | {
1421 | const std::size_t arena_size = string_arena::header_size() + length;
1422 | char* buf = static_cast(::operator new(arena_size));
1423 | string_arena* a = new (buf) string_arena{length};
1424 | std::memcpy(a->data(), s, length);
1425 | return shared_string_ref{a};
1426 | }
1427 |
1428 | explicit shared_string_ref(string_arena* a) noexcept
1429 | :
1430 | string_ref{
1431 | a->begin(),
1432 | a->end(),
1433 | string_ref::resource_management{©_construct, &move_construct, &destroy},
1434 | a
1435 | }
1436 | { }
1437 |
1438 | const string_arena_base* get_arena() const noexcept
1439 | {
1440 | return static_cast(this->context);
1441 | }
1442 |
1443 | string_arena_base* get_arena() noexcept
1444 | {
1445 | return static_cast(this->context);
1446 | }
1447 |
1448 | static string_ref::state_type copy_construct(const string_ref& base) noexcept
1449 | {
1450 | const shared_string_ref& s = static_cast(base);
1451 | const string_arena_base* a = s.get_arena();
1452 | if (a) a->ref_count.fetch_add(1, std::memory_order_relaxed);
1453 | return s.state();
1454 | }
1455 |
1456 | static string_ref::state_type move_construct(string_ref&& base) noexcept
1457 | {
1458 | shared_string_ref& s = static_cast(base);
1459 | auto st = s.state();
1460 | s.context = nullptr;
1461 | s.clear();
1462 | return st;
1463 | }
1464 |
1465 | static void destroy(string_ref& base) noexcept
1466 | {
1467 | shared_string_ref& s = static_cast(base);
1468 | string_arena* a = static_cast(s.get_arena());
1469 | if (a && (a->ref_count.fetch_sub(1, std::memory_order_release) == 1))
1470 | {
1471 | std::atomic_thread_fence(std::memory_order_acquire);
1472 | ::operator delete(a);
1473 | }
1474 | }
1475 |
1476 | template
1477 | struct allocated_string_arena : string_arena_base
1478 | {
1479 | constexpr allocated_string_arena(const Allocator& alloc, std::size_t length) noexcept
1480 | : string_arena_base{{1}, length}, allocator(alloc)
1481 | { }
1482 |
1483 | constexpr static std::size_t header_size() noexcept
1484 | {
1485 | return sizeof(allocated_string_arena);
1486 | }
1487 |
1488 | char* data() noexcept
1489 | {
1490 | return reinterpret_cast(this) + header_size();
1491 | }
1492 |
1493 | const char* data() const noexcept
1494 | {
1495 | return reinterpret_cast(this) + header_size();
1496 | }
1497 |
1498 | const char* begin() const noexcept { return data(); }
1499 |
1500 | const char* end() const noexcept { return data() + length; }
1501 |
1502 | std::size_t allocated_size() const noexcept { return header_size() + length; }
1503 |
1504 | Allocator allocator;
1505 | };
1506 |
1507 | template
1508 | explicit shared_string_ref(allocated_string_arena* a) noexcept
1509 | :
1510 | string_ref{
1511 | a->begin(),
1512 | a->end(),
1513 | string_ref::resource_management{
1514 | ©_construct,
1515 | &move_construct,
1516 | &allocator_destroy
1517 | },
1518 | a
1519 | }
1520 | { }
1521 |
1522 | template
1523 | string_ref allocate_string_ref(
1524 | const Allocator& allocator,
1525 | const char* s,
1526 | std::size_t length
1527 | )
1528 | {
1529 | using allocator_type = typename std::allocator_traits<
1530 | Allocator
1531 | >::template rebind_alloc;
1532 | using arena_type = allocated_string_arena;
1533 |
1534 | allocator_type alloc{allocator};
1535 | const std::size_t arena_size = arena_type::header_size() + length;
1536 | char* buf = alloc.allocate(arena_size);
1537 | arena_type* a = new (buf) arena_type{alloc, length};
1538 | std::memcpy(a->data(), s, length);
1539 | return shared_string_ref{a};
1540 | }
1541 |
1542 | template
1543 | static void allocator_destroy(string_ref& base) noexcept
1544 | {
1545 | using arena_type = allocated_string_arena;
1546 |
1547 | shared_string_ref& s = static_cast(base);
1548 | arena_type* a = static_cast(s.get_arena());
1549 | if (a && (a->ref_count.fetch_sub(1, std::memory_order_release) == 1))
1550 | {
1551 | std::atomic_thread_fence(std::memory_order_acquire);
1552 |
1553 | Allocator alloc = std::move(a->allocator);
1554 | const std::size_t allocated_size = a->allocated_size();
1555 | a->~arena_type();
1556 | alloc.deallocate(reinterpret_cast(a), allocated_size);
1557 | }
1558 | }
1559 |
1560 | public:
1561 |
1562 | shared_string_ref(const char* beg)
1563 | : string_ref{allocate_string_ref(beg, detail::cstring_null_scan(beg) - beg)}
1564 | { }
1565 |
1566 | shared_string_ref(const char* beg, const char* end)
1567 | : string_ref{allocate_string_ref(beg, end - beg)}
1568 | { }
1569 |
1570 | template
1571 | shared_string_ref(const Allocator& alloc, const char* beg)
1572 | : string_ref{allocate_string_ref(alloc, beg, detail::cstring_null_scan(beg) - beg)}
1573 | { }
1574 |
1575 | template
1576 | shared_string_ref(const Allocator& alloc, const char* beg, const char* end)
1577 | : string_ref{allocate_string_ref(alloc, beg, end - beg)}
1578 | { }
1579 |
1580 | std::size_t use_count() const noexcept
1581 | {
1582 | const string_arena_base* a = get_arena();
1583 | return a ? a->ref_count.load(std::memory_order_acquire) : 0;
1584 | }
1585 | };
1586 |
1587 | } // end namespace stdx
1588 |
1589 | #endif
1590 |
1591 |
1592 |
1593 | #ifndef STDX_LAUNDER_HPP
1594 | #define STDX_LAUNDER_HPP
1595 |
1596 | #include
1597 |
1598 |
1599 | namespace stdx {
1600 |
1601 | #if __cplusplus >= 201703L
1602 | #if defined(__cpp_lib_launder)
1603 | #define STDX_HAVE_NATIVE_LAUNDER 1
1604 | using std::launder;
1605 | #elif defined(STDX_CLANG_COMPILER)
1606 | #if __has_builtin(__builtin_launder)
1607 | #define STDX_HAVE_NATIVE_LAUNDER 1
1608 | template
1609 | constexpr T* launder(T* p) noexcept
1610 | {
1611 | return __builtin_launder(p);
1612 | }
1613 | #endif
1614 | #endif
1615 | #endif
1616 |
1617 | #if !defined(STDX_HAVE_NATIVE_LAUNDER)
1618 | template
1619 | constexpr T* launder(T* p) noexcept
1620 | {
1621 | return p;
1622 | }
1623 | #endif
1624 |
1625 | } // end namespace stdx
1626 |
1627 | #endif
1628 |
1629 |
1630 |
1631 | #ifndef STDX_ERROR_HPP
1632 | #define STDX_ERROR_HPP
1633 |
1634 | #include
1635 | #include
1636 | #include
1637 | #include
1638 | #include
1639 |
1640 |
1641 | namespace stdx {
1642 |
1643 | class error;
1644 |
1645 | namespace detail {
1646 |
1647 | template
1648 | struct error_ctor_args;
1649 |
1650 | } // end namespace detail
1651 |
1652 | } // end namespace stdx
1653 |
1654 | namespace stdx_adl {
1655 |
1656 | namespace detail {
1657 |
1658 | template