@auto_js/utility 0.0.3 → 0.0.5

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.
package/CMakeLists.txt CHANGED
@@ -19,6 +19,7 @@ target_sources(utility_js
19
19
  functional/function_constant.cc
20
20
  functional/function_ref.cc
21
21
  functional/functional.cc
22
+ functional/move_only_function.cc
22
23
  functional/regular_return.cc
23
24
  memory/autorelease_pool.cc
24
25
  memory/comparator.cc
@@ -38,6 +39,7 @@ target_sources(utility_js
38
39
  utility/facade.cc
39
40
  utility/hash.cc
40
41
  utility/ranges.cc
42
+ utility/sequence.cc
41
43
  utility/string.cc
42
44
  utility/utility.cc
43
45
  utility/variant.cc
@@ -3,6 +3,7 @@ export import :functional.bind;
3
3
  export import :functional.elide;
4
4
  export import :functional.function_constant;
5
5
  export import :functional.function_ref;
6
+ export import :functional.move_only_function;
6
7
  export import :functional.regular_return;
7
8
  import std;
8
9
 
@@ -14,32 +15,6 @@ struct overloaded : Visitors... {
14
15
  using Visitors::operator()...;
15
16
  };
16
17
 
17
- // Holder for functions in libc++ which doesn't support std::move_only_function
18
- #if defined(__cpp_lib_move_only_function) && !defined(DEBUG)
19
-
20
- export template <class Signature>
21
- using maybe_move_only_function = std::move_only_function<Signature>;
22
-
23
- export template <class Fn>
24
- auto make_indirect_moveable_function(Fn fn) {
25
- return fn;
26
- }
27
-
28
- #else
29
-
30
- export template <class Signature>
31
- using maybe_move_only_function = std::function<remove_function_cvref_t<Signature>>;
32
-
33
- export template <class Fn>
34
- auto make_indirect_moveable_function(Fn&& fn) {
35
- auto fn_ptr = std::make_shared<std::remove_cvref_t<Fn>>(std::forward<Fn>(fn));
36
- return [ = ](auto&&... args) -> decltype(auto) {
37
- return (*fn_ptr)(std::forward<decltype(args)>(args)...);
38
- };
39
- }
40
-
41
- #endif
42
-
43
18
  // Hide `-Wunused-value` warnings
44
19
  export constexpr auto unused = [](auto&& /*nothing*/) -> void {};
45
20
 
@@ -63,6 +38,46 @@ constexpr auto invoke_this_as(auto&& func, auto&&... args)
63
38
  return std::forward<decltype(func)>(func).InvokeAsType_::operator()(std::forward<decltype(args)>(args)...);
64
39
  }
65
40
 
41
+ // Invoke the given function with each value in a pack. The first parameter is a value and the
42
+ // second parameter is a continuation thunk. If it reaches the end it will invoke the function with
43
+ // no parameters, returning that result.
44
+ export constexpr auto template_traverse(auto values_pack, const auto& invoke) -> decltype(auto) {
45
+ const auto fold = util::overloaded{
46
+ [ & ]() -> decltype(auto) { return invoke(); },
47
+ [ & ](this const auto& self, auto value, auto... values) -> decltype(auto) {
48
+ return invoke(value, [ & ]() -> decltype(auto) { return self(values...); });
49
+ },
50
+ };
51
+ const auto [... values ] = values_pack;
52
+ return fold(values...);
53
+ }
54
+
55
+ // Fold operation for template packs. If the pack is empty then `identity` is returned. Otherwise
56
+ // `identity` is ignored.
57
+ export constexpr auto template_fold(auto values_pack, auto identity, const auto& invoke) -> decltype(auto) {
58
+ const auto fold = util::overloaded{
59
+ [ & ]() -> decltype(auto) { return identity; },
60
+ [ & ](auto left) -> decltype(auto) { return left; },
61
+ [ & ](this const auto& self, auto left, auto right, auto... values) -> decltype(auto) {
62
+ return self(invoke(left, right), values...);
63
+ },
64
+ };
65
+ const auto [... values ] = values_pack;
66
+ return fold(values...);
67
+ };
68
+
69
+ // Template pack reducer
70
+ export constexpr auto template_reduce(auto values_pack, auto accumulator, const auto& invoke) -> decltype(auto) {
71
+ const auto reduce = util::overloaded{
72
+ [ & ](auto accumulator) -> decltype(auto) { return accumulator; },
73
+ [ & ](this const auto& self, auto accumulator, auto value, auto... values) -> decltype(auto) {
74
+ return self(invoke(accumulator, value), values...);
75
+ },
76
+ };
77
+ const auto [... values ] = values_pack;
78
+ return reduce(accumulator, values...);
79
+ };
80
+
66
81
  // Invoke the given function with the constant expression matching a runtime value.
67
82
  export constexpr auto template_switch(const auto& value, auto case_pack, auto invoke) -> decltype(auto) {
68
83
  auto dispatch = [ & ](this const auto& self, auto case_, auto... cases) -> decltype(auto) {
@@ -82,6 +97,23 @@ export constexpr auto template_switch(const auto& value, auto case_pack, auto in
82
97
  return dispatch(cases...);
83
98
  }
84
99
 
100
+ // It seems like this should work but clang 22.1.6 throws this error:
101
+ // /workspace/packages/auto_js/js/assertions.cc:37:15: error: static assertion expression is not an integral constant expression
102
+ // 37 | static_assert(transfer<double>(std::variant<int, double>{1.1}) == 1.1);
103
+ // | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
104
+ // /workspace/packages/utility/functional/functional.cc:73:11: note: subobject of type 'const util::overloaded<(lambda at /workspace/packages/utility/functional/functional.cc:85:4), (lambda at /workspace/packages/utility/functional/functional.cc:86:4)> &const' is not initialized
105
+ // 73 | return invoke(value, [ & ]() -> decltype(auto) { return self(values...); });
106
+ //
107
+ // export constexpr auto template_switch(const auto& value, auto case_pack, auto invoke) -> decltype(auto) {
108
+ // return template_traverse(
109
+ // case_pack,
110
+ // util::overloaded{
111
+ // [ & ](auto case_, auto next) -> decltype(auto) { return value == case_ ? invoke(case_) : next(); },
112
+ // [ & ]() -> decltype(auto) { return invoke(); },
113
+ // }
114
+ // );
115
+ // }
116
+
85
117
  // libc++'s `std::reference_wrapper` causes a template instantiation while looking for
86
118
  // `result_type`. That breaks our transfer machinery.
87
119
  export template <class Type>
@@ -0,0 +1,95 @@
1
+ export module util:functional.move_only_function;
2
+ import :type_traits;
3
+ import std;
4
+
5
+ namespace util {
6
+
7
+ #if defined(__cpp_lib_move_only_function)
8
+
9
+ export template <class Signature>
10
+ using move_only_function = std::move_only_function<Signature>;
11
+
12
+ #else
13
+
14
+ // Virtual base class. There must be two, one for each const/non-const signature because virtual
15
+ // function cannot have explicit this or requirement clauses.
16
+ template <class CvRef, class Signature>
17
+ struct move_only_function_virtual;
18
+
19
+ template <class... Args, bool Nx, class Result>
20
+ struct move_only_function_virtual<int, auto(Args...) noexcept(Nx)->Result> {
21
+ virtual constexpr ~move_only_function_virtual() = default;
22
+ virtual constexpr auto operator()(Args... args) noexcept(Nx) -> Result = 0;
23
+ };
24
+
25
+ template <class... Args, bool Nx, class Result>
26
+ struct move_only_function_virtual<const int, auto(Args...) noexcept(Nx)->Result> {
27
+ virtual constexpr ~move_only_function_virtual() = default;
28
+ virtual constexpr auto operator()(Args... args) const noexcept(Nx) -> Result = 0;
29
+ };
30
+
31
+ // Instance per underlying function type
32
+ template <class CvRef, class Signature, class Function>
33
+ class move_only_function_instance;
34
+
35
+ template <class... Args, bool Nx, class Result, class Function>
36
+ class move_only_function_instance<int, auto(Args...) noexcept(Nx)->Result, Function>
37
+ : public move_only_function_virtual<int, auto(Args...) noexcept(Nx)->Result> {
38
+ public:
39
+ explicit constexpr move_only_function_instance(Function function) : function_{std::move(function)} {}
40
+ virtual constexpr auto operator()(Args... args) noexcept(Nx) -> Result { return function_(std::forward<Args>(args)...); }
41
+
42
+ private:
43
+ Function function_;
44
+ };
45
+
46
+ template <class... Args, bool Nx, class Result, class Function>
47
+ class move_only_function_instance<const int, auto(Args...) noexcept(Nx)->Result, Function>
48
+ : public move_only_function_virtual<const int, auto(Args...) noexcept(Nx)->Result> {
49
+ public:
50
+ explicit constexpr move_only_function_instance(Function function) : function_{std::move(function)} {}
51
+ virtual constexpr auto operator()(Args... args) const noexcept(Nx) -> Result { return function_(std::forward<Args>(args)...); }
52
+
53
+ private:
54
+ Function function_;
55
+ };
56
+
57
+ // std::move_only_function polyfill implementation (separated to split out cvref from signature)
58
+ template <class CvRef, class Signature>
59
+ class move_only_function_handle;
60
+
61
+ template <class Signature>
62
+ using move_only_function_handle_for =
63
+ move_only_function_handle<util::extract_function_cvref_t<Signature>, remove_function_cvref_t<Signature>>;
64
+
65
+ template <class CvRef, class... Args, bool Nx, class Result>
66
+ class move_only_function_handle<CvRef, auto(Args...) noexcept(Nx)->Result> {
67
+ public:
68
+ move_only_function_handle() = default;
69
+
70
+ template <class Function>
71
+ // NOLINTNEXTLINE(google-explicit-constructor)
72
+ constexpr move_only_function_handle(Function function) :
73
+ instance_{std::make_unique<move_only_function_instance<CvRef, auto(Args...) noexcept(Nx)->Result, Function>>(std::move(function))} {}
74
+ constexpr auto operator()(this util::apply_cvref_t<CvRef, move_only_function_handle>& self, Args... args) noexcept(Nx) -> Result {
75
+ return (*self.instance_)(std::forward<Args>(args)...);
76
+ }
77
+ constexpr explicit operator bool() const { return bool{instance_}; }
78
+
79
+ private:
80
+ std::unique_ptr<move_only_function_virtual<CvRef, auto(Args...) noexcept(Nx)->Result>> instance_;
81
+ };
82
+
83
+ // std::move_only_function polyfill
84
+ export template <class Signature>
85
+ struct move_only_function : move_only_function_handle_for<Signature> {
86
+ move_only_function() = default;
87
+ using move_only_function_handle_for<Signature>::move_only_function_handle_for;
88
+ };
89
+
90
+ template <class Function>
91
+ move_only_function(Function) -> move_only_function<util::function_signature_t<Function>>;
92
+
93
+ #endif
94
+
95
+ } // namespace util
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@auto_js/utility",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "author": "https://github.com/laverdet/",
5
5
  "homepage": "https://github.com/laverdet/isolated-vm/tree/experimental/packages/utility",
6
6
  "license": "ISC",
@@ -3,3 +3,48 @@ export import :type_traits.function_traits;
3
3
  export import :type_traits.transform;
4
4
  export import :type_traits.type_of;
5
5
  export import :type_traits.type_pack;
6
+ import std;
7
+
8
+ namespace util {
9
+
10
+ // Check if casting from one numeric to another can be done without loss. It is probably missing a
11
+ // plethora of exotic cases but I think this covers the fundamentals.
12
+ export template <class From, class To>
13
+ struct safe_numeric_conversion {
14
+ using limits_from = std::numeric_limits<From>;
15
+ using limits_to = std::numeric_limits<To>;
16
+ constexpr static auto value =
17
+ // Check signed / floating point compatibility
18
+ (limits_to::is_signed || !limits_from::is_signed) &&
19
+ (limits_from::is_integer || !limits_to::is_integer) &&
20
+ // Check precision
21
+ (limits_to::digits >= limits_from::digits) &&
22
+ // Check range
23
+ (limits_from::lowest() >= limits_to::lowest()) &&
24
+ (limits_from::max() <= limits_to::max());
25
+ };
26
+
27
+ export template <class From, class To>
28
+ constexpr auto safe_numeric_conversion_v = safe_numeric_conversion<From, To>::value;
29
+
30
+ static_assert(safe_numeric_conversion<std::int32_t, std::int64_t>::value);
31
+ static_assert(!safe_numeric_conversion<std::int32_t, std::uint64_t>::value);
32
+ static_assert(!safe_numeric_conversion<std::int64_t, double>::value);
33
+ static_assert(safe_numeric_conversion<std::int32_t, double>::value);
34
+ static_assert(safe_numeric_conversion<float, double>::value);
35
+ static_assert(!safe_numeric_conversion<double, float>::value);
36
+
37
+ // Resolves to the type of every type in the pack. If any type differs then it is ill-formed.
38
+ template <class... Type>
39
+ struct identical_type;
40
+
41
+ template <class... Type>
42
+ using identical_type_t = identical_type<Type...>::type;
43
+
44
+ template <class Type>
45
+ struct identical_type<Type> : std::type_identity<Type> {};
46
+
47
+ template <class Type, class... Rest>
48
+ struct identical_type<Type, Type, Rest...> : identical_type<Type, Rest...> {};
49
+
50
+ } // namespace util
@@ -7,7 +7,7 @@ import std;
7
7
  // NOLINTBEGIN(cppcoreguidelines-pro-bounds-array-to-pointer-decay, modernize-avoid-c-arrays)
8
8
  namespace util {
9
9
 
10
- template <class Type>
10
+ export template <class Type>
11
11
  struct fixed_value {
12
12
  using type = Type;
13
13
  // NOLINTNEXTLINE(google-explicit-constructor)
package/utility/ranges.cc CHANGED
@@ -55,11 +55,11 @@ export constexpr auto into_range(meta_range auto&& range) {
55
55
  // Returns either a `std::tuple<size_type>` with the range's size, or `std::tuple<>` if it can't be
56
56
  // determined in constant time.
57
57
  export template <class Type>
58
- constexpr auto maybe_range_size(const Type& range) {
58
+ constexpr auto maybe_range_size(const Type& range, auto... if_not) {
59
59
  if constexpr (std::ranges::sized_range<Type>) {
60
60
  return std::tuple{std::ranges::size(range)};
61
61
  } else {
62
- return std::tuple{};
62
+ return std::tuple{if_not...};
63
63
  }
64
64
  }
65
65
 
@@ -0,0 +1,22 @@
1
+ export module util:utility.sequence;
2
+ import std;
3
+
4
+ namespace util {
5
+
6
+ // Return a sequence of index constants
7
+ export template <std::size_t Size>
8
+ constexpr auto sequence = []() consteval -> auto {
9
+ // With C++26 P2686 we can do constexpr decomposition. So instead of the `tuple` of
10
+ // `integral_constants` it can be an array of `std::size_t`.
11
+ // https://clang.llvm.org/cxx_status.html
12
+
13
+ // std::array<std::size_t, Size> result{};
14
+ // std::ranges::copy(std::ranges::views::iota(std::size_t{0}, Size), result.begin());
15
+ // return result;
16
+
17
+ return []<std::size_t... Index>(std::index_sequence<Index...> /*sequence*/) consteval {
18
+ return std::tuple{std::integral_constant<std::size_t, Index>{}...};
19
+ }(std::make_index_sequence<Size>());
20
+ }();
21
+
22
+ } // namespace util
package/utility/string.cc CHANGED
@@ -1,5 +1,7 @@
1
1
  export module util:utility.string;
2
+ import :type_traits;
2
3
  import :utility.constant_wrapper;
4
+ import :utility.sequence;
3
5
  import std;
4
6
 
5
7
  namespace util {
@@ -16,6 +18,24 @@ consteval auto make_consteval_string_view(constant_wrapper<Value> /*cw*/) -> std
16
18
  return make_consteval_string_view(constant_wrapper<Value>::value);
17
19
  }
18
20
 
21
+ // Concatenate a pack of util::cw<"xyz">
22
+ // NOLINTBEGIN(cppcoreguidelines-pro-bounds-array-to-pointer-decay, cppcoreguidelines-pro-bounds-pointer-arithmetic, modernize-avoid-c-arrays)
23
+ export template <auto... Strings>
24
+ consteval auto consteval_strcat(constant_wrapper<Strings>... /*strings*/) {
25
+ using char_type = identical_type_t<std::remove_extent_t<typename constant_wrapper<Strings>::value_type>...>;
26
+ constexpr auto chars = [ & ]() -> auto {
27
+ constexpr auto size = (... + (std::extent_v<typename constant_wrapper<Strings>::value_type> - 1));
28
+ std::array<char_type, size + 1> chars{};
29
+ std::size_t offset = 0;
30
+ (..., (std::ranges::copy(Strings.value, chars.data() + offset), offset += std::extent_v<typename constant_wrapper<Strings>::value_type> - 1));
31
+ return chars;
32
+ }();
33
+ auto [... indices ] = sequence<chars.size()>;
34
+ constexpr char_type string[ chars.size() ] = {chars[ indices ]...};
35
+ return cw<string>;
36
+ }
37
+ // NOLINTEND(cppcoreguidelines-pro-bounds-array-to-pointer-decay, cppcoreguidelines-pro-bounds-pointer-arithmetic, modernize-avoid-c-arrays)
38
+
19
39
  // Helper for `codepoint_char_sequence` which stores an array of the most characters it will take to
20
40
  // represent a codepoint in a given character type.
21
41
  template <class Char, std::size_t Extent>
@@ -4,6 +4,7 @@ export import :utility.covariant_value;
4
4
  export import :utility.facade;
5
5
  export import :utility.hash;
6
6
  export import :utility.ranges;
7
+ export import :utility.sequence;
7
8
  export import :utility.string;
8
9
  // export import :utility.variant;
9
10
  import std;
@@ -31,22 +32,6 @@ export class non_moveable {
31
32
  auto operator=(const non_moveable&) = delete;
32
33
  };
33
34
 
34
- // Return a sequence of index constants
35
- export template <std::size_t Size>
36
- constexpr auto sequence = []() consteval -> auto {
37
- // With C++26 P2686 we can do constexpr decomposition. So instead of the `tuple` of
38
- // `integral_constants` it can be an array of `std::size_t`.
39
- // https://clang.llvm.org/cxx_status.html
40
-
41
- // std::array<std::size_t, Size> result{};
42
- // std::ranges::copy(std::ranges::views::iota(std::size_t{0}, Size), result.begin());
43
- // return result;
44
-
45
- return []<std::size_t... Index>(std::index_sequence<Index...> /*sequence*/) consteval {
46
- return std::tuple{std::integral_constant<std::size_t, Index>{}...};
47
- }(std::make_index_sequence<Size>());
48
- }();
49
-
50
35
  // Checked container / range access for types which don't have `.at()`
51
36
  export auto at(auto&& range, std::size_t index) -> decltype(auto) {
52
37
  if (range.size() <= index) {