20#ifndef SCRIPT_INTERFACE_GET_VALUE_HPP
21#define SCRIPT_INTERFACE_GET_VALUE_HPP
29#include <boost/algorithm/string/join.hpp>
53inline std::string simplify_symbol_variant(
Variant const &v);
56template <
typename T>
auto simplify_symbol(T
const *) {
57 auto constexpr is_string = std::is_same_v<T, std::string>;
58 auto const symbol_for_variant = Utils::demangle<Variant>();
59 auto const name_for_variant = std::string(
"ScriptInterface::Variant");
60 auto name = (is_string) ? std::string{
"std::string"} : Utils::demangle<T>();
61 for (std::string::size_type pos{};
62 (pos = name.find(symbol_for_variant, pos)) != name.npos;
63 pos += name_for_variant.length()) {
64 name.replace(pos, symbol_for_variant.length(), name_for_variant);
70template <
typename T, std::
size_t N>
72 auto const name_val = simplify_symbol(
static_cast<T *
>(
nullptr));
73 return "Utils::Vector<" + Utils::demangle<T>() +
", " + std::to_string(N) +
78template <
typename T>
auto simplify_symbol(std::vector<T>
const *vec) {
79 auto const name_val = simplify_symbol(
static_cast<T *
>(
nullptr));
80 std::string metadata{
""};
82 metadata +=
"{.size=" + std::to_string(vec->size()) +
"}";
84 return "std::vector<" + name_val +
">" + metadata;
88inline auto simplify_symbol(std::vector<Variant>
const *vec) {
89 auto value_type_name = std::string(
"ScriptInterface::Variant");
90 std::string metadata{
""};
92 std::set<std::string> types = {};
93 for (
auto const &v : *vec) {
94 types.insert(simplify_symbol_variant(v));
96 value_type_name +=
"{" + boost::algorithm::join(types,
", ") +
"}";
97 metadata +=
"{.size=" + std::to_string(vec->size()) +
"}";
99 return "std::vector<" + value_type_name +
">" + metadata;
103template <
typename K,
typename V>
104auto simplify_symbol(std::unordered_map<K, V>
const *) {
105 auto const name_key = simplify_symbol(
static_cast<K *
>(
nullptr));
106 auto const name_val = simplify_symbol(
static_cast<V *
>(
nullptr));
107 return "std::unordered_map<" + name_key +
", " + name_val +
">";
112auto simplify_symbol(std::unordered_map<K, Variant>
const *map) {
113 auto const name_key = simplify_symbol(
static_cast<K *
>(
nullptr));
114 auto value_type_name = std::string(
"ScriptInterface::Variant");
116 std::set<std::string> types = {};
117 for (
auto const &kv : *map) {
118 types.insert(simplify_symbol_variant(kv.second));
120 value_type_name +=
"{" + boost::algorithm::join(types,
", ") +
"}";
122 return "std::unordered_map<" + name_key +
", " + value_type_name +
">";
125struct simplify_symbol_visitor : boost::static_visitor<std::string> {
126 template <
class T> std::string operator()(T
const &t)
const {
127 return simplify_symbol(&t);
132inline std::string simplify_symbol_variant(
Variant const &v) {
133 return boost::apply_visitor(simplify_symbol_visitor(), v);
137template <
typename T>
auto simplify_symbol_containee(T
const *) {
138 return std::string(
"");
142template <
typename T>
auto simplify_symbol_containee(std::vector<T>
const *) {
143 auto const name_val = simplify_symbol(
static_cast<T *
>(
nullptr));
148template <
typename K,
typename V>
149auto simplify_symbol_containee(std::unordered_map<K, V>
const *) {
150 auto const name_key = simplify_symbol(
static_cast<K *
>(
nullptr));
151 auto const name_val = simplify_symbol(
static_cast<V *
>(
nullptr));
152 return name_key +
"' or '" + name_val;
155struct simplify_symbol_containee_visitor : boost::static_visitor<std::string> {
156 template <
class T> std::string operator()(
const T &)
const {
157 return simplify_symbol_containee(
static_cast<T *
>(
nullptr));
165inline auto simplify_symbol_containee_variant(
Variant const &v) {
166 return boost::apply_visitor(simplify_symbol_containee_visitor(), v);
177template <
class To,
class From>
178using allow_conversion =
179 std::integral_constant<bool, std::is_same_v<To, From> ||
180 (std::is_convertible_v<To, From> &&
181 std::is_floating_point_v<To> &&
182 std::is_arithmetic_v<From>)>;
184template <
class To>
struct conversion_visitor : boost::static_visitor<To> {
185 template <
class From>
186 std::enable_if_t<allow_conversion<To, From>::value, To>
187 operator()(
const From &value)
const {
191 template <
class From>
192 std::enable_if_t<!allow_conversion<To, From>::value, To>
193 operator()(
const From &)
const {
194 throw boost::bad_get{};
204template <
typename T,
typename =
void>
struct get_value_helper {
205 T operator()(
Variant const &v)
const {
206 return boost::apply_visitor(detail::conversion_visitor<T>(), v);
210template <
class T, std::
size_t N>
211struct vector_conversion_visitor : boost::static_visitor<Utils::Vector<T, N>> {
218 auto operator()(std::vector<Variant>
const &vv)
const {
219 if (N != vv.size()) {
220 throw boost::bad_get{};
224 std::ranges::transform(vv, ret.begin(), get_value_helper<T>{});
229 template <
typename U>
231 operator()(std::vector<U, std::allocator<U>>
const &v)
const {
233 throw boost::bad_get{};
239 throw boost::bad_get{};
244template <
typename T, std::
size_t N>
245struct get_value_helper<
Utils::Vector<T, N>> {
247 return boost::apply_visitor(detail::vector_conversion_visitor<T, N>(), v);
252struct GetVectorOrEmpty : boost::static_visitor<std::vector<T>> {
254 template <
typename U> std::vector<T> operator()(U
const &)
const {
255 throw boost::bad_get{};
259 std::vector<T> operator()(std::vector<T>
const &v)
const {
return v; }
261 template <
typename V = T,
262 std::enable_if_t<!std::is_same_v<V, Variant>,
bool> =
true>
263 std::vector<T> operator()(std::vector<Variant>
const &vv)
const {
264 std::vector<T> ret(vv.size());
266 std::ranges::transform(vv, ret.begin(), get_value_helper<T>{});
273template <
typename T>
struct get_value_helper<std::
vector<T>, void> {
274 std::vector<T> operator()(
Variant const &v)
const {
275 return boost::apply_visitor(GetVectorOrEmpty<T>(), v);
279template <
typename K,
typename T>
280struct GetMapOrEmpty : boost::static_visitor<std::unordered_map<K, T>> {
282 template <
typename U> std::unordered_map<K, T> operator()(U
const &)
const {
283 throw boost::bad_get{};
287 std::unordered_map<K, T> operator()(std::unordered_map<K, T>
const &v)
const {
291 template <
typename V = T,
292 std::enable_if_t<!std::is_same_v<V, Variant>,
bool> =
true>
293 std::unordered_map<K, T>
294 operator()(std::unordered_map<K, Variant>
const &v)
const {
295 std::unordered_map<K, T> ret;
296 for (
auto it = v.begin(); it != v.end(); ++it) {
297 ret.insert({it->first, get_value_helper<T>{}(it->second)});
305struct get_value_helper<std::unordered_map<int, T>, void> {
306 std::unordered_map<int, T> operator()(
Variant const &v)
const {
307 return boost::apply_visitor(GetMapOrEmpty<int, T>(), v);
311struct get_value_helper<std::unordered_map<std::string, T>, void> {
312 std::unordered_map<std::string, T> operator()(
Variant const &v)
const {
313 return boost::apply_visitor(GetMapOrEmpty<std::string, T>(), v);
318class bad_get_nullptr :
public boost::bad_get {};
325struct get_value_helper<
327 typename std::enable_if_t<std::is_base_of_v<ObjectHandle, T>, void>> {
328 std::shared_ptr<T> operator()(
Variant const &v)
const {
329 auto so_ptr = boost::get<ObjectRef>(v);
331 throw bad_get_nullptr{};
334 auto t_ptr = std::dynamic_pointer_cast<T>(so_ptr);
340 throw boost::bad_get{};
352inline void handle_bad_get(
Variant const &v, std::string
const &name) {
353 auto const container_name = demangle::simplify_symbol_variant(v);
354 auto const containee_name = demangle::simplify_symbol_containee_variant(v);
355 auto const expected_containee_name =
356 demangle::simplify_symbol_containee(
static_cast<T *
>(
nullptr));
357 auto const from_container = !containee_name.empty();
358 auto const to_container = !expected_containee_name.empty();
359 auto what =
"Provided argument of type '" + container_name +
"'";
360 if (not name.empty()) {
361 what +=
" for parameter '" + name +
"'";
365 }
catch (bad_get_nullptr
const &) {
366 auto const item_error = (to_container) ?
" contains a value that" :
"";
367 throw Exception(what + item_error +
" is a null pointer");
368 }
catch (boost::bad_get
const &) {
369 auto const non_convertible = std::string(
" is not convertible to ");
370 auto item_error = std::string(
"");
371 if (from_container and to_container) {
372 item_error +=
" because it contains a value that";
373 item_error += non_convertible +
"'" + expected_containee_name +
"'";
375 auto const target = demangle::simplify_symbol(
static_cast<T *
>(
nullptr));
376 throw Exception(what + non_convertible +
"'" + target +
"'" + item_error);
380template <
typename T> T get_value(
Variant const &v, std::string
const &name) {
382 return detail::get_value_helper<T>{}(v);
384 detail::handle_bad_get<T>(v, name);
401 return detail::get_value<T>(v,
"");
411 if (vals.count(name) == 0ul) {
412 throw Exception(
"Parameter '" + name +
"' is missing.");
414 return detail::get_value<T>(vals.at(name), name);
424 if (vals.count(name)) {
425 return get_value<T>(vals.at(name));
434template <
typename T,
typename... Types,
typename... ArgNames>
436 ArgNames &&...args) {
437 return std::make_shared<T>(
438 get_value<Types>(vals, std::forward<ArgNames>(args))...);
443 dst = get_value<T>(vals, name);
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.
boost::make_recursive_variant< None, bool, int, std::size_t, double, std::string, ObjectRef, Utils::Vector3b, Utils::Vector3i, Utils::Vector2d, Utils::Vector3d, Utils::Vector4d, std::vector< int >, std::vector< double >, std::vector< boost::recursive_variant_ >, std::unordered_map< int, boost::recursive_variant_ >, std::unordered_map< std::string, boost::recursive_variant_ > >::type 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