nvfuser-cu121-torch25 0.2.25.dev20250201__cp312-cp312-manylinux_2_28_x86_64.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (242) hide show
  1. nvfuser/_C.cpython-312-x86_64-linux-gnu.so +0 -0
  2. nvfuser/__init__.py +618 -0
  3. nvfuser/__init__.pyi +4 -0
  4. nvfuser/contrib/__init__.py +9 -0
  5. nvfuser/contrib/nn/__init__.py +13 -0
  6. nvfuser/contrib/nn/normalization.py +725 -0
  7. nvfuser/include/nvfuser/alias_analysis.h +116 -0
  8. nvfuser/include/nvfuser/bfs.h +929 -0
  9. nvfuser/include/nvfuser/codegen.h +26 -0
  10. nvfuser/include/nvfuser/compute_at.h +28 -0
  11. nvfuser/include/nvfuser/compute_at_map.h +394 -0
  12. nvfuser/include/nvfuser/contiguity.h +351 -0
  13. nvfuser/include/nvfuser/cuda_utils.h +50 -0
  14. nvfuser/include/nvfuser/debug.h +50 -0
  15. nvfuser/include/nvfuser/device_lower/analysis/bank_conflict.h +53 -0
  16. nvfuser/include/nvfuser/device_lower/analysis/circular_buffer.h +109 -0
  17. nvfuser/include/nvfuser/device_lower/analysis/device_version.h +65 -0
  18. nvfuser/include/nvfuser/device_lower/analysis/divisible_split.h +28 -0
  19. nvfuser/include/nvfuser/device_lower/analysis/fused_reduction.h +36 -0
  20. nvfuser/include/nvfuser/device_lower/analysis/index_compute.h +322 -0
  21. nvfuser/include/nvfuser/device_lower/analysis/predicate_elimination.h +71 -0
  22. nvfuser/include/nvfuser/device_lower/analysis/sync_information.h +47 -0
  23. nvfuser/include/nvfuser/device_lower/analysis/tensor_memory.h +65 -0
  24. nvfuser/include/nvfuser/device_lower/analysis/thread_predicate.h +158 -0
  25. nvfuser/include/nvfuser/device_lower/analysis/tma.h +93 -0
  26. nvfuser/include/nvfuser/device_lower/analysis/trivial_broadcast.h +75 -0
  27. nvfuser/include/nvfuser/device_lower/id_model_options.h +135 -0
  28. nvfuser/include/nvfuser/device_lower/lower2device.h +391 -0
  29. nvfuser/include/nvfuser/device_lower/pass/alias_memory.h +37 -0
  30. nvfuser/include/nvfuser/device_lower/pass/allocation.h +32 -0
  31. nvfuser/include/nvfuser/device_lower/pass/circular_buffer.h +191 -0
  32. nvfuser/include/nvfuser/device_lower/pass/expr_sort.h +17 -0
  33. nvfuser/include/nvfuser/device_lower/pass/fusion_simplifier.h +21 -0
  34. nvfuser/include/nvfuser/device_lower/pass/grid_serialization.h +26 -0
  35. nvfuser/include/nvfuser/device_lower/pass/index.h +200 -0
  36. nvfuser/include/nvfuser/device_lower/pass/inline_ptx.h +16 -0
  37. nvfuser/include/nvfuser/device_lower/pass/insert_syncs.h +39 -0
  38. nvfuser/include/nvfuser/device_lower/pass/instrument.h +24 -0
  39. nvfuser/include/nvfuser/device_lower/pass/loop_rotation.h +150 -0
  40. nvfuser/include/nvfuser/device_lower/pass/loops.h +68 -0
  41. nvfuser/include/nvfuser/device_lower/pass/magic_zero.h +86 -0
  42. nvfuser/include/nvfuser/device_lower/pass/misaligned_vectorization.h +118 -0
  43. nvfuser/include/nvfuser/device_lower/pass/predicate.h +23 -0
  44. nvfuser/include/nvfuser/device_lower/pass/replace_size.h +24 -0
  45. nvfuser/include/nvfuser/device_lower/pass/scalar_hoist.h +115 -0
  46. nvfuser/include/nvfuser/device_lower/pass/unroll.h +98 -0
  47. nvfuser/include/nvfuser/device_lower/pass/vectorize_welford.h +45 -0
  48. nvfuser/include/nvfuser/device_lower/pass/warp_reduce.h +23 -0
  49. nvfuser/include/nvfuser/device_lower/utils.h +382 -0
  50. nvfuser/include/nvfuser/device_lower/validation.h +74 -0
  51. nvfuser/include/nvfuser/disjoint_set.h +556 -0
  52. nvfuser/include/nvfuser/dispatch.h +334 -0
  53. nvfuser/include/nvfuser/driver_api.h +49 -0
  54. nvfuser/include/nvfuser/dynamic_transform.h +316 -0
  55. nvfuser/include/nvfuser/dynamic_type/C++20/type_traits +37 -0
  56. nvfuser/include/nvfuser/dynamic_type/dynamic_type.h +969 -0
  57. nvfuser/include/nvfuser/dynamic_type/error.h +24 -0
  58. nvfuser/include/nvfuser/dynamic_type/type_traits.h +703 -0
  59. nvfuser/include/nvfuser/evaluator_common.h +295 -0
  60. nvfuser/include/nvfuser/exceptions.h +283 -0
  61. nvfuser/include/nvfuser/expr_evaluator.h +125 -0
  62. nvfuser/include/nvfuser/expr_simplifier.h +218 -0
  63. nvfuser/include/nvfuser/flatbuffers/allocator.h +68 -0
  64. nvfuser/include/nvfuser/flatbuffers/array.h +253 -0
  65. nvfuser/include/nvfuser/flatbuffers/base.h +486 -0
  66. nvfuser/include/nvfuser/flatbuffers/buffer.h +154 -0
  67. nvfuser/include/nvfuser/flatbuffers/buffer_ref.h +53 -0
  68. nvfuser/include/nvfuser/flatbuffers/code_generator.h +80 -0
  69. nvfuser/include/nvfuser/flatbuffers/code_generators.h +234 -0
  70. nvfuser/include/nvfuser/flatbuffers/default_allocator.h +64 -0
  71. nvfuser/include/nvfuser/flatbuffers/detached_buffer.h +114 -0
  72. nvfuser/include/nvfuser/flatbuffers/flatbuffer_builder.h +1225 -0
  73. nvfuser/include/nvfuser/flatbuffers/flatbuffers.h +272 -0
  74. nvfuser/include/nvfuser/flatbuffers/flatc.h +130 -0
  75. nvfuser/include/nvfuser/flatbuffers/flex_flat_util.h +36 -0
  76. nvfuser/include/nvfuser/flatbuffers/flexbuffers.h +1889 -0
  77. nvfuser/include/nvfuser/flatbuffers/grpc.h +300 -0
  78. nvfuser/include/nvfuser/flatbuffers/hash.h +127 -0
  79. nvfuser/include/nvfuser/flatbuffers/idl.h +1359 -0
  80. nvfuser/include/nvfuser/flatbuffers/minireflect.h +420 -0
  81. nvfuser/include/nvfuser/flatbuffers/reflection.h +522 -0
  82. nvfuser/include/nvfuser/flatbuffers/reflection_generated.h +1471 -0
  83. nvfuser/include/nvfuser/flatbuffers/registry.h +128 -0
  84. nvfuser/include/nvfuser/flatbuffers/stl_emulation.h +513 -0
  85. nvfuser/include/nvfuser/flatbuffers/string.h +64 -0
  86. nvfuser/include/nvfuser/flatbuffers/struct.h +53 -0
  87. nvfuser/include/nvfuser/flatbuffers/table.h +168 -0
  88. nvfuser/include/nvfuser/flatbuffers/util.h +731 -0
  89. nvfuser/include/nvfuser/flatbuffers/vector.h +393 -0
  90. nvfuser/include/nvfuser/flatbuffers/vector_downward.h +273 -0
  91. nvfuser/include/nvfuser/flatbuffers/verifier.h +317 -0
  92. nvfuser/include/nvfuser/fusion.h +511 -0
  93. nvfuser/include/nvfuser/fusion_guard.h +37 -0
  94. nvfuser/include/nvfuser/fusion_profiler.h +311 -0
  95. nvfuser/include/nvfuser/fusion_segmenter.h +751 -0
  96. nvfuser/include/nvfuser/global_allocator.h +27 -0
  97. nvfuser/include/nvfuser/grouped_reduction.h +47 -0
  98. nvfuser/include/nvfuser/host_ir/container.h +60 -0
  99. nvfuser/include/nvfuser/host_ir/executor.h +152 -0
  100. nvfuser/include/nvfuser/host_ir/host_ir.h +320 -0
  101. nvfuser/include/nvfuser/host_ir/lower.h +35 -0
  102. nvfuser/include/nvfuser/id_model/circular_buffer_indexing.h +56 -0
  103. nvfuser/include/nvfuser/id_model/contiguity.h +166 -0
  104. nvfuser/include/nvfuser/id_model/id_model.h +359 -0
  105. nvfuser/include/nvfuser/id_model/id_model_index_compute.h +81 -0
  106. nvfuser/include/nvfuser/id_model/indexing.h +208 -0
  107. nvfuser/include/nvfuser/id_model/indexing_traversal.h +72 -0
  108. nvfuser/include/nvfuser/id_model/indexing_utils.h +62 -0
  109. nvfuser/include/nvfuser/id_model/loop_promotion.h +180 -0
  110. nvfuser/include/nvfuser/id_model/predicate_indexing.h +104 -0
  111. nvfuser/include/nvfuser/id_model/schedule.h +54 -0
  112. nvfuser/include/nvfuser/id_model/to_string.h +87 -0
  113. nvfuser/include/nvfuser/id_model/transform_replay.h +58 -0
  114. nvfuser/include/nvfuser/id_model/utils.h +176 -0
  115. nvfuser/include/nvfuser/id_model/validation_utils.h +55 -0
  116. nvfuser/include/nvfuser/index_compute.h +651 -0
  117. nvfuser/include/nvfuser/instrumentation.h +107 -0
  118. nvfuser/include/nvfuser/ir/all_nodes.h +14 -0
  119. nvfuser/include/nvfuser/ir/base_nodes.h +687 -0
  120. nvfuser/include/nvfuser/ir/builder.h +215 -0
  121. nvfuser/include/nvfuser/ir/builder_passkey.h +29 -0
  122. nvfuser/include/nvfuser/ir/cloner.h +185 -0
  123. nvfuser/include/nvfuser/ir/container.h +226 -0
  124. nvfuser/include/nvfuser/ir/graphviz.h +119 -0
  125. nvfuser/include/nvfuser/ir/interface_nodes.h +957 -0
  126. nvfuser/include/nvfuser/ir/internal_base_nodes.h +744 -0
  127. nvfuser/include/nvfuser/ir/internal_nodes.h +2792 -0
  128. nvfuser/include/nvfuser/ir/iostream.h +98 -0
  129. nvfuser/include/nvfuser/ir/printer.h +57 -0
  130. nvfuser/include/nvfuser/ir/utils.h +801 -0
  131. nvfuser/include/nvfuser/iter_visitor.h +661 -0
  132. nvfuser/include/nvfuser/kernel.h +299 -0
  133. nvfuser/include/nvfuser/kernel_db/kernel_db.h +109 -0
  134. nvfuser/include/nvfuser/kernel_db/utils.h +37 -0
  135. nvfuser/include/nvfuser/kernel_ir.h +1457 -0
  136. nvfuser/include/nvfuser/kernel_ir_dispatch.h +147 -0
  137. nvfuser/include/nvfuser/linked_hash_map.h +97 -0
  138. nvfuser/include/nvfuser/logical_domain_map.h +577 -0
  139. nvfuser/include/nvfuser/macros.h +23 -0
  140. nvfuser/include/nvfuser/mma_type.h +257 -0
  141. nvfuser/include/nvfuser/multidevice/c10d_mock.h +175 -0
  142. nvfuser/include/nvfuser/multidevice/communication.h +232 -0
  143. nvfuser/include/nvfuser/multidevice/communicator.h +179 -0
  144. nvfuser/include/nvfuser/multidevice/device_mesh.h +95 -0
  145. nvfuser/include/nvfuser/multidevice/executor.h +107 -0
  146. nvfuser/include/nvfuser/multidevice/multidevice.h +18 -0
  147. nvfuser/include/nvfuser/multidevice/utils.h +187 -0
  148. nvfuser/include/nvfuser/non_divisible_split.h +86 -0
  149. nvfuser/include/nvfuser/opaque_type.h +129 -0
  150. nvfuser/include/nvfuser/ops/alias.h +192 -0
  151. nvfuser/include/nvfuser/ops/all_ops.h +13 -0
  152. nvfuser/include/nvfuser/ops/arith.h +712 -0
  153. nvfuser/include/nvfuser/ops/composite.h +130 -0
  154. nvfuser/include/nvfuser/ops/indexing.h +55 -0
  155. nvfuser/include/nvfuser/ops/normalization.h +263 -0
  156. nvfuser/include/nvfuser/ops/utils.h +127 -0
  157. nvfuser/include/nvfuser/options.h +313 -0
  158. nvfuser/include/nvfuser/parallel_dimension_map.h +95 -0
  159. nvfuser/include/nvfuser/parallel_type_bitmap.h +365 -0
  160. nvfuser/include/nvfuser/polymorphic_value.h +432 -0
  161. nvfuser/include/nvfuser/predicate_compute.h +213 -0
  162. nvfuser/include/nvfuser/python_frontend/distributed_tensor.h +50 -0
  163. nvfuser/include/nvfuser/python_frontend/fusion_cache.h +298 -0
  164. nvfuser/include/nvfuser/python_frontend/fusion_definition.h +372 -0
  165. nvfuser/include/nvfuser/python_frontend/fusion_record.h +3124 -0
  166. nvfuser/include/nvfuser/python_frontend/fusion_state.h +143 -0
  167. nvfuser/include/nvfuser/python_frontend/python_bindings.h +27 -0
  168. nvfuser/include/nvfuser/python_frontend/segmentation.h +246 -0
  169. nvfuser/include/nvfuser/python_frontend/translation.h +20 -0
  170. nvfuser/include/nvfuser/python_frontend/translation_utils.h +308 -0
  171. nvfuser/include/nvfuser/scheduler/all_schedulers.h +17 -0
  172. nvfuser/include/nvfuser/scheduler/ampere_multi_matmul.h +206 -0
  173. nvfuser/include/nvfuser/scheduler/cache_policy_refiner.h +19 -0
  174. nvfuser/include/nvfuser/scheduler/compile_time_info.h +322 -0
  175. nvfuser/include/nvfuser/scheduler/debug_utils.h +68 -0
  176. nvfuser/include/nvfuser/scheduler/expr_eval_sched.h +45 -0
  177. nvfuser/include/nvfuser/scheduler/heuristic.h +113 -0
  178. nvfuser/include/nvfuser/scheduler/hopper_multi_matmul.h +204 -0
  179. nvfuser/include/nvfuser/scheduler/mark_aliases.h +19 -0
  180. nvfuser/include/nvfuser/scheduler/matmul.h +40 -0
  181. nvfuser/include/nvfuser/scheduler/matmul_heuristic.h +293 -0
  182. nvfuser/include/nvfuser/scheduler/matmul_heuristic_plugin.h +65 -0
  183. nvfuser/include/nvfuser/scheduler/matmul_heuristic_plugin_api.h +99 -0
  184. nvfuser/include/nvfuser/scheduler/matmul_utils.h +54 -0
  185. nvfuser/include/nvfuser/scheduler/mma_utils.h +500 -0
  186. nvfuser/include/nvfuser/scheduler/multi_matmul.h +74 -0
  187. nvfuser/include/nvfuser/scheduler/no_op.h +48 -0
  188. nvfuser/include/nvfuser/scheduler/normalization_inner.h +49 -0
  189. nvfuser/include/nvfuser/scheduler/normalization_inner_outer.h +51 -0
  190. nvfuser/include/nvfuser/scheduler/normalization_outer.h +48 -0
  191. nvfuser/include/nvfuser/scheduler/normalization_utils.h +379 -0
  192. nvfuser/include/nvfuser/scheduler/pointwise.h +183 -0
  193. nvfuser/include/nvfuser/scheduler/pointwise_heuristic.h +118 -0
  194. nvfuser/include/nvfuser/scheduler/pointwise_utils.h +24 -0
  195. nvfuser/include/nvfuser/scheduler/reduction.h +43 -0
  196. nvfuser/include/nvfuser/scheduler/reduction_heuristic.h +339 -0
  197. nvfuser/include/nvfuser/scheduler/reduction_utils.h +159 -0
  198. nvfuser/include/nvfuser/scheduler/registry.h +97 -0
  199. nvfuser/include/nvfuser/scheduler/registry_utils.h +111 -0
  200. nvfuser/include/nvfuser/scheduler/resize.h +41 -0
  201. nvfuser/include/nvfuser/scheduler/resize_heuristic.h +67 -0
  202. nvfuser/include/nvfuser/scheduler/runtime_info.h +166 -0
  203. nvfuser/include/nvfuser/scheduler/scheduler_types.h +80 -0
  204. nvfuser/include/nvfuser/scheduler/transpose.h +114 -0
  205. nvfuser/include/nvfuser/scheduler/transpose_heuristic.h +164 -0
  206. nvfuser/include/nvfuser/scheduler/utils.h +771 -0
  207. nvfuser/include/nvfuser/scheduler/vectorize_helper.h +349 -0
  208. nvfuser/include/nvfuser/serde/factory.h +55 -0
  209. nvfuser/include/nvfuser/serde/fusion_cache_generated.h +4319 -0
  210. nvfuser/include/nvfuser/serde/fusion_record.h +124 -0
  211. nvfuser/include/nvfuser/serde/polymorphic_value.h +52 -0
  212. nvfuser/include/nvfuser/serde/utils.h +34 -0
  213. nvfuser/include/nvfuser/struct.inl +127 -0
  214. nvfuser/include/nvfuser/swizzle.h +54 -0
  215. nvfuser/include/nvfuser/sys_utils.h +40 -0
  216. nvfuser/include/nvfuser/tensor_metadata.h +118 -0
  217. nvfuser/include/nvfuser/tma.h +124 -0
  218. nvfuser/include/nvfuser/transform_iter.h +522 -0
  219. nvfuser/include/nvfuser/transform_replay.h +297 -0
  220. nvfuser/include/nvfuser/transform_rfactor.h +33 -0
  221. nvfuser/include/nvfuser/transform_view.h +136 -0
  222. nvfuser/include/nvfuser/type.h +1125 -0
  223. nvfuser/include/nvfuser/type_promotion.h +61 -0
  224. nvfuser/include/nvfuser/utils.h +619 -0
  225. nvfuser/include/nvfuser/val_graph.h +446 -0
  226. nvfuser/include/nvfuser/val_graph_visitor.h +259 -0
  227. nvfuser/include/nvfuser/validator_utils.h +92 -0
  228. nvfuser/include/nvfuser/vectorization_info.h +31 -0
  229. nvfuser/include/nvfuser/visibility.h +21 -0
  230. nvfuser/lib/libnvfuser_codegen.so +0 -0
  231. nvfuser/nvfuser_version.py +69 -0
  232. nvfuser/pytorch_utils.py +184 -0
  233. nvfuser/share/cmake/nvfuser/NvfuserConfig-release.cmake +20 -0
  234. nvfuser/share/cmake/nvfuser/NvfuserConfig.cmake +106 -0
  235. nvfuser/utils.py +18 -0
  236. nvfuser/version.py +1 -0
  237. nvfuser_cu121_torch25-0.2.25.dev20250201.dist-info/LICENSE +976 -0
  238. nvfuser_cu121_torch25-0.2.25.dev20250201.dist-info/METADATA +16 -0
  239. nvfuser_cu121_torch25-0.2.25.dev20250201.dist-info/RECORD +242 -0
  240. nvfuser_cu121_torch25-0.2.25.dev20250201.dist-info/WHEEL +5 -0
  241. nvfuser_cu121_torch25-0.2.25.dev20250201.dist-info/top_level.txt +1 -0
  242. nvfuser_cu121_torch25.libs/libnvToolsExt-847d78f2.so.1.0.0 +0 -0
@@ -0,0 +1,969 @@
1
+ // clang-format off
2
+ /*
3
+ * SPDX-FileCopyrightText: Copyright (c) 2023-present NVIDIA CORPORATION & AFFILIATES.
4
+ * All rights reserved.
5
+ * SPDX-License-Identifier: BSD-3-Clause
6
+ */
7
+ // clang-format on
8
+ #pragma once
9
+
10
+ #include <algorithm>
11
+ #include <cstddef>
12
+ #include <initializer_list>
13
+ #include <optional>
14
+ #include <ostream>
15
+ #include <tuple>
16
+ #include <type_traits>
17
+ #include <typeinfo>
18
+ #include <variant>
19
+
20
+ #include "C++20/type_traits"
21
+ #include "error.h"
22
+ #include "type_traits.h"
23
+
24
+ namespace dynamic_type {
25
+
26
+ // We must disable a lot of compiler warnings to make this work. The reason for
27
+ // the need to disable these warnings is not because the code quality in this
28
+ // file is bad, but because these apparently "bad" practices are necessary. For
29
+ // example, if you have a dynamic type that can be either a bool or a class
30
+ // SomeType{}, then we should support the ~ operator on it, because in the C++
31
+ // standard bool supports it. Usually, when people write code like ~bool, they
32
+ // are making a mistake, and the compiler will want you to use !bool instead.
33
+ // However, in our case here we will allow everything that the C++ standard
34
+ // allows. The compiler should yell at the user who uses DynamicType with ~
35
+ // but not at us for implementing it.
36
+
37
+ #if defined(__clang__)
38
+ #pragma clang diagnostic push
39
+ #pragma clang diagnostic ignored "-Wunused-comparison"
40
+ #pragma clang diagnostic ignored "-Wbitwise-instead-of-logical"
41
+ #pragma clang diagnostic ignored "-Wliteral-conversion"
42
+ #pragma clang diagnostic ignored "-Wunused-lambda-capture"
43
+ #pragma clang diagnostic ignored "-Wunknown-warning-option"
44
+ #pragma clang diagnostic ignored "-Wbool-operation"
45
+ #endif
46
+
47
+ #if defined(__GNUC__) && !defined(__clang__)
48
+ #pragma GCC diagnostic push
49
+ #pragma GCC diagnostic ignored "-Wbool-operation"
50
+ // gcc, even the latest version (13.1.1), is complaining about the following
51
+ // code:
52
+ // std::optional<bool> ret = std::nullopt;
53
+ // ...
54
+ // DYNAMIC_TYPE_CHECK(ret.has_value(), ...);
55
+ // return ret.value();
56
+ // saying that ret.value() is used uninitialized. This complaint is totoally
57
+ // nonsense.
58
+ #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
59
+ #endif
60
+
61
+ template <template <typename...> typename... Templates>
62
+ // Note: `Templates` is a list of templates, not a list of types.
63
+ // Just like std::vector is a template, std::vector<int> is a type.
64
+ struct Containers {
65
+ template <typename DynamicType, typename... MemberTypes>
66
+ using VariantType =
67
+ std::variant<std::monostate, MemberTypes..., Templates<DynamicType>...>;
68
+
69
+ template <typename DynamicType, typename... MemberTypes>
70
+ using TypeIdentitiesAsTuple = std::tuple<
71
+ std::type_identity<std::monostate>,
72
+ std::type_identity<MemberTypes>...,
73
+ std::type_identity<Templates<DynamicType>>...>;
74
+
75
+ template <typename DynamicType, typename... MemberTypes>
76
+ using ForAllTypes = dynamic_type::
77
+ ForAllTypes<std::monostate, MemberTypes..., Templates<DynamicType>...>;
78
+
79
+ // Check if T is one of the types in the type list MemberTypes..., or a
80
+ // container
81
+ template <typename T, typename DynamicType, typename... MemberTypes>
82
+ static constexpr auto is_candidate_type = dynamic_type::
83
+ belongs_to<T, std::monostate, MemberTypes..., Templates<DynamicType>...>;
84
+
85
+ template <typename ItemT>
86
+ using ForAllContainerTypes = dynamic_type::ForAllTypes<Templates<ItemT>...>;
87
+
88
+ template <typename ItemT>
89
+ static constexpr auto
90
+ all_container_type_identities_constructible_from_initializer_list() {
91
+ return dynamic_type::remove_void_from_tuple(ForAllContainerTypes<
92
+ ItemT>{}([](auto t) {
93
+ using T = typename decltype(t)::type;
94
+ if constexpr (std::is_constructible_v<T, std::initializer_list<ItemT>>) {
95
+ return std::type_identity<T>{};
96
+ } else {
97
+ return;
98
+ }
99
+ }));
100
+ }
101
+
102
+ template <typename ItemT>
103
+ using AllContainerTypeIdentitiesConstructibleFromInitializerList =
104
+ decltype(all_container_type_identities_constructible_from_initializer_list<
105
+ ItemT>());
106
+ };
107
+
108
+ using NoContainers = Containers<>;
109
+
110
+ template <typename Containers, typename... Ts>
111
+ struct DynamicType {
112
+ using VariantType =
113
+ typename Containers::template VariantType<DynamicType, Ts...>;
114
+ VariantType value;
115
+
116
+ using TypeIdentitiesAsTuple =
117
+ typename Containers::template TypeIdentitiesAsTuple<DynamicType, Ts...>;
118
+ static constexpr TypeIdentitiesAsTuple type_identities_as_tuple{};
119
+
120
+ static constexpr std::size_t num_types =
121
+ std::tuple_size_v<TypeIdentitiesAsTuple>;
122
+
123
+ using ForAllTypes =
124
+ typename Containers::template ForAllTypes<DynamicType, Ts...>;
125
+ static constexpr ForAllTypes for_all_types{};
126
+
127
+ // Check if T is one of the types in the type list Ts... or a container
128
+ template <typename T>
129
+ static constexpr auto is_candidate_type =
130
+ Containers::template is_candidate_type<T, DynamicType, Ts...>;
131
+
132
+ template <typename T>
133
+ static constexpr bool can_cast_to = any_check(
134
+ [](auto t) {
135
+ return opcheck<typename decltype(t)::type>.canCastTo(opcheck<T>);
136
+ },
137
+ type_identities_as_tuple);
138
+
139
+ template <typename ItemT>
140
+ using AllContainerTypeIdentitiesConstructibleFromInitializerList =
141
+ typename Containers::
142
+ template AllContainerTypeIdentitiesConstructibleFromInitializerList<
143
+ ItemT>;
144
+
145
+ template <typename ItemT>
146
+ static constexpr auto
147
+ num_container_types_constructible_from_initializer_list =
148
+ std::tuple_size_v<
149
+ AllContainerTypeIdentitiesConstructibleFromInitializerList<
150
+ ItemT>>;
151
+
152
+ template <typename FuncT, typename FirstArg, typename... OtherArgs>
153
+ static inline constexpr decltype(auto) dispatch(
154
+ FuncT&& f,
155
+ FirstArg&& arg0,
156
+ OtherArgs&&... args) {
157
+ // Recursively dispatch on `args`, only leaving arg0 as undispatched
158
+ // argument
159
+ auto f0 = [&](auto&& a0) -> decltype(auto) {
160
+ if constexpr (sizeof...(OtherArgs) == 0) {
161
+ return std::forward<FuncT>(f)(std::forward<decltype(a0)>(a0));
162
+ } else {
163
+ auto f_others = [&](auto&&... others) -> decltype(auto) {
164
+ return std::forward<FuncT>(f)(
165
+ std::forward<decltype(a0)>(a0),
166
+ std::forward<decltype(others)>(others)...);
167
+ };
168
+ return dispatch(f_others, std::forward<OtherArgs>(args)...);
169
+ }
170
+ };
171
+ // Does arg0 need dispatch?
172
+ if constexpr (std::is_same_v<std::decay_t<FirstArg>, DynamicType>) {
173
+ // Infer return result: if f always returns the same type, then we return
174
+ // the same type as well. Otherwise, we return DynamicType assuming that
175
+ // DynamicType is the common holder of these types. Void is treated
176
+ // specially here: if for some case the function returns some type, and
177
+ // for other cases the function returns void, then we ignore void and use
178
+ // the cases with return value for inference. We decide to do this because
179
+ // non-void return values can be ignored, but void returning can never
180
+ // pass any information. There is no single best inference strategy that
181
+ // fits all cases, ignoring void seems to be good tradeoff.
182
+ auto get_single_result_type = [](auto t) {
183
+ using T = typename decltype(t)::type;
184
+ using RetT = decltype(f0(std::declval<T>()));
185
+ if constexpr (!std::is_void_v<RetT>) {
186
+ return std::type_identity<RetT>{};
187
+ } else {
188
+ // return void instead of std::type_identity<void> so that we can use
189
+ // remove_void_from_tuple to remove it.
190
+ return;
191
+ }
192
+ };
193
+ using result_types = decltype(remove_void_from_tuple(
194
+ DynamicType::for_all_types(get_single_result_type)));
195
+ constexpr bool returns_void = (std::tuple_size_v<result_types> == 0);
196
+ if constexpr (returns_void) {
197
+ DynamicType::for_all_types([&](auto t) -> decltype(auto) {
198
+ using T = typename decltype(t)::type;
199
+ if (arg0.template is<T>()) {
200
+ f0(arg0.template as<T>());
201
+ }
202
+ });
203
+ return;
204
+ } else {
205
+ constexpr bool has_single_return_type =
206
+ are_all_same<result_types>::value;
207
+ using result_type = std::conditional_t<
208
+ has_single_return_type,
209
+ typename std::tuple_element_t<0, result_types>::type,
210
+ DynamicType>;
211
+ // Needs to wrap reference as optional<reference_wrapper<T>> because
212
+ // C++ does not allow rebinding a reference.
213
+ constexpr bool is_reference = std::is_reference_v<result_type>;
214
+ using ret_storage_t = std::conditional_t<
215
+ is_reference,
216
+ std::optional<
217
+ std::reference_wrapper<std::remove_reference_t<result_type>>>,
218
+ result_type>;
219
+ ret_storage_t ret{};
220
+ DynamicType::for_all_types([&](auto t) -> decltype(auto) {
221
+ using T = typename decltype(t)::type;
222
+ if (arg0.template is<T>()) {
223
+ const T& a0 = arg0.template as<T>();
224
+ if constexpr (std::
225
+ is_convertible_v<decltype(f0(a0)), result_type>) {
226
+ ret = f0(a0);
227
+ } else {
228
+ DYNAMIC_TYPE_CHECK(
229
+ false,
230
+ "Result is dynamic but not convertible to result type");
231
+ }
232
+ }
233
+ });
234
+ if constexpr (is_reference) {
235
+ return ret->get();
236
+ } else {
237
+ return ret;
238
+ }
239
+ }
240
+ } else {
241
+ // No need to dispatch arg0, just perfectly forwarding it.
242
+ return f0(std::forward<FirstArg>(arg0));
243
+ }
244
+ }
245
+
246
+ constexpr DynamicType() = default;
247
+
248
+ template <typename T, typename = decltype(VariantType(std::declval<T>()))>
249
+ constexpr DynamicType(T&& value) : value(std::forward<T>(value)) {}
250
+
251
+ template <
252
+ template <typename...> typename Template,
253
+ typename ItemT,
254
+ typename = std::enable_if_t<
255
+ is_candidate_type<Template<DynamicType>> &&
256
+ !std::is_same_v<ItemT, DynamicType>>>
257
+ constexpr DynamicType(Template<ItemT> value)
258
+ : value([](auto input) {
259
+ Template<DynamicType> result;
260
+ std::transform(
261
+ input.begin(),
262
+ input.end(),
263
+ std::back_inserter(result),
264
+ [](auto& item) { return DynamicType(std::move(item)); });
265
+ return result;
266
+ }(std::move(value))) {}
267
+
268
+ template <
269
+ typename ItemT = DynamicType,
270
+ typename = std::enable_if_t<
271
+ // enable this ctor only when there is only one container supporting
272
+ // initializer_list, otherwise it is ambiguous to tell which container
273
+ // to use.
274
+ num_container_types_constructible_from_initializer_list<ItemT> == 1>>
275
+ constexpr DynamicType(std::initializer_list<DynamicType> list)
276
+ : DynamicType(typename std::tuple_element_t<
277
+ 0,
278
+ AllContainerTypeIdentitiesConstructibleFromInitializerList<
279
+ DynamicType>>::type(list)) {}
280
+
281
+ // Returns the type_info of the actual type of the variant value. For
282
+ // example, if value holds an int, then this will return typeid(int).
283
+ const std::type_info& type() const {
284
+ return std::visit(
285
+ [](auto value) -> const std::type_info& { return typeid(value); },
286
+ value);
287
+ }
288
+
289
+ template <typename T>
290
+ constexpr bool is() const {
291
+ return std::holds_alternative<T>(value);
292
+ }
293
+
294
+ template <template <typename...> typename Template>
295
+ constexpr bool is() const {
296
+ return is<Template<DynamicType>>();
297
+ }
298
+
299
+ constexpr bool isNull() const {
300
+ return std::holds_alternative<std::monostate>(value);
301
+ }
302
+
303
+ constexpr bool hasValue() const {
304
+ return !isNull();
305
+ }
306
+
307
+ template <typename T, typename = std::enable_if_t<is_candidate_type<T>>>
308
+ constexpr const T& as() const {
309
+ return std::get<T>(value);
310
+ }
311
+
312
+ template <typename T, typename = std::enable_if_t<is_candidate_type<T>>>
313
+ constexpr T& as() {
314
+ return std::get<T>(value);
315
+ }
316
+
317
+ template <
318
+ template <typename...> typename Template,
319
+ typename = std::enable_if_t<is_candidate_type<Template<DynamicType>>>>
320
+ constexpr const Template<DynamicType>& as() const {
321
+ return as<Template<DynamicType>>();
322
+ }
323
+
324
+ template <
325
+ template <typename...> typename Template,
326
+ typename = std::enable_if_t<is_candidate_type<Template<DynamicType>>>>
327
+ constexpr Template<DynamicType>& as() {
328
+ return as<Template<DynamicType>>();
329
+ }
330
+
331
+ template <typename T, typename = std::enable_if_t<can_cast_to<T>>>
332
+ explicit constexpr operator T() const {
333
+ return dispatch(
334
+ [](auto x) -> decltype(auto) {
335
+ using X = decltype(x);
336
+ if constexpr (opcheck<X>.canCastTo(opcheck<T>)) {
337
+ return (T)x;
338
+ }
339
+ },
340
+ *this);
341
+ }
342
+
343
+ template <
344
+ template <typename...> typename Template,
345
+ typename ItemT,
346
+ typename = std::enable_if_t<
347
+ is_candidate_type<Template<DynamicType>> && can_cast_to<ItemT>>>
348
+ explicit constexpr operator Template<ItemT>() const {
349
+ DYNAMIC_TYPE_CHECK(
350
+ is<Template<DynamicType>>(),
351
+ "Cannot cast from ",
352
+ type().name(),
353
+ " to ",
354
+ typeid(Template<ItemT>).name(),
355
+ " : incompatible type");
356
+ Template<ItemT> result;
357
+ std::transform(
358
+ as<Template<DynamicType>>().begin(),
359
+ as<Template<DynamicType>>().end(),
360
+ std::back_inserter(result),
361
+ [](const auto& item) { return (ItemT)item; });
362
+ return result;
363
+ }
364
+
365
+ // Intentionally not overloading operator=, because the compiler generated
366
+ // default behavior usually makes more sense than the overloaded one. For
367
+ // example, if we have
368
+ // struct SomeType {};
369
+ // using IntOrCustom = DynamicType<int, SomeType>;
370
+ // IntOrCustom x(1);
371
+ // IntOrCustom y(SomeType{});
372
+ // x = y;
373
+ // Then the compiler generated behavior will get us SomeType{} for x, but if
374
+ // we overload based on the underlying type, we will get a runtime error,
375
+ // because it is not possible to assign SomeType{} to an int.
376
+
377
+ constexpr decltype(auto) operator->() {
378
+ return dispatch(
379
+ [](auto&& x) -> decltype(auto) {
380
+ using X = decltype(x);
381
+ using XD = std::decay_t<X>;
382
+ if constexpr (std::is_pointer_v<XD>) {
383
+ return (std::decay_t<X>)(x);
384
+ } else if constexpr (opcheck<XD>->value()) {
385
+ return std::forward<X>(x).operator->();
386
+ }
387
+ },
388
+ *this);
389
+ }
390
+
391
+ template <typename IndexT>
392
+ static constexpr bool has_square_bracket = any_check(
393
+ [](auto t) {
394
+ using T = typename decltype(t)::type;
395
+ if constexpr (opcheck<T>[opcheck<IndexT>]) {
396
+ return std::is_same_v<
397
+ decltype(std::declval<T>()[std::declval<IndexT>()]),
398
+ DynamicType&>;
399
+ }
400
+ return false;
401
+ },
402
+ type_identities_as_tuple);
403
+
404
+ #define DEFINE_SQUARE_BRACKET_OPERATOR(__const) \
405
+ template <typename IndexT> \
406
+ std::enable_if_t< \
407
+ !std::is_same_v<IndexT, DynamicType> && has_square_bracket<IndexT>, \
408
+ __const DynamicType&> \
409
+ operator[](const IndexT& i) __const { \
410
+ std::optional<std::reference_wrapper<__const DynamicType>> ret = \
411
+ std::nullopt; \
412
+ for_all_types([this, &ret, &i](auto t) { \
413
+ using T = typename decltype(t)::type; \
414
+ if constexpr (opcheck<T>[opcheck<IndexT>]) { \
415
+ if constexpr (std::is_same_v< \
416
+ decltype(std::declval<T>()[std::declval<IndexT>()]), \
417
+ DynamicType&>) { \
418
+ if (is<T>()) { \
419
+ ret = std::ref(as<T>()[i]); \
420
+ } \
421
+ } \
422
+ } \
423
+ }); \
424
+ DYNAMIC_TYPE_CHECK( \
425
+ ret.has_value(), \
426
+ "Cannot index ", \
427
+ type().name(), \
428
+ " with ", \
429
+ typeid(IndexT).name(), \
430
+ " : incompatible type"); \
431
+ return ret.value(); \
432
+ }
433
+
434
+ DEFINE_SQUARE_BRACKET_OPERATOR()
435
+ DEFINE_SQUARE_BRACKET_OPERATOR(const)
436
+ #undef DEFINE_SQUARE_BRACKET_OPERATOR
437
+
438
+ static constexpr bool has_any_square_bracket = any_check(
439
+ [](auto t) {
440
+ using IndexT = typename decltype(t)::type;
441
+ return has_square_bracket<IndexT>;
442
+ },
443
+ type_identities_as_tuple);
444
+
445
+ #define DEFINE_SQUARE_BRACKET_OPERATOR(__const) \
446
+ template <typename DT> \
447
+ std::enable_if_t< \
448
+ std::is_same_v<DT, DynamicType> && has_any_square_bracket, \
449
+ __const DynamicType&> \
450
+ operator[](const DT& i) __const { \
451
+ std::optional<std::reference_wrapper<__const DynamicType>> ret = \
452
+ std::nullopt; \
453
+ for_all_types([this, &ret, &i](auto t) { \
454
+ using IndexT = typename decltype(t)::type; \
455
+ if constexpr (has_square_bracket<IndexT>) { \
456
+ if (i.template is<IndexT>()) { \
457
+ ret = std::ref((*this)[i.template as<IndexT>()]); \
458
+ } \
459
+ } \
460
+ }); \
461
+ DYNAMIC_TYPE_CHECK( \
462
+ ret.has_value(), \
463
+ "Cannot index ", \
464
+ type().name(), \
465
+ " with ", \
466
+ i.type().name(), \
467
+ " : incompatible type"); \
468
+ return ret.value(); \
469
+ }
470
+
471
+ DEFINE_SQUARE_BRACKET_OPERATOR()
472
+ DEFINE_SQUARE_BRACKET_OPERATOR(const)
473
+ #undef DEFINE_SQUARE_BRACKET_OPERATOR
474
+
475
+ // ->* over for accessing candidate members. This will be converted as a .*
476
+ // with a candidate type. For example, if you have:
477
+ // DynamicType<NoContainers, A, B, C> abc;
478
+ // then you can use abc->*A::x to access the member x of A. Member access also
479
+ // support functions, just make sure that you get the correct precedence. For
480
+ // example: use (abc->*A::print)() instead of abc->*A::print().
481
+
482
+ #define DEFINE_ARROW_STAR_OPERATOR(__const) \
483
+ template < \
484
+ typename Ret, \
485
+ typename Class, \
486
+ typename = std::enable_if_t<is_candidate_type<Class>>> \
487
+ constexpr decltype(auto) operator->*(Ret Class::* member) __const { \
488
+ /* Use decltype(auto) instead of auto as return type so that references */ \
489
+ /* and qualifiers are preserved*/ \
490
+ if constexpr (std::is_function_v<Ret>) { \
491
+ return [this, member](auto&&... args) { \
492
+ return (as<Class>().*member)(std::forward<decltype(args)>(args)...); \
493
+ }; \
494
+ } else { \
495
+ return as<Class>().*member; \
496
+ } \
497
+ }
498
+
499
+ DEFINE_ARROW_STAR_OPERATOR()
500
+ DEFINE_ARROW_STAR_OPERATOR(const)
501
+ #undef DEFINE_ARROW_STAR_OPERATOR
502
+
503
+ // ->* operator for non-candidate access. This will just forward the argument
504
+ // to the overloaded ->* of candidates. Due to limitations of C++'s type
505
+ // system, we can only enable this when all the types in the type list that
506
+ // support this operator have the same return type.
507
+
508
+ #define DEFINE_ARROW_STAR_OPERATOR(__const) \
509
+ template <typename MemberT> \
510
+ static constexpr auto all_arrow_star_ret_types##__const = \
511
+ remove_void_from_tuple(for_all_types([](auto t) { \
512
+ using T = typename decltype(t)::type; \
513
+ if constexpr (opcheck<T>->*opcheck<MemberT>) { \
514
+ return std::type_identity< \
515
+ decltype(std::declval<__const T>()->*std::declval<MemberT>())>{}; \
516
+ } \
517
+ })); \
518
+ \
519
+ template <typename MemberT> \
520
+ using AllArrowStarRetTypes##__const = \
521
+ decltype(all_arrow_star_ret_types##__const<MemberT>); \
522
+ \
523
+ template <typename MemberT> \
524
+ static constexpr bool all_arrow_star_ret_types_are_same##__const = \
525
+ all_same_type(all_arrow_star_ret_types##__const<MemberT>); \
526
+ \
527
+ template <typename MemberT> \
528
+ using ArrowStarRetType##__const = \
529
+ typename first_or_void<AllArrowStarRetTypes##__const<MemberT>>::type; \
530
+ \
531
+ template <typename MemberT> \
532
+ constexpr std::enable_if_t< \
533
+ all_arrow_star_ret_types_are_same##__const<MemberT>, \
534
+ typename ArrowStarRetType##__const<MemberT>::type> \
535
+ operator->*(const MemberT& member) __const { \
536
+ using RetT = typename ArrowStarRetType##__const<MemberT>::type; \
537
+ std::optional<wrap_reference_t<RetT>> ret = std::nullopt; \
538
+ for_all_types([this, &member, &ret](auto t) { \
539
+ using T = typename decltype(t)::type; \
540
+ if constexpr (opcheck<T>->*opcheck<MemberT>) { \
541
+ if (is<T>()) { \
542
+ ret = as<T>()->*member; \
543
+ } \
544
+ } \
545
+ }); \
546
+ DYNAMIC_TYPE_CHECK( \
547
+ ret.has_value(), \
548
+ "Cannot access member with type ", \
549
+ typeid(RetT).name(), \
550
+ " : incompatible type"); \
551
+ return ret.value(); \
552
+ }
553
+
554
+ DEFINE_ARROW_STAR_OPERATOR()
555
+ DEFINE_ARROW_STAR_OPERATOR(const)
556
+ #undef DEFINE_ARROW_STAR_OPERATOR
557
+
558
+ // TODO: support operator(). This is not supported yet because it is the most
559
+ // difficulty one to implement because it can has arbitrary number of
560
+ // arguments. I believe it is doable, but I decide to leave it for future.
561
+ };
562
+
563
+ template <typename T>
564
+ struct is_dynamic_type : std::false_type {};
565
+
566
+ template <typename... Ts>
567
+ struct is_dynamic_type<DynamicType<Ts...>> : std::true_type {};
568
+
569
+ template <typename T>
570
+ constexpr bool is_dynamic_type_v = is_dynamic_type<T>::value;
571
+
572
+ #define DEFINE_BINARY_OP(opname, op, func_name, return_type, check_existence) \
573
+ template <typename X, typename Y, typename RetT> \
574
+ constexpr bool opname##_type_compatible() { \
575
+ if constexpr (opcheck<X> op opcheck<Y>) { \
576
+ if constexpr (std::is_convertible_v< \
577
+ decltype(std::declval<X>() op std::declval<Y>()), \
578
+ RetT>) { \
579
+ return true; \
580
+ } \
581
+ } \
582
+ return false; \
583
+ } \
584
+ template <typename RetT> \
585
+ constexpr auto opname##_is_valid = [](auto&& x, auto&& y) { \
586
+ using X = decltype(x); \
587
+ using Y = decltype(y); \
588
+ if constexpr (opname##_type_compatible<X, Y, RetT>()) { \
589
+ return std::true_type{}; \
590
+ } else { \
591
+ return; \
592
+ } \
593
+ }; \
594
+ template <typename LHS, typename RHS> \
595
+ constexpr bool opname##_defined() { \
596
+ constexpr bool lhs_is_dt = is_dynamic_type_v<std::decay_t<LHS>>; \
597
+ constexpr bool rhs_is_dt = is_dynamic_type_v<std::decay_t<RHS>>; \
598
+ using DT = \
599
+ std::conditional_t<lhs_is_dt, std::decay_t<LHS>, std::decay_t<RHS>>; \
600
+ if constexpr (!lhs_is_dt && !rhs_is_dt) { \
601
+ return false; \
602
+ } else if constexpr ( \
603
+ (lhs_is_dt && !rhs_is_dt && \
604
+ opcheck<std::decay_t<RHS>>.hasExplicitCastTo( \
605
+ opcheck<std::decay_t<LHS>>)) || \
606
+ (!lhs_is_dt && rhs_is_dt && \
607
+ opcheck<std::decay_t<LHS>>.hasExplicitCastTo( \
608
+ opcheck<std::decay_t<RHS>>))) { \
609
+ return opname##_defined<DT, DT>(); \
610
+ } else { \
611
+ if constexpr (check_existence) { \
612
+ using should_define_t = decltype(DT::dispatch( \
613
+ opname##_is_valid<DT>, std::declval<LHS>(), std::declval<RHS>())); \
614
+ return std::is_same_v<should_define_t, std::true_type>; \
615
+ } else { \
616
+ return true; \
617
+ } \
618
+ } \
619
+ } \
620
+ template < \
621
+ typename LHS, \
622
+ typename RHS, \
623
+ typename DT = std::conditional_t< \
624
+ is_dynamic_type_v<std::decay_t<LHS>>, \
625
+ std::decay_t<LHS>, \
626
+ std::decay_t<RHS>>, \
627
+ typename = std::enable_if_t<opname##_defined<LHS, RHS>()>> \
628
+ inline constexpr return_type func_name(LHS&& x, RHS&& y) { \
629
+ constexpr bool lhs_is_dt = is_dynamic_type_v<std::decay_t<LHS>>; \
630
+ constexpr bool rhs_is_dt = is_dynamic_type_v<std::decay_t<RHS>>; \
631
+ if constexpr ( \
632
+ lhs_is_dt && !rhs_is_dt && \
633
+ opcheck<std::decay_t<RHS>>.hasExplicitCastTo( \
634
+ opcheck<std::decay_t<LHS>>)) { \
635
+ return x op(DT) y; \
636
+ } else if constexpr ( \
637
+ !lhs_is_dt && rhs_is_dt && \
638
+ opcheck<std::decay_t<LHS>>.hasExplicitCastTo( \
639
+ opcheck<std::decay_t<RHS>>)) { \
640
+ return (DT)x op y; \
641
+ } else { \
642
+ return DT::dispatch( \
643
+ [](auto&& x, auto&& y) -> decltype(auto) { \
644
+ using X = decltype(x); \
645
+ using Y = decltype(y); \
646
+ if constexpr (false) { \
647
+ /* TODO: This doesn't work on gcc 11.4 with C++20, temporarily \
648
+ * disabled and use the more verbose implementation below. We \
649
+ * should reenable this when we upgrade our compilers. */ \
650
+ if constexpr (opname##_type_compatible<X, Y, return_type>()) { \
651
+ return std::forward<X>(x) op std::forward<Y>(y); \
652
+ } \
653
+ } else { \
654
+ if constexpr (opcheck<X> op opcheck<Y>) { \
655
+ if constexpr (std::is_convertible_v< \
656
+ decltype(std::declval<X>() \
657
+ op std::declval<Y>()), \
658
+ return_type>) { \
659
+ return std::forward<X>(x) op std::forward<Y>(y); \
660
+ } \
661
+ } \
662
+ } \
663
+ }, \
664
+ std::forward<LHS>(x), \
665
+ std::forward<RHS>(y)); \
666
+ } \
667
+ }
668
+
669
+ DEFINE_BINARY_OP(add, +, operator+, DT, true);
670
+ DEFINE_BINARY_OP(minus, -, operator-, DT, true);
671
+ DEFINE_BINARY_OP(mul, *, operator*, DT, true);
672
+ DEFINE_BINARY_OP(div, /, operator/, DT, true);
673
+ DEFINE_BINARY_OP(mod, %, operator%, DT, true);
674
+ DEFINE_BINARY_OP(band, &, operator&, DT, true);
675
+ DEFINE_BINARY_OP(bor, |, operator|, DT, true);
676
+ DEFINE_BINARY_OP(xor, ^, operator^, DT, true);
677
+ DEFINE_BINARY_OP(land, &&, operator&&, DT, true);
678
+ DEFINE_BINARY_OP(lor, ||, operator||, DT, true);
679
+ DEFINE_BINARY_OP(lshift, <<, operator<<, DT, true);
680
+ DEFINE_BINARY_OP(rshift, >>, operator>>, DT, true);
681
+
682
+ // Not defining comparison operators that returns DynamicType as operator
683
+ // overloading, because we want to leave the operator overloading for comparison
684
+ // operators that returns bool. Instead, we give each operator a function name,
685
+ // so that users can use the function name to call the operator. That is:
686
+ // dt1 < dt2 --> returns a bool (defined below by DEFINE_COMPARE_OP)
687
+ // lt(dt1, dt2) --> returns a DynamicType
688
+ DEFINE_BINARY_OP(named_eq, ==, eq, DT, true);
689
+ DEFINE_BINARY_OP(named_neq, !=, ne, DT, true);
690
+ DEFINE_BINARY_OP(named_lt, <, lt, DT, true);
691
+ DEFINE_BINARY_OP(named_gt, >, gt, DT, true);
692
+ DEFINE_BINARY_OP(named_le, <=, le, DT, true);
693
+ DEFINE_BINARY_OP(named_ge, >=, ge, DT, true);
694
+
695
+ // std::monostate has definitions on compare operators, so DynamicType should
696
+ // always define them as well. There is no need for any SFINAE about member type
697
+ // here. https://en.cppreference.com/w/cpp/utility/variant/monostate
698
+ DEFINE_BINARY_OP(eq, ==, operator==, bool, false);
699
+ DEFINE_BINARY_OP(neq, !=, operator!=, bool, false);
700
+ DEFINE_BINARY_OP(lt, <, operator<, bool, false);
701
+ DEFINE_BINARY_OP(gt, >, operator>, bool, false);
702
+ DEFINE_BINARY_OP(le, <=, operator<=, bool, false);
703
+ DEFINE_BINARY_OP(ge, >=, operator>=, bool, false);
704
+
705
+ #undef DEFINE_BINARY_OP
706
+
707
+ #define DEFINE_UNARY_OP(opname, op) \
708
+ /*TODO: we should inline the definition of opname##_helper into enable_if,*/ \
709
+ /*but I can only do this in C++20 */ \
710
+ constexpr auto opname##_helper = [](auto x) constexpr { \
711
+ return (op opcheck<typename decltype(x)::type>); \
712
+ }; \
713
+ template < \
714
+ typename DT, \
715
+ typename = std::enable_if_t< \
716
+ is_dynamic_type_v<std::decay_t<DT>> && \
717
+ any_check( \
718
+ opname##_helper, std::decay_t<DT>::type_identities_as_tuple)>> \
719
+ inline constexpr decltype(auto) operator op(DT&& x) { \
720
+ return std::decay_t<DT>::dispatch( \
721
+ [](auto&& x) -> decltype(auto) { \
722
+ if constexpr (op opcheck<std::decay_t<decltype(x)>>) { \
723
+ return op std::forward<decltype(x)>(x); \
724
+ } \
725
+ }, \
726
+ std::forward<DT>(x)); \
727
+ }
728
+
729
+ DEFINE_UNARY_OP(pos, +);
730
+ DEFINE_UNARY_OP(neg, -);
731
+ DEFINE_UNARY_OP(bnot, ~);
732
+ DEFINE_UNARY_OP(lnot, !);
733
+ #undef DEFINE_UNARY_OP
734
+
735
+ // Intentionally not supporting the following unary ops:
736
+ // DEFINE_UNARY_OP(addr, &);
737
+ // Because it only makes sense if and only if both T& and T* are included in
738
+ // the type list, however, std::variant does not allow reference type to be
739
+ // an alternative. Also, if we overloaded the operator&, how can we get the
740
+ // address of the dynamic type itself?
741
+
742
+ template <typename DT>
743
+ auto star_defined_checker = [](auto t) {
744
+ using T = typename decltype(t)::type;
745
+ if constexpr (*opcheck<T>) {
746
+ return std::is_same_v<decltype(*std::declval<T>()), DT&>;
747
+ }
748
+ return false;
749
+ };
750
+
751
+ template <
752
+ typename DT,
753
+ typename = std::enable_if_t<
754
+ is_dynamic_type_v<DT> &&
755
+ any_check(star_defined_checker<DT>, DT::type_identities_as_tuple)>>
756
+ DT& operator*(const DT& x) {
757
+ std::optional<std::reference_wrapper<DT>> ret = std::nullopt;
758
+ DT::for_all_types([&ret, &x](auto t) {
759
+ using T = typename decltype(t)::type;
760
+ if constexpr (*opcheck<T>) {
761
+ if constexpr (std::is_same_v<decltype(*std::declval<T>()), DT&>) {
762
+ if (x.template is<T>()) {
763
+ ret = std::ref(*(x.template as<T>()));
764
+ }
765
+ }
766
+ }
767
+ });
768
+ DYNAMIC_TYPE_CHECK(ret.has_value(), "Cannot dereference ", x.type().name());
769
+ return ret.value();
770
+ }
771
+
772
+ // Printing
773
+ // TODO: we should inline the definition of can_print into enable_if, but I can
774
+ // only do this in C++20
775
+ constexpr auto can_print = [](auto x) constexpr {
776
+ using T = typename decltype(x)::type;
777
+ if constexpr (opcheck<std::ostream&> << opcheck<T>) {
778
+ return std::is_same_v<
779
+ decltype(std::declval<std::ostream&>() << std::declval<T>()),
780
+ std::ostream&>;
781
+ }
782
+ return false;
783
+ };
784
+ template <
785
+ typename DT,
786
+ typename = std::enable_if_t<
787
+ is_dynamic_type_v<DT> &&
788
+ any_check(can_print, DT::type_identities_as_tuple)>>
789
+ std::ostream& operator<<(std::ostream& os, const DT& dt) {
790
+ bool printed = false;
791
+ DT::for_all_types([&printed, &os, &dt](auto _) {
792
+ using T = typename decltype(_)::type;
793
+ if constexpr (opcheck<std::ostream&> << opcheck<T>) {
794
+ if constexpr (std::is_same_v<
795
+ decltype(os << std::declval<T>()),
796
+ std::ostream&>) {
797
+ if (dt.template is<T>()) {
798
+ os << dt.template as<T>();
799
+ printed = true;
800
+ }
801
+ }
802
+ }
803
+ });
804
+ DYNAMIC_TYPE_CHECK(
805
+ printed, "Can not print ", dt.type().name(), " : incompatible type");
806
+ return os;
807
+ }
808
+
809
+ #define DEFINE_LEFT_PPMM(opname, op) \
810
+ /*TODO: we should inline the definition of opname##_helper into enable_if,*/ \
811
+ /*but I can only do this in C++20 */ \
812
+ constexpr auto opname##_helper = [](auto x) constexpr { \
813
+ using X = typename decltype(x)::type; \
814
+ if constexpr (op opcheck<X&>) { \
815
+ return std::is_same_v<decltype(op std::declval<X&>()), X&>; \
816
+ } \
817
+ return false; \
818
+ }; \
819
+ template < \
820
+ typename DT, \
821
+ typename = std::enable_if_t< \
822
+ is_dynamic_type_v<DT> && \
823
+ any_check(opname##_helper, DT::type_identities_as_tuple)>> \
824
+ inline constexpr DT& operator op(DT & x) { \
825
+ bool computed = false; \
826
+ DT::for_all_types([&computed, &x](auto _) { \
827
+ using Type = typename decltype(_)::type; \
828
+ if constexpr (op opcheck<Type&>) { \
829
+ if constexpr (std::is_same_v< \
830
+ decltype(op std::declval<Type&>()), \
831
+ Type&>) { \
832
+ if (x.template is<Type>()) { \
833
+ op x.template as<Type>(); \
834
+ computed = true; \
835
+ } \
836
+ } \
837
+ } \
838
+ }); \
839
+ DYNAMIC_TYPE_CHECK( \
840
+ computed, \
841
+ "Cannot compute ", \
842
+ #op, \
843
+ x.type().name(), \
844
+ " : incompatible type"); \
845
+ return x; \
846
+ }
847
+
848
+ DEFINE_LEFT_PPMM(lpp, ++);
849
+ DEFINE_LEFT_PPMM(lmm, --);
850
+
851
+ #undef DEFINE_LEFT_PPMM
852
+
853
+ #define DEFINE_RIGHT_PPMM(opname, op) \
854
+ /*TODO: we should inline the definition of opname##_helper into enable_if,*/ \
855
+ /*but I can only do this in C++20 */ \
856
+ template <typename DTVariantType> \
857
+ constexpr auto opname##_helper = [](auto x) constexpr { \
858
+ using X = typename decltype(x)::type; \
859
+ if constexpr (opcheck<X&> op) { \
860
+ return std:: \
861
+ is_constructible_v<DTVariantType, decltype(std::declval<X&>() op)>; \
862
+ } \
863
+ return false; \
864
+ }; \
865
+ template <typename DT> \
866
+ inline constexpr std::enable_if_t< \
867
+ is_dynamic_type_v<DT> && \
868
+ any_check( \
869
+ opname##_helper<typename DT::VariantType>, \
870
+ DT::type_identities_as_tuple), \
871
+ DT> operator op(DT & x, int) { \
872
+ DT ret; \
873
+ DT::for_all_types([&ret, &x](auto _) { \
874
+ using Type = typename decltype(_)::type; \
875
+ if constexpr (opcheck<Type&> op) { \
876
+ if constexpr (std::is_constructible_v< \
877
+ typename DT::VariantType, \
878
+ decltype(std::declval<Type&>() op)>) { \
879
+ if (x.template is<Type>()) { \
880
+ ret = DT(x.template as<Type>() op); \
881
+ } \
882
+ } \
883
+ } \
884
+ }); \
885
+ DYNAMIC_TYPE_CHECK( \
886
+ !ret.template is<std::monostate>(), \
887
+ "Cannot compute ", \
888
+ x.type().name(), \
889
+ #op, \
890
+ " : incompatible type"); \
891
+ return ret; \
892
+ }
893
+
894
+ DEFINE_RIGHT_PPMM(rpp, ++);
895
+ DEFINE_RIGHT_PPMM(rmm, --);
896
+
897
+ #undef DEFINE_RIGHT_PPMM
898
+
899
+ #define DEFINE_ASSIGNMENT_OP(op, assign_op) \
900
+ template < \
901
+ typename DT, \
902
+ typename T, \
903
+ typename = std::enable_if_t< \
904
+ is_dynamic_type_v<DT> && (opcheck<DT> op opcheck<T>)>> \
905
+ inline constexpr DT& operator assign_op(DT & x, const T & y) { \
906
+ return x = x op y; \
907
+ }
908
+
909
+ DEFINE_ASSIGNMENT_OP(+, +=);
910
+ DEFINE_ASSIGNMENT_OP(-, -=);
911
+ DEFINE_ASSIGNMENT_OP(*, *=);
912
+ DEFINE_ASSIGNMENT_OP(/, /=);
913
+ DEFINE_ASSIGNMENT_OP(%, %=);
914
+ DEFINE_ASSIGNMENT_OP(&, &=);
915
+ DEFINE_ASSIGNMENT_OP(|, |=);
916
+ DEFINE_ASSIGNMENT_OP(^, ^=);
917
+ DEFINE_ASSIGNMENT_OP(<<, <<=);
918
+ DEFINE_ASSIGNMENT_OP(>>, >>=);
919
+
920
+ // Intentionally not overloading operator comma",". This operator is rarely
921
+ // overloaded, and the automatically defined version by the compiler usually
922
+ // does what we want.
923
+
924
+ // Check that, whether there exist two different types T and U, where both T and
925
+ // U are contained in the type list of dynamic type DT, and T == U is defined.
926
+ template <typename DT>
927
+ constexpr bool has_cross_type_equality =
928
+ any(remove_void_from_tuple(DT::for_all_types([](auto t) {
929
+ using T = typename decltype(t)::type;
930
+ return any(remove_void_from_tuple(DT::for_all_types([](auto u) {
931
+ using U = typename decltype(u)::type;
932
+ if constexpr (std::is_same_v<T, U>) {
933
+ return;
934
+ } else {
935
+ return opcheck<T> == opcheck<U>;
936
+ }
937
+ })));
938
+ })));
939
+
940
+ #if defined(__clang__)
941
+ #pragma clang diagnostic pop
942
+ #endif
943
+
944
+ #if defined(__GNUC__) && !defined(__clang__)
945
+ #pragma GCC diagnostic pop
946
+ #endif
947
+
948
+ } // namespace dynamic_type
949
+
950
+ // Hashing:
951
+
952
+ template <typename Containers, typename... Ts>
953
+ struct std::hash<dynamic_type::DynamicType<Containers, Ts...>> {
954
+ // The hashing should be consistent with the equality operator. That is, if
955
+ // a == b, then a and b should always has the same hash. However, because we
956
+ // are using the hashing function for std::variant as our hasing function,
957
+ // there is no way for us to guarantee this if there are cross-type
958
+ // equality. For example, 0 == 0.0, but they don't have the same hash value.
959
+ // So the hashing function for DynamicType<NoContainers, int, double> as
960
+ // defined here is illegal.
961
+ static_assert(
962
+ !dynamic_type::has_cross_type_equality<
963
+ dynamic_type::DynamicType<Containers, Ts...>>,
964
+ "Hash function of DynamicType can not be automatically defined while there are cross-type equality.");
965
+ using DT = dynamic_type::DynamicType<Containers, Ts...>;
966
+ std::size_t operator()(DT const& dt) const noexcept {
967
+ return std::hash<typename DT::VariantType>{}(dt.value);
968
+ }
969
+ };