@auto_js/utility 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/CMakeLists.txt +45 -0
  2. package/LICENSE +13 -0
  3. package/_module.cc +14 -0
  4. package/assertions.cc +32 -0
  5. package/container/mutable_priority_queue.cc +35 -0
  6. package/container/sealed_map.cc +121 -0
  7. package/container/segmented_priority_queue.cc +105 -0
  8. package/container/tinker_queue.cc +73 -0
  9. package/functional/bind.cc +151 -0
  10. package/functional/elide.cc +52 -0
  11. package/functional/flat_tuple.cc +90 -0
  12. package/functional/function_constant.cc +58 -0
  13. package/functional/function_ref.cc +68 -0
  14. package/functional/functional.cc +107 -0
  15. package/functional/regular_return.cc +55 -0
  16. package/include/auto_js/chunk_view.h +363 -0
  17. package/include/auto_js/no_unique_address.h +8 -0
  18. package/memory/autorelease_pool.cc +281 -0
  19. package/memory/comparator.cc +53 -0
  20. package/memory/memory.cc +64 -0
  21. package/memory/noinit_allocator.cc +64 -0
  22. package/meta/algorithm.cc +74 -0
  23. package/package.json +11 -0
  24. package/platform/lockable.cc +261 -0
  25. package/platform/shim/darwin.cc +8 -0
  26. package/platform/stop_token.cc +107 -0
  27. package/platform/timer.cc +175 -0
  28. package/platform/timer.h.cc +36 -0
  29. package/type_traits/function_traits.cc +173 -0
  30. package/type_traits/transform.cc +60 -0
  31. package/type_traits/type_of.cc +72 -0
  32. package/type_traits/type_pack.cc +116 -0
  33. package/type_traits/type_traits.cc +5 -0
  34. package/utility/constant_wrapper.cc +57 -0
  35. package/utility/covariant_value.cc +33 -0
  36. package/utility/facade.cc +126 -0
  37. package/utility/hash.cc +37 -0
  38. package/utility/ranges.cc +55 -0
  39. package/utility/string.cc +328 -0
  40. package/utility/utility.cc +150 -0
  41. package/utility/variant.cc +26 -0
package/CMakeLists.txt ADDED
@@ -0,0 +1,45 @@
1
+ add_library(utility_js OBJECT)
2
+ target_compile_features(utility_js PUBLIC cxx_std_23)
3
+
4
+ # Setup `#include "auto_js/[..]"`
5
+ target_include_directories(utility_js PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
6
+
7
+ target_sources(utility_js
8
+ PRIVATE
9
+ assertions.cc
10
+ platform/timer.cc
11
+ PUBLIC FILE_SET CXX_MODULES FILES
12
+ _module.cc
13
+ container/mutable_priority_queue.cc
14
+ container/sealed_map.cc
15
+ container/segmented_priority_queue.cc
16
+ container/tinker_queue.cc
17
+ functional/bind.cc
18
+ functional/elide.cc
19
+ functional/flat_tuple.cc
20
+ functional/function_constant.cc
21
+ functional/function_ref.cc
22
+ functional/functional.cc
23
+ functional/regular_return.cc
24
+ memory/autorelease_pool.cc
25
+ memory/comparator.cc
26
+ memory/memory.cc
27
+ memory/noinit_allocator.cc
28
+ meta/algorithm.cc
29
+ platform/lockable.cc
30
+ platform/stop_token.cc
31
+ platform/timer.h.cc
32
+ type_traits/function_traits.cc
33
+ type_traits/transform.cc
34
+ type_traits/type_of.cc
35
+ type_traits/type_pack.cc
36
+ type_traits/type_traits.cc
37
+ utility/constant_wrapper.cc
38
+ utility/covariant_value.cc
39
+ utility/facade.cc
40
+ utility/hash.cc
41
+ utility/ranges.cc
42
+ utility/string.cc
43
+ utility/utility.cc
44
+ utility/variant.cc
45
+ )
package/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright 2017 Marcel Laverdet
2
+
3
+ Permission to use, copy, modify, and/or distribute this software for any
4
+ purpose with or without fee is hereby granted, provided that the above
5
+ copyright notice and this permission notice appear in all copies.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
8
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
9
+ FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
10
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
11
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
12
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
13
+ PERFORMANCE OF THIS SOFTWARE.
package/_module.cc ADDED
@@ -0,0 +1,14 @@
1
+ export module util;
2
+ export import :container.mutable_priority_queue;
3
+ export import :container.sealed_map;
4
+ export import :container.segmented_priority_queue;
5
+ export import :container.tinker_queue;
6
+ export import :functional;
7
+ export import :memory;
8
+ export import :meta.algorithm;
9
+ export import :platform.lockable;
10
+ export import :platform.stop_token;
11
+ export import :platform.timer;
12
+ export import :type_traits;
13
+ export import :utility;
14
+ export import :utility.variant;
package/assertions.cc ADDED
@@ -0,0 +1,32 @@
1
+ #include <tuple> // IWYU pragma: keep
2
+ #include <type_traits>
3
+ import util;
4
+
5
+ constexpr auto assert_strings_equal(auto... strings) {
6
+ const auto [... left ] = util::sequence<sizeof...(strings)>;
7
+ const auto [... right ] = util::sequence<sizeof...(strings)>;
8
+ static_assert(
9
+ (([ = ]() -> bool {
10
+ auto left_string = strings...[ left ];
11
+ using char_type = std::remove_extent_t<typename decltype(left_string)::value_type>;
12
+ // NOLINTNEXTLINE(modernize-type-traits)
13
+ return ((left_string == util::interpolate_string<char_type>(strings...[ right ])) && ...);
14
+ }()) &&
15
+ ...)
16
+ );
17
+ }
18
+
19
+ [[maybe_unused]] constexpr auto result = []() -> int {
20
+ // https://en.cppreference.com/w/cpp/language/string_literal.html
21
+
22
+ // Quick check of interpolation matrix
23
+ assert_strings_equal(util::cw<u8"💖">, util::cw<u"💖">, util::cw<U"💖">);
24
+ assert_strings_equal(util::cw<"wow">, util::cw<u8"wow">, util::cw<u"wow">, util::cw<U"wow">);
25
+
26
+ // Unpaired utf-16 surrogates
27
+ constexpr auto one_high_surrogate = util::cw<u"\xd83d">;
28
+ static_assert(one_high_surrogate == util::interpolate_string<char16_t>(one_high_surrogate));
29
+ constexpr auto two_high_surrogate = util::cw<u"\xd83d\xd83d">;
30
+ static_assert(two_high_surrogate == util::interpolate_string<char16_t>(two_high_surrogate));
31
+ return 0;
32
+ }();
@@ -0,0 +1,35 @@
1
+ module;
2
+ #include <concepts>
3
+ #include <queue>
4
+ #include <utility>
5
+ export module util:container.mutable_priority_queue;
6
+
7
+ namespace util {
8
+
9
+ template <class Value>
10
+ struct mutable_entry {
11
+ mutable_entry() = default;
12
+ explicit mutable_entry(auto&&... args)
13
+ requires std::constructible_from<Value, decltype(args)...>
14
+ : value_{std::forward<decltype(args)>(args)...} {}
15
+ auto operator*() const -> Value& { return value_; }
16
+ auto operator<=>(const mutable_entry&) const = default;
17
+
18
+ private:
19
+ mutable Value value_;
20
+ };
21
+
22
+ // Allows the top element of `std::priority_queue` to be mutable
23
+ export template <class Value>
24
+ class mutable_priority_queue : public std::priority_queue<mutable_entry<Value>> {
25
+ private:
26
+ using priority_queue = std::priority_queue<mutable_entry<Value>>;
27
+
28
+ public:
29
+ using value_type = Value;
30
+
31
+ auto top() -> value_type& { return *priority_queue::top(); }
32
+ [[nodiscard]] auto top() const -> const value_type& { return *priority_queue::top(); }
33
+ };
34
+
35
+ } // namespace util
@@ -0,0 +1,121 @@
1
+ module;
2
+ #include <algorithm>
3
+ #include <concepts>
4
+ #include <format>
5
+ #include <ranges>
6
+ #include <type_traits>
7
+ #include <utility>
8
+ export module util:container.sealed_map;
9
+ import :functional;
10
+
11
+ namespace util {
12
+
13
+ struct sorted_equivalent_t {};
14
+
15
+ export template <class Key, class Type, size_t Size>
16
+ class sealed_map {
17
+ public:
18
+ using key_type = Key;
19
+ using value_type = std::pair<const key_type, Type>;
20
+ using container_type = std::array<value_type, Size>;
21
+
22
+ private:
23
+ // Holder for consteval'd key into the map
24
+ struct key_for {
25
+ using value_type = size_t;
26
+ constexpr key_for() :
27
+ index_{std::numeric_limits<value_type>::max()} {}
28
+ explicit constexpr key_for(auto index) :
29
+ index_{static_cast<value_type>(index)} {}
30
+
31
+ constexpr auto operator*() const -> std::size_t { return index_; }
32
+ explicit constexpr operator bool() const { return index_ != std::numeric_limits<value_type>::max(); }
33
+
34
+ private:
35
+ size_t index_;
36
+ };
37
+
38
+ // Check for duplicates
39
+ explicit consteval sealed_map(sorted_equivalent_t /*tag*/, container_type values) :
40
+ values_{std::move(values)} {
41
+ const auto projection = [](const auto& entry) { return entry.first; };
42
+ const auto duplicate = std::ranges::adjacent_find(values, std::equal_to{}, projection);
43
+ if (duplicate != values.end()) {
44
+ constexpr auto render = util::overloaded{
45
+ [](const auto& /*value*/) -> std::string { return "[??]"; },
46
+ [](std::string value) -> std::string { return value; },
47
+ };
48
+ // This error message can't actually render in constexpr but it's the thought that counts.
49
+ throw std::logic_error{std::format("Duplicate key {}", render(projection(*duplicate)))};
50
+ }
51
+ }
52
+
53
+ public:
54
+ // Default constructed values
55
+ explicit consteval sealed_map(std::type_identity<Type> /*default_construct*/, auto... keys)
56
+ requires std::default_initializable<Type> :
57
+ sealed_map{
58
+ sorted_equivalent_t{},
59
+ util::elide{[ & ]() -> container_type {
60
+ auto keys_array = std::array<key_type, Size>{std::move(keys)...};
61
+ std::ranges::sort(keys_array);
62
+ auto [... sorted_keys ] = std::move(keys_array);
63
+ return {value_type{std::move(sorted_keys), Type{}}...};
64
+ }}
65
+ } {}
66
+
67
+ // Keys & values provided
68
+ explicit consteval sealed_map(std::in_place_t /*tag*/, auto... pairs) :
69
+ sealed_map{
70
+ sorted_equivalent_t{},
71
+ util::elide{[ & ]() -> container_type {
72
+ // Sort without invoking `operator()=`, which might not exist on the value type
73
+ auto entries = std::array<value_type, Size>{std::move(pairs)...};
74
+ auto indices = std::array<size_t, Size>{};
75
+ std::ranges::copy(std::ranges::iota_view{size_t{0}, Size}, indices.begin());
76
+ const auto projection = [ & ](size_t ii) -> const auto& { return entries.at(ii).first; };
77
+ std::ranges::sort(indices, std::less{}, projection);
78
+ const auto [... sorted_indices ] = indices;
79
+ return {value_type(std::move(entries.at(sorted_indices)))...};
80
+ }}
81
+ } {}
82
+
83
+ [[nodiscard]] constexpr auto begin(this auto& self) { return self.values_.begin(); }
84
+ [[nodiscard]] constexpr auto end(this auto& self) { return self.values_.end(); }
85
+
86
+ // Returns a reference to the value at the key previously found by `lookup`.
87
+ [[nodiscard]] constexpr auto at(this auto& self, key_for index) -> auto& {
88
+ if (!index) {
89
+ throw std::out_of_range{"Key not found"};
90
+ }
91
+ return *std::next(self.values_.begin(), *index);
92
+ }
93
+
94
+ // Find the given key in the map and return a nullable pointer to the value.
95
+ [[nodiscard]] constexpr auto find(const auto& key) const -> auto* {
96
+ const auto index = lookup(key);
97
+ return index ? std::addressof(at(index)) : nullptr;
98
+ }
99
+
100
+ // Return an opaque index type which can be used with `at`. Generally should be used in a
101
+ // consteval context.
102
+ [[nodiscard]] constexpr auto lookup(const auto& key) const -> key_for {
103
+ auto less = overloaded{
104
+ [](const value_type& left, const auto& right) constexpr { return left.first < right; },
105
+ [](const auto& left, const value_type& right) constexpr { return left < right.first; },
106
+ };
107
+ auto range = std::equal_range(values_.begin(), values_.end(), key, less);
108
+ return range.first == range.second ? key_for{} : key_for{std::distance(values_.begin(), range.first)};
109
+ }
110
+
111
+ private:
112
+ container_type values_;
113
+ };
114
+
115
+ template <class Key, class Type, class... Rest>
116
+ sealed_map(std::type_identity<Type>, Key, Rest...) -> sealed_map<Key, Type, sizeof...(Rest) + 1>;
117
+
118
+ template <class Key, class Type, class... Rest>
119
+ sealed_map(std::in_place_t, std::pair<Key, Type>, Rest...) -> sealed_map<Key, Type, sizeof...(Rest) + 1>;
120
+
121
+ } // namespace util
@@ -0,0 +1,105 @@
1
+ module;
2
+ #include <algorithm>
3
+ #include <array>
4
+ #include <functional>
5
+ #include <iterator>
6
+ #include <span>
7
+ #include <utility>
8
+ export module util:container.segmented_priority_queue;
9
+
10
+ namespace util {
11
+
12
+ // Span which expands or contracts to include or possibly exclude the given members.
13
+ export template <class Type>
14
+ class ratchet_span : public std::span<Type> {
15
+ public:
16
+ using span_type = std::span<Type>;
17
+ using span_type::begin;
18
+ using span_type::end;
19
+ using span_type::span_type;
20
+ using iterator = span_type::iterator;
21
+
22
+ auto erase(auto it) -> void;
23
+ auto insert(auto it) -> void;
24
+ static auto make_iterator(auto it) -> iterator;
25
+ };
26
+
27
+ // N number of queues, which tracks which ones currently may have elements
28
+ export template <class Queue, size_t Size>
29
+ class segmented_priority_queue {
30
+ public:
31
+ using container_type = Queue;
32
+ using queues_type = std::array<container_type, Size>;
33
+ using value_type = container_type::value_type;
34
+
35
+ [[nodiscard]] auto containers() const -> std::span<const container_type> { return not_empty_; }
36
+ [[nodiscard]] auto empty() const -> bool { return std::ranges::all_of(containers(), &Queue::empty); }
37
+ [[nodiscard]] auto size() const -> size_t { return std::ranges::fold_left(containers(), 0UZ, std::plus{}, &Queue::size); }
38
+ auto at(unsigned priority) -> container_type&;
39
+ auto at(unsigned priority) const -> const container_type& { return queues_.at(priority); }
40
+ auto containers() -> std::span<container_type> { return not_empty_; }
41
+ auto emplace(unsigned priority, auto&&... args) -> void { at(priority).emplace(std::forward<decltype(args)>(args)...); }
42
+ auto front(this auto& self) -> auto& { return std::ranges::find_if_not(self.containers(), &Queue::empty)->front(); }
43
+ auto pop() -> void;
44
+
45
+ private:
46
+ queues_type queues_;
47
+ ratchet_span<container_type> not_empty_{queues_.end(), queues_.end()};
48
+ };
49
+
50
+ // ---
51
+
52
+ // `ratchet_span`
53
+ template <class Type>
54
+ auto ratchet_span<Type>::erase(auto it) -> void {
55
+ auto local_it = make_iterator(it);
56
+ auto next = std::next(local_it);
57
+ if (end() == next) {
58
+ *this = {begin(), local_it};
59
+ } else if (begin() == local_it) {
60
+ *this = {next, end()};
61
+ }
62
+ }
63
+
64
+ template <class Type>
65
+ auto ratchet_span<Type>::insert(auto it) -> void {
66
+ auto local_it = make_iterator(it);
67
+ *this = {
68
+ std::min(local_it, begin()),
69
+ std::max(std::next(local_it), end())
70
+ };
71
+ }
72
+
73
+ template <class Type>
74
+ auto ratchet_span<Type>::make_iterator(auto it) -> iterator {
75
+ // `std::span<Type>::iterator` is implementation-defined, and in libc++ the constructor is
76
+ // private.
77
+ auto span = std::span<Type>{it, it};
78
+ return span.begin();
79
+ }
80
+
81
+ // `segmented_priority_queue`
82
+ template <class Queue, size_t Size>
83
+ auto segmented_priority_queue<Queue, Size>::at(unsigned priority) -> container_type& {
84
+ // We don't know what you're about to do with it, so assume that it will gain an element
85
+ auto& queue = queues_.at(priority);
86
+ not_empty_.insert(&queue);
87
+ return queue;
88
+ }
89
+
90
+ template <class Queue, size_t Size>
91
+ auto segmented_priority_queue<Queue, Size>::pop() -> void {
92
+ for (auto& queue : not_empty_) {
93
+ if (queue.empty()) {
94
+ not_empty_.erase(&queue);
95
+ } else {
96
+ queue.pop();
97
+ if (queue.empty()) {
98
+ not_empty_.erase(&queue);
99
+ }
100
+ return;
101
+ }
102
+ }
103
+ }
104
+
105
+ } // namespace util
@@ -0,0 +1,73 @@
1
+ module;
2
+ #include <algorithm>
3
+ #include <cassert>
4
+ #include <concepts>
5
+ #include <deque>
6
+ #include <utility>
7
+ export module util:container.tinker_queue;
8
+
9
+ namespace util {
10
+
11
+ // A queue that lets you pick elements out the middle for some reason
12
+ export template <class Value>
13
+ class tinker_queue {
14
+ public:
15
+ static_assert(std::constructible_from<Value>);
16
+ static_assert(std::constructible_from<bool, Value>);
17
+
18
+ using container_type = std::deque<Value>;
19
+ using value_type = Value;
20
+
21
+ [[nodiscard]] auto empty() const -> bool { return size() == 0; }
22
+ [[nodiscard]] auto size() const -> size_t { return queue_.size() - empty_middle_values_; }
23
+ // nb: You don't want a `clear` method here because you want tasks to be destroyed in the order
24
+ // they were added
25
+ auto emplace(auto&&... args) -> void;
26
+ auto front(this auto& self) -> auto& { return self.queue_.front(); }
27
+ auto pop() -> void;
28
+ auto take(std::invocable<const value_type&> auto predicate) -> value_type;
29
+
30
+ private:
31
+ container_type queue_;
32
+ size_t empty_middle_values_{};
33
+ };
34
+
35
+ // ---
36
+
37
+ template <class Value>
38
+ auto tinker_queue<Value>::emplace(auto&&... args) -> void {
39
+ queue_.emplace_back(std::forward<decltype(args)>(args)...);
40
+ assert(queue_.back());
41
+ }
42
+
43
+ template <class Value>
44
+ auto tinker_queue<Value>::pop() -> void {
45
+ queue_.pop_front();
46
+ for (auto& value : queue_) {
47
+ if (value) {
48
+ return;
49
+ } else {
50
+ queue_.pop_front();
51
+ --empty_middle_values_;
52
+ }
53
+ }
54
+ }
55
+
56
+ template <class Value>
57
+ auto tinker_queue<Value>::take(std::invocable<const value_type&> auto predicate) -> value_type {
58
+ auto it = std::ranges::find_if(const_cast<const container_type&>(queue_), predicate);
59
+ if (it == queue_.begin()) {
60
+ auto result = std::move(*it);
61
+ pop();
62
+ return result;
63
+ } else if (it == queue_.end()) {
64
+ return {};
65
+ } else {
66
+ auto result = std::exchange(std::move(*it), {});
67
+ assert(result);
68
+ ++empty_middle_values_;
69
+ return result;
70
+ }
71
+ }
72
+
73
+ } // namespace util
@@ -0,0 +1,151 @@
1
+ module;
2
+ #include "auto_js/no_unique_address.h"
3
+ #include <type_traits>
4
+ #include <utility>
5
+ export module util:functional.bind;
6
+ import :functional.flat_tuple;
7
+ import :type_traits;
8
+ import :utility;
9
+
10
+ namespace util {
11
+
12
+ // Captures the given parameters in way such that empty objects yield an empty and
13
+ // default-constructible invocable. See:
14
+ // static_assert(std::is_empty_v<decltype([]() {})>);
15
+ // static_assert(!std::is_empty_v<decltype([ a = std::monostate{} ]() {})>);
16
+ export template <class Invocable, class... Bound>
17
+ class bind;
18
+
19
+ template <class Invocable, class... Bound>
20
+ bind(Invocable, Bound...) -> bind<Invocable, Bound...>;
21
+
22
+ // Storage for `bind` specializations
23
+ template <class Invocable, class... Bound>
24
+ struct bind_storage {
25
+ public:
26
+ explicit constexpr bind_storage()
27
+ requires std::is_empty_v<Invocable> && (... && std::is_empty_v<Bound>)
28
+ = default;
29
+
30
+ explicit constexpr bind_storage(Invocable invocable, Bound... params) :
31
+ invocable_{std::move(invocable)},
32
+ params_{std::forward<Bound>(params)...} {}
33
+
34
+ NO_UNIQUE_ADDRESS Invocable invocable_;
35
+ NO_UNIQUE_ADDRESS flat_tuple<Bound...> params_;
36
+ };
37
+
38
+ // Overloaded signature `util::bind`
39
+ template <class Invocable, class... Bound>
40
+ class bind_overloaded : private bind_storage<Invocable, Bound...> {
41
+ private:
42
+ using storage_type = bind_storage<Invocable, Bound...>;
43
+ using storage_type::invocable_;
44
+ using storage_type::params_;
45
+
46
+ public:
47
+ using storage_type::storage_type;
48
+
49
+ template <class... Args>
50
+ constexpr auto operator()(Args&&... args) noexcept(std::is_nothrow_invocable_v<Invocable&, Bound&..., Args...>)
51
+ -> std::invoke_result_t<Invocable&, Bound&..., Args...> {
52
+ return invoke(std::forward<Args>(args)...);
53
+ }
54
+
55
+ template <class Self, class... Args>
56
+ constexpr auto operator()(this Self&& self, Args&&... args) noexcept(std::is_nothrow_invocable_v<apply_cvref_t<Self, Invocable>, apply_cvref_t<Self, Bound>..., Args...>)
57
+ -> std::invoke_result_t<apply_cvref_t<Self, Invocable>, apply_cvref_t<Self, Bound>..., Args...> {
58
+ return std::forward<Self>(self).invoke(std::forward<Args>(args)...);
59
+ }
60
+
61
+ private:
62
+ constexpr auto invoke(this auto&& self, auto&&... args) -> decltype(auto) {
63
+ const auto [... indices ] = util::sequence<sizeof...(Bound)>;
64
+ // NOLINTNEXTLINE(bugprone-use-after-move)
65
+ return std::forward<decltype(self)>(self).invocable_(
66
+ // NOLINTNEXTLINE(bugprone-use-after-move)
67
+ get<indices>(std::forward<decltype(self)>(self).params_)...,
68
+ std::forward<decltype(args)>(args)...
69
+ );
70
+ }
71
+ };
72
+
73
+ // Explicit signature `util::bind`
74
+ template <class Invocable, class Signature, class... Bound>
75
+ class bind_explicit;
76
+
77
+ template <class Invocable, class... Args, bool Nx, class Result, class... Bound>
78
+ class bind_explicit<Invocable, auto(Args...) noexcept(Nx)->Result, Bound...> : private bind_storage<Invocable, Bound...> {
79
+ private:
80
+ using storage_type = bind_storage<Invocable, Bound...>;
81
+ using storage_type::invocable_;
82
+ using storage_type::params_;
83
+
84
+ using try_cvref_type = apply_cvref_t<ensure_reference_t<invocable_type_t<Invocable>>, Invocable>;
85
+ using cvref_type =
86
+ std::conditional_t<std::is_invocable_v<try_cvref_type, apply_cvref_t<try_cvref_type, Bound>...>, try_cvref_type, bind_explicit&>;
87
+ using this_type = apply_cvref_t<cvref_type, bind_explicit>;
88
+ constexpr static auto invoke_as_mutable = std::is_same_v<this_type, bind_explicit&>;
89
+ constexpr static auto invoke_as_forward = !invoke_as_mutable;
90
+
91
+ public:
92
+ using storage_type::storage_type;
93
+
94
+ // This overload allows `std::invocable<bind<...>>` without a reference, which makes concept
95
+ // parameters work correctly.
96
+ constexpr auto operator()(Args /*&&*/... args) noexcept(Nx) -> Result
97
+ requires invoke_as_mutable {
98
+ return invoke(std::forward<Args>(args)...);
99
+ }
100
+
101
+ // Otherwise, explicit `this` is used
102
+ constexpr auto operator()(this this_type self, Args /*&&*/... args) noexcept(Nx) -> Result
103
+ requires invoke_as_forward {
104
+ return std::forward<this_type>(self).invoke(std::forward<Args>(args)...);
105
+ }
106
+
107
+ private:
108
+ constexpr auto invoke(this auto&& self, Args /*&&*/... args) noexcept(Nx) -> Result {
109
+ const auto [... indices ] = util::sequence<sizeof...(Bound)>;
110
+ // NOLINTNEXTLINE(bugprone-use-after-move)
111
+ return std::forward<this_type>(self).invocable_(
112
+ // NOLINTNEXTLINE(bugprone-use-after-move)
113
+ get<indices>(std::forward<this_type>(self).params_)...,
114
+ std::forward<decltype(args)>(args)...
115
+ );
116
+ }
117
+ };
118
+
119
+ // Strip the first N parameters from the signature
120
+ template <class Signature, std::size_t Count>
121
+ struct bind_signature;
122
+
123
+ template <class Signature, std::size_t Count>
124
+ using bind_signature_t = bind_signature<Signature, Count>::type;
125
+
126
+ template <class First, class... Args, bool Nx, class Result, std::size_t Count>
127
+ requires(Count > 0)
128
+ struct bind_signature<auto(First, Args...) noexcept(Nx)->Result, Count>
129
+ : bind_signature<auto(Args...) noexcept(Nx)->Result, Count - 1> {};
130
+
131
+ template <class... Args, bool Nx, class Result>
132
+ struct bind_signature<auto(Args...) noexcept(Nx)->Result, 0>
133
+ : std::type_identity<auto(Args...) noexcept(Nx)->Result> {};
134
+
135
+ template <class Invocable, class... Bound>
136
+ using bind_explicit_params_t =
137
+ bind_explicit<Invocable, bind_signature_t<function_signature_t<Invocable>, sizeof...(Bound)>, Bound...>;
138
+
139
+ // Instantiate without known function signature
140
+ template <class Invocable, class... Bound>
141
+ class bind : public bind_overloaded<Invocable, Bound...> {
142
+ using bind_overloaded<Invocable, Bound...>::bind_overloaded;
143
+ };
144
+
145
+ // Instantiate with known function signature
146
+ template <class Invocable, class... Bound>
147
+ requires requires { typename function_signature_t<Invocable>; }
148
+ class bind<Invocable, Bound...> : public bind_explicit_params_t<Invocable, Bound...> {
149
+ using bind_explicit_params_t<Invocable, Bound...>::bind_explicit_params_t;
150
+ };
151
+ } // namespace util
@@ -0,0 +1,52 @@
1
+ module;
2
+ #include "auto_js/no_unique_address.h"
3
+ #include <concepts>
4
+ #include <tuple>
5
+ #include <utility>
6
+ export module util:functional.elide;
7
+
8
+ namespace util {
9
+
10
+ // Lazy value creation. Enables construction of non-movable types via copy elision. Also, can be
11
+ // used for deferred construction for `try_emplace`.
12
+
13
+ // Adapted from discontinued proposal:
14
+ // https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p3288r0.html
15
+ export template <class Type, class Invocable, class... Args>
16
+ class elide {
17
+ public:
18
+ using result_type = Type;
19
+
20
+ explicit constexpr elide(Invocable invocable, Args /*&&*/... args) :
21
+ invocable_{std::move(invocable)},
22
+ args_{std::forward<decltype(args)>(args)...} {}
23
+
24
+ // NOLINTNEXTLINE(google-explicit-constructor)
25
+ constexpr operator result_type() && {
26
+ auto [... args ] = std::move(args_);
27
+ // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage)
28
+ return std::move(invocable_)(std::move(args)...);
29
+ }
30
+
31
+ private:
32
+ NO_UNIQUE_ADDRESS Invocable invocable_;
33
+ NO_UNIQUE_ADDRESS std::tuple<Args...> args_;
34
+ };
35
+
36
+ template <class Invocable, class... Args>
37
+ elide(Invocable, Args&&...) -> elide<std::invoke_result_t<Invocable, Args...>, Invocable, Args...>;
38
+
39
+ // `util::constructor<T>` adapts a constructible type into a callable object. Great for use with
40
+ // `util::elide`.
41
+ template <class Type>
42
+ struct constructor_t {
43
+ constexpr auto operator()(auto&&... args) const -> Type
44
+ requires std::constructible_from<Type, decltype(args)...> {
45
+ return Type(std::forward<decltype(args)>(args)...);
46
+ }
47
+ };
48
+
49
+ export template <class Type>
50
+ constexpr inline auto constructor = constructor_t<Type>{};
51
+
52
+ } // namespace util