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.
- nvfuser/_C.cpython-312-x86_64-linux-gnu.so +0 -0
- nvfuser/__init__.py +618 -0
- nvfuser/__init__.pyi +4 -0
- nvfuser/contrib/__init__.py +9 -0
- nvfuser/contrib/nn/__init__.py +13 -0
- nvfuser/contrib/nn/normalization.py +725 -0
- nvfuser/include/nvfuser/alias_analysis.h +116 -0
- nvfuser/include/nvfuser/bfs.h +929 -0
- nvfuser/include/nvfuser/codegen.h +26 -0
- nvfuser/include/nvfuser/compute_at.h +28 -0
- nvfuser/include/nvfuser/compute_at_map.h +394 -0
- nvfuser/include/nvfuser/contiguity.h +351 -0
- nvfuser/include/nvfuser/cuda_utils.h +50 -0
- nvfuser/include/nvfuser/debug.h +50 -0
- nvfuser/include/nvfuser/device_lower/analysis/bank_conflict.h +53 -0
- nvfuser/include/nvfuser/device_lower/analysis/circular_buffer.h +109 -0
- nvfuser/include/nvfuser/device_lower/analysis/device_version.h +65 -0
- nvfuser/include/nvfuser/device_lower/analysis/divisible_split.h +28 -0
- nvfuser/include/nvfuser/device_lower/analysis/fused_reduction.h +36 -0
- nvfuser/include/nvfuser/device_lower/analysis/index_compute.h +322 -0
- nvfuser/include/nvfuser/device_lower/analysis/predicate_elimination.h +71 -0
- nvfuser/include/nvfuser/device_lower/analysis/sync_information.h +47 -0
- nvfuser/include/nvfuser/device_lower/analysis/tensor_memory.h +65 -0
- nvfuser/include/nvfuser/device_lower/analysis/thread_predicate.h +158 -0
- nvfuser/include/nvfuser/device_lower/analysis/tma.h +93 -0
- nvfuser/include/nvfuser/device_lower/analysis/trivial_broadcast.h +75 -0
- nvfuser/include/nvfuser/device_lower/id_model_options.h +135 -0
- nvfuser/include/nvfuser/device_lower/lower2device.h +391 -0
- nvfuser/include/nvfuser/device_lower/pass/alias_memory.h +37 -0
- nvfuser/include/nvfuser/device_lower/pass/allocation.h +32 -0
- nvfuser/include/nvfuser/device_lower/pass/circular_buffer.h +191 -0
- nvfuser/include/nvfuser/device_lower/pass/expr_sort.h +17 -0
- nvfuser/include/nvfuser/device_lower/pass/fusion_simplifier.h +21 -0
- nvfuser/include/nvfuser/device_lower/pass/grid_serialization.h +26 -0
- nvfuser/include/nvfuser/device_lower/pass/index.h +200 -0
- nvfuser/include/nvfuser/device_lower/pass/inline_ptx.h +16 -0
- nvfuser/include/nvfuser/device_lower/pass/insert_syncs.h +39 -0
- nvfuser/include/nvfuser/device_lower/pass/instrument.h +24 -0
- nvfuser/include/nvfuser/device_lower/pass/loop_rotation.h +150 -0
- nvfuser/include/nvfuser/device_lower/pass/loops.h +68 -0
- nvfuser/include/nvfuser/device_lower/pass/magic_zero.h +86 -0
- nvfuser/include/nvfuser/device_lower/pass/misaligned_vectorization.h +118 -0
- nvfuser/include/nvfuser/device_lower/pass/predicate.h +23 -0
- nvfuser/include/nvfuser/device_lower/pass/replace_size.h +24 -0
- nvfuser/include/nvfuser/device_lower/pass/scalar_hoist.h +115 -0
- nvfuser/include/nvfuser/device_lower/pass/unroll.h +98 -0
- nvfuser/include/nvfuser/device_lower/pass/vectorize_welford.h +45 -0
- nvfuser/include/nvfuser/device_lower/pass/warp_reduce.h +23 -0
- nvfuser/include/nvfuser/device_lower/utils.h +382 -0
- nvfuser/include/nvfuser/device_lower/validation.h +74 -0
- nvfuser/include/nvfuser/disjoint_set.h +556 -0
- nvfuser/include/nvfuser/dispatch.h +334 -0
- nvfuser/include/nvfuser/driver_api.h +49 -0
- nvfuser/include/nvfuser/dynamic_transform.h +316 -0
- nvfuser/include/nvfuser/dynamic_type/C++20/type_traits +37 -0
- nvfuser/include/nvfuser/dynamic_type/dynamic_type.h +969 -0
- nvfuser/include/nvfuser/dynamic_type/error.h +24 -0
- nvfuser/include/nvfuser/dynamic_type/type_traits.h +703 -0
- nvfuser/include/nvfuser/evaluator_common.h +295 -0
- nvfuser/include/nvfuser/exceptions.h +283 -0
- nvfuser/include/nvfuser/expr_evaluator.h +125 -0
- nvfuser/include/nvfuser/expr_simplifier.h +218 -0
- nvfuser/include/nvfuser/flatbuffers/allocator.h +68 -0
- nvfuser/include/nvfuser/flatbuffers/array.h +253 -0
- nvfuser/include/nvfuser/flatbuffers/base.h +486 -0
- nvfuser/include/nvfuser/flatbuffers/buffer.h +154 -0
- nvfuser/include/nvfuser/flatbuffers/buffer_ref.h +53 -0
- nvfuser/include/nvfuser/flatbuffers/code_generator.h +80 -0
- nvfuser/include/nvfuser/flatbuffers/code_generators.h +234 -0
- nvfuser/include/nvfuser/flatbuffers/default_allocator.h +64 -0
- nvfuser/include/nvfuser/flatbuffers/detached_buffer.h +114 -0
- nvfuser/include/nvfuser/flatbuffers/flatbuffer_builder.h +1225 -0
- nvfuser/include/nvfuser/flatbuffers/flatbuffers.h +272 -0
- nvfuser/include/nvfuser/flatbuffers/flatc.h +130 -0
- nvfuser/include/nvfuser/flatbuffers/flex_flat_util.h +36 -0
- nvfuser/include/nvfuser/flatbuffers/flexbuffers.h +1889 -0
- nvfuser/include/nvfuser/flatbuffers/grpc.h +300 -0
- nvfuser/include/nvfuser/flatbuffers/hash.h +127 -0
- nvfuser/include/nvfuser/flatbuffers/idl.h +1359 -0
- nvfuser/include/nvfuser/flatbuffers/minireflect.h +420 -0
- nvfuser/include/nvfuser/flatbuffers/reflection.h +522 -0
- nvfuser/include/nvfuser/flatbuffers/reflection_generated.h +1471 -0
- nvfuser/include/nvfuser/flatbuffers/registry.h +128 -0
- nvfuser/include/nvfuser/flatbuffers/stl_emulation.h +513 -0
- nvfuser/include/nvfuser/flatbuffers/string.h +64 -0
- nvfuser/include/nvfuser/flatbuffers/struct.h +53 -0
- nvfuser/include/nvfuser/flatbuffers/table.h +168 -0
- nvfuser/include/nvfuser/flatbuffers/util.h +731 -0
- nvfuser/include/nvfuser/flatbuffers/vector.h +393 -0
- nvfuser/include/nvfuser/flatbuffers/vector_downward.h +273 -0
- nvfuser/include/nvfuser/flatbuffers/verifier.h +317 -0
- nvfuser/include/nvfuser/fusion.h +511 -0
- nvfuser/include/nvfuser/fusion_guard.h +37 -0
- nvfuser/include/nvfuser/fusion_profiler.h +311 -0
- nvfuser/include/nvfuser/fusion_segmenter.h +751 -0
- nvfuser/include/nvfuser/global_allocator.h +27 -0
- nvfuser/include/nvfuser/grouped_reduction.h +47 -0
- nvfuser/include/nvfuser/host_ir/container.h +60 -0
- nvfuser/include/nvfuser/host_ir/executor.h +152 -0
- nvfuser/include/nvfuser/host_ir/host_ir.h +320 -0
- nvfuser/include/nvfuser/host_ir/lower.h +35 -0
- nvfuser/include/nvfuser/id_model/circular_buffer_indexing.h +56 -0
- nvfuser/include/nvfuser/id_model/contiguity.h +166 -0
- nvfuser/include/nvfuser/id_model/id_model.h +359 -0
- nvfuser/include/nvfuser/id_model/id_model_index_compute.h +81 -0
- nvfuser/include/nvfuser/id_model/indexing.h +208 -0
- nvfuser/include/nvfuser/id_model/indexing_traversal.h +72 -0
- nvfuser/include/nvfuser/id_model/indexing_utils.h +62 -0
- nvfuser/include/nvfuser/id_model/loop_promotion.h +180 -0
- nvfuser/include/nvfuser/id_model/predicate_indexing.h +104 -0
- nvfuser/include/nvfuser/id_model/schedule.h +54 -0
- nvfuser/include/nvfuser/id_model/to_string.h +87 -0
- nvfuser/include/nvfuser/id_model/transform_replay.h +58 -0
- nvfuser/include/nvfuser/id_model/utils.h +176 -0
- nvfuser/include/nvfuser/id_model/validation_utils.h +55 -0
- nvfuser/include/nvfuser/index_compute.h +651 -0
- nvfuser/include/nvfuser/instrumentation.h +107 -0
- nvfuser/include/nvfuser/ir/all_nodes.h +14 -0
- nvfuser/include/nvfuser/ir/base_nodes.h +687 -0
- nvfuser/include/nvfuser/ir/builder.h +215 -0
- nvfuser/include/nvfuser/ir/builder_passkey.h +29 -0
- nvfuser/include/nvfuser/ir/cloner.h +185 -0
- nvfuser/include/nvfuser/ir/container.h +226 -0
- nvfuser/include/nvfuser/ir/graphviz.h +119 -0
- nvfuser/include/nvfuser/ir/interface_nodes.h +957 -0
- nvfuser/include/nvfuser/ir/internal_base_nodes.h +744 -0
- nvfuser/include/nvfuser/ir/internal_nodes.h +2792 -0
- nvfuser/include/nvfuser/ir/iostream.h +98 -0
- nvfuser/include/nvfuser/ir/printer.h +57 -0
- nvfuser/include/nvfuser/ir/utils.h +801 -0
- nvfuser/include/nvfuser/iter_visitor.h +661 -0
- nvfuser/include/nvfuser/kernel.h +299 -0
- nvfuser/include/nvfuser/kernel_db/kernel_db.h +109 -0
- nvfuser/include/nvfuser/kernel_db/utils.h +37 -0
- nvfuser/include/nvfuser/kernel_ir.h +1457 -0
- nvfuser/include/nvfuser/kernel_ir_dispatch.h +147 -0
- nvfuser/include/nvfuser/linked_hash_map.h +97 -0
- nvfuser/include/nvfuser/logical_domain_map.h +577 -0
- nvfuser/include/nvfuser/macros.h +23 -0
- nvfuser/include/nvfuser/mma_type.h +257 -0
- nvfuser/include/nvfuser/multidevice/c10d_mock.h +175 -0
- nvfuser/include/nvfuser/multidevice/communication.h +232 -0
- nvfuser/include/nvfuser/multidevice/communicator.h +179 -0
- nvfuser/include/nvfuser/multidevice/device_mesh.h +95 -0
- nvfuser/include/nvfuser/multidevice/executor.h +107 -0
- nvfuser/include/nvfuser/multidevice/multidevice.h +18 -0
- nvfuser/include/nvfuser/multidevice/utils.h +187 -0
- nvfuser/include/nvfuser/non_divisible_split.h +86 -0
- nvfuser/include/nvfuser/opaque_type.h +129 -0
- nvfuser/include/nvfuser/ops/alias.h +192 -0
- nvfuser/include/nvfuser/ops/all_ops.h +13 -0
- nvfuser/include/nvfuser/ops/arith.h +712 -0
- nvfuser/include/nvfuser/ops/composite.h +130 -0
- nvfuser/include/nvfuser/ops/indexing.h +55 -0
- nvfuser/include/nvfuser/ops/normalization.h +263 -0
- nvfuser/include/nvfuser/ops/utils.h +127 -0
- nvfuser/include/nvfuser/options.h +313 -0
- nvfuser/include/nvfuser/parallel_dimension_map.h +95 -0
- nvfuser/include/nvfuser/parallel_type_bitmap.h +365 -0
- nvfuser/include/nvfuser/polymorphic_value.h +432 -0
- nvfuser/include/nvfuser/predicate_compute.h +213 -0
- nvfuser/include/nvfuser/python_frontend/distributed_tensor.h +50 -0
- nvfuser/include/nvfuser/python_frontend/fusion_cache.h +298 -0
- nvfuser/include/nvfuser/python_frontend/fusion_definition.h +372 -0
- nvfuser/include/nvfuser/python_frontend/fusion_record.h +3124 -0
- nvfuser/include/nvfuser/python_frontend/fusion_state.h +143 -0
- nvfuser/include/nvfuser/python_frontend/python_bindings.h +27 -0
- nvfuser/include/nvfuser/python_frontend/segmentation.h +246 -0
- nvfuser/include/nvfuser/python_frontend/translation.h +20 -0
- nvfuser/include/nvfuser/python_frontend/translation_utils.h +308 -0
- nvfuser/include/nvfuser/scheduler/all_schedulers.h +17 -0
- nvfuser/include/nvfuser/scheduler/ampere_multi_matmul.h +206 -0
- nvfuser/include/nvfuser/scheduler/cache_policy_refiner.h +19 -0
- nvfuser/include/nvfuser/scheduler/compile_time_info.h +322 -0
- nvfuser/include/nvfuser/scheduler/debug_utils.h +68 -0
- nvfuser/include/nvfuser/scheduler/expr_eval_sched.h +45 -0
- nvfuser/include/nvfuser/scheduler/heuristic.h +113 -0
- nvfuser/include/nvfuser/scheduler/hopper_multi_matmul.h +204 -0
- nvfuser/include/nvfuser/scheduler/mark_aliases.h +19 -0
- nvfuser/include/nvfuser/scheduler/matmul.h +40 -0
- nvfuser/include/nvfuser/scheduler/matmul_heuristic.h +293 -0
- nvfuser/include/nvfuser/scheduler/matmul_heuristic_plugin.h +65 -0
- nvfuser/include/nvfuser/scheduler/matmul_heuristic_plugin_api.h +99 -0
- nvfuser/include/nvfuser/scheduler/matmul_utils.h +54 -0
- nvfuser/include/nvfuser/scheduler/mma_utils.h +500 -0
- nvfuser/include/nvfuser/scheduler/multi_matmul.h +74 -0
- nvfuser/include/nvfuser/scheduler/no_op.h +48 -0
- nvfuser/include/nvfuser/scheduler/normalization_inner.h +49 -0
- nvfuser/include/nvfuser/scheduler/normalization_inner_outer.h +51 -0
- nvfuser/include/nvfuser/scheduler/normalization_outer.h +48 -0
- nvfuser/include/nvfuser/scheduler/normalization_utils.h +379 -0
- nvfuser/include/nvfuser/scheduler/pointwise.h +183 -0
- nvfuser/include/nvfuser/scheduler/pointwise_heuristic.h +118 -0
- nvfuser/include/nvfuser/scheduler/pointwise_utils.h +24 -0
- nvfuser/include/nvfuser/scheduler/reduction.h +43 -0
- nvfuser/include/nvfuser/scheduler/reduction_heuristic.h +339 -0
- nvfuser/include/nvfuser/scheduler/reduction_utils.h +159 -0
- nvfuser/include/nvfuser/scheduler/registry.h +97 -0
- nvfuser/include/nvfuser/scheduler/registry_utils.h +111 -0
- nvfuser/include/nvfuser/scheduler/resize.h +41 -0
- nvfuser/include/nvfuser/scheduler/resize_heuristic.h +67 -0
- nvfuser/include/nvfuser/scheduler/runtime_info.h +166 -0
- nvfuser/include/nvfuser/scheduler/scheduler_types.h +80 -0
- nvfuser/include/nvfuser/scheduler/transpose.h +114 -0
- nvfuser/include/nvfuser/scheduler/transpose_heuristic.h +164 -0
- nvfuser/include/nvfuser/scheduler/utils.h +771 -0
- nvfuser/include/nvfuser/scheduler/vectorize_helper.h +349 -0
- nvfuser/include/nvfuser/serde/factory.h +55 -0
- nvfuser/include/nvfuser/serde/fusion_cache_generated.h +4319 -0
- nvfuser/include/nvfuser/serde/fusion_record.h +124 -0
- nvfuser/include/nvfuser/serde/polymorphic_value.h +52 -0
- nvfuser/include/nvfuser/serde/utils.h +34 -0
- nvfuser/include/nvfuser/struct.inl +127 -0
- nvfuser/include/nvfuser/swizzle.h +54 -0
- nvfuser/include/nvfuser/sys_utils.h +40 -0
- nvfuser/include/nvfuser/tensor_metadata.h +118 -0
- nvfuser/include/nvfuser/tma.h +124 -0
- nvfuser/include/nvfuser/transform_iter.h +522 -0
- nvfuser/include/nvfuser/transform_replay.h +297 -0
- nvfuser/include/nvfuser/transform_rfactor.h +33 -0
- nvfuser/include/nvfuser/transform_view.h +136 -0
- nvfuser/include/nvfuser/type.h +1125 -0
- nvfuser/include/nvfuser/type_promotion.h +61 -0
- nvfuser/include/nvfuser/utils.h +619 -0
- nvfuser/include/nvfuser/val_graph.h +446 -0
- nvfuser/include/nvfuser/val_graph_visitor.h +259 -0
- nvfuser/include/nvfuser/validator_utils.h +92 -0
- nvfuser/include/nvfuser/vectorization_info.h +31 -0
- nvfuser/include/nvfuser/visibility.h +21 -0
- nvfuser/lib/libnvfuser_codegen.so +0 -0
- nvfuser/nvfuser_version.py +69 -0
- nvfuser/pytorch_utils.py +184 -0
- nvfuser/share/cmake/nvfuser/NvfuserConfig-release.cmake +20 -0
- nvfuser/share/cmake/nvfuser/NvfuserConfig.cmake +106 -0
- nvfuser/utils.py +18 -0
- nvfuser/version.py +1 -0
- nvfuser_cu121_torch25-0.2.25.dev20250201.dist-info/LICENSE +976 -0
- nvfuser_cu121_torch25-0.2.25.dev20250201.dist-info/METADATA +16 -0
- nvfuser_cu121_torch25-0.2.25.dev20250201.dist-info/RECORD +242 -0
- nvfuser_cu121_torch25-0.2.25.dev20250201.dist-info/WHEEL +5 -0
- nvfuser_cu121_torch25-0.2.25.dev20250201.dist-info/top_level.txt +1 -0
- 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
|
+
};
|