28#include <boost/algorithm/string/join.hpp>
54inline std::string simplify_symbol_variant(
Variant const &v);
57template <
typename T>
auto simplify_symbol(T
const *) {
58 auto constexpr is_string = std::is_same_v<T, std::string>;
59 auto const symbol_for_variant = Utils::demangle<Variant>();
60 auto const name_for_variant = std::string(
"ScriptInterface::Variant");
61 auto name = (is_string) ? std::string{
"std::string"} : Utils::demangle<T>();
62 for (std::string::size_type pos{};
63 (pos = name.find(symbol_for_variant, pos)) != name.npos;
64 pos += name_for_variant.length()) {
65 name.replace(pos, symbol_for_variant.length(), name_for_variant);
71template <
typename T, std::
size_t N>
73 auto const name_val = simplify_symbol(
static_cast<T *
>(
nullptr));
74 return "Utils::Vector<" + Utils::demangle<T>() +
", " + std::to_string(N) +
79template <
typename T>
auto simplify_symbol(std::vector<T>
const *vec) {
80 auto const name_val = simplify_symbol(
static_cast<T *
>(
nullptr));
81 std::string metadata{
""};
83 metadata +=
"{.size=" + std::to_string(vec->size()) +
"}";
85 return "std::vector<" + name_val +
">" + metadata;
89inline auto simplify_symbol(std::vector<Variant>
const *vec) {
90 auto value_type_name = std::string(
"ScriptInterface::Variant");
91 std::string metadata{
""};
93 std::set<std::string> types = {};
94 for (
auto const &v : *vec) {
95 types.insert(simplify_symbol_variant(v));
97 value_type_name +=
"{" + boost::algorithm::join(types,
", ") +
"}";
98 metadata +=
"{.size=" + std::to_string(vec->size()) +
"}";
100 return "std::vector<" + value_type_name +
">" + metadata;
104template <
typename K,
typename V>
105auto simplify_symbol(std::unordered_map<K, V>
const *) {
106 auto const name_key = simplify_symbol(
static_cast<K *
>(
nullptr));
107 auto const name_val = simplify_symbol(
static_cast<V *
>(
nullptr));
108 return "std::unordered_map<" + name_key +
", " + name_val +
">";
113auto simplify_symbol(std::unordered_map<K, Variant>
const *map) {
114 auto const name_key = simplify_symbol(
static_cast<K *
>(
nullptr));
115 auto value_type_name = std::string(
"ScriptInterface::Variant");
117 std::set<std::string> types = {};
118 for (
auto const &variant :
std::views::elements<1>(*map)) {
119 types.insert(simplify_symbol_variant(variant));
121 value_type_name +=
"{" + boost::algorithm::join(types,
", ") +
"}";
123 return "std::unordered_map<" + name_key +
", " + value_type_name +
">";
126struct simplify_symbol_visitor {
127 template <
class T> std::string operator()(T
const &t)
const {
128 return simplify_symbol(&t);
133inline std::string simplify_symbol_variant(
Variant const &v) {
134 return std::visit(simplify_symbol_visitor(), v);
138template <
typename T>
auto simplify_symbol_containee(T
const *) {
139 return std::string(
"");
143template <
typename T>
auto simplify_symbol_containee(std::vector<T>
const *) {
144 auto const name_val = simplify_symbol(
static_cast<T *
>(
nullptr));
149template <
typename K,
typename V>
150auto simplify_symbol_containee(std::unordered_map<K, V>
const *) {
151 auto const name_key = simplify_symbol(
static_cast<K *
>(
nullptr));
152 auto const name_val = simplify_symbol(
static_cast<V *
>(
nullptr));
153 return name_key +
"' or '" + name_val;
156struct simplify_symbol_containee_visitor {
157 template <
class T> std::string operator()(
const T &)
const {
158 return simplify_symbol_containee(
static_cast<T *
>(
nullptr));
166inline auto simplify_symbol_containee_variant(
Variant const &v) {
167 return std::visit(simplify_symbol_containee_visitor(), v);
178template <
class To,
class From>
179using allow_conversion =
180 std::integral_constant<bool, std::is_same_v<To, From> ||
181 (std::is_convertible_v<To, From> &&
182 std::is_floating_point_v<To> &&
183 std::is_arithmetic_v<From>)>;
185template <
class To>
struct conversion_visitor {
186 template <
class From> To operator()(
const From &value)
const {
187 if constexpr (allow_conversion<To, From>::value) {
190 throw std::bad_variant_access{};
200template <
typename T>
struct get_value_helper {
201 T operator()(
Variant const &v)
const {
202 return std::visit(detail::conversion_visitor<T>(), v);
206template <
class T, std::
size_t N>
struct vector_conversion_visitor {
209 throw std::bad_variant_access{};
212 template <
typename U>
213 requires allow_conversion<T, U>::value
218 template <
typename U>
219 requires(std::is_same_v<U, Variant> or allow_conversion<T, U>::value)
222 throw std::bad_variant_access{};
224 if constexpr (std::is_same_v<U, Variant>) {
226 std::ranges::transform(vector, ret.begin(), get_value_helper<T>{});
235template <
typename T, std::
size_t N>
236struct get_value_helper<
Utils::Vector<T, N>> {
238 return std::visit(detail::vector_conversion_visitor<T, N>(), v);
242template <
typename T>
struct VisitorVector {
244 template <
typename U> std::vector<T> operator()(U
const &)
const {
245 throw std::bad_variant_access{};
249 std::vector<T> operator()(std::vector<T>
const &v)
const {
return v; }
251 std::vector<T> operator()(std::vector<Variant>
const &vv)
const
252 requires(not std::is_same_v<T, Variant>)
254 std::vector<T> ret(vv.size());
256 std::ranges::transform(vv, ret.begin(), get_value_helper<T>{});
261 template <
typename U, std::
size_t N>
262 requires allow_conversion<T, U>::value
269template <
typename T>
struct get_value_helper<
std::
vector<T>> {
270 std::vector<T> operator()(
Variant const &v)
const {
271 return std::visit(VisitorVector<T>(), v);
275template <
typename K,
typename T>
struct VisitorMap {
277 template <
typename U> std::unordered_map<K, T> operator()(U
const &)
const {
278 throw std::bad_variant_access{};
282 auto operator()(std::unordered_map<K, T>
const &map)
const {
return map; }
284 auto operator()(std::unordered_map<K, Variant>
const &map)
const
285 requires(not std::is_same_v<T, Variant>)
287 std::unordered_map<K, T> ret;
288 for (
auto const &[key, variant] : map) {
289 ret.emplace(key, get_value_helper<T>{}(variant));
296template <
typename T>
struct get_value_helper<
std::unordered_map<int, T>> {
297 std::unordered_map<int, T> operator()(
Variant const &v)
const {
298 return std::visit(VisitorMap<int, T>(), v);
302struct get_value_helper<
std::unordered_map<std::string, T>> {
303 std::unordered_map<std::string, T> operator()(
Variant const &v)
const {
304 return std::visit(VisitorMap<std::string, T>(), v);
309template <>
struct get_value_helper<
std::filesystem::path> {
310 auto operator()(
Variant const &v)
const {
311 if (
auto const *source = std::get_if<std::string>(&v)) {
312 return std::filesystem::path(*source);
314 return std::get<std::filesystem::path>(v);
319class bad_get_nullptr :
public std::bad_variant_access {};
326 requires(std::is_base_of_v<ObjectHandle, T>)
327struct get_value_helper<std::shared_ptr<T>> {
328 std::shared_ptr<T> operator()(
Variant const &v)
const {
329 auto so_ptr = std::get<ObjectRef>(v);
331 throw bad_get_nullptr{};
334 if (
auto t_ptr = std::dynamic_pointer_cast<T>(so_ptr)) {
338 throw std::bad_variant_access{};
350inline void handle_bad_get(
Variant const &v, std::string
const &name) {
351 auto const container_name = demangle::simplify_symbol_variant(v);
352 auto const containee_name = demangle::simplify_symbol_containee_variant(v);
353 auto const expected_containee_name =
354 demangle::simplify_symbol_containee(
static_cast<T *
>(
nullptr));
355 auto const from_container = !containee_name.empty();
356 auto const to_container = !expected_containee_name.empty();
357 auto what =
"Provided argument of type '" + container_name +
"'";
358 if (not name.empty()) {
359 what +=
" for parameter '" + name +
"'";
363 }
catch (bad_get_nullptr
const &) {
364 auto const item_error = (to_container) ?
" contains a value that" :
"";
365 throw Exception(what + item_error +
" is a null pointer");
366 }
catch (std::bad_variant_access
const &) {
367 auto const non_convertible = std::string(
" is not convertible to ");
368 auto item_error = std::string(
"");
369 if (from_container and to_container) {
370 item_error +=
" because it contains a value that";
371 item_error += non_convertible +
"'" + expected_containee_name +
"'";
373 auto const target = demangle::simplify_symbol(
static_cast<T *
>(
nullptr));
374 throw Exception(what + non_convertible +
"'" + target +
"'" + item_error);
378template <
typename T> T get_value(
Variant const &v, std::string
const &name) {
380 return detail::get_value_helper<T>{}(v);
382 detail::handle_bad_get<T>(v, name);
399 return detail::get_value<T>(v,
"");
410 throw Exception(
"Parameter '" + name +
"' is missing.");
412 return detail::get_value<T>(
vals.at(name), name);
422 if (
vals.contains(name)) {
432template <
typename T,
typename...
Types,
typename...
ArgNames>
435 return std::make_shared<T>(
DEVICE_QUALIFIER constexpr iterator begin() noexcept
DEVICE_QUALIFIER constexpr iterator end() noexcept
T get_value(Variant const &v)
Extract value of specific type T from a Variant.
std::unordered_map< std::string, Variant > VariantMap
T get_value_or(VariantMap const &vals, std::string const &name, T const &default_)
Get a value from a VariantMap by name, or return a default value if it does not exist.
std::shared_ptr< T > make_shared_from_args(VariantMap const &vals, ArgNames &&...args)
Make a new std::shared_ptr<T> with arguments extracted from a VariantMap.
make_recursive_variant< ObjectRef > Variant
Possible types for parameters.
void set_from_args(T &dst, VariantMap const &vals, const char *name)
std::string demangle()
Get a human-readable name for a type.
std::vector< T, allocator< T > > vector
Recursive variant implementation.