Added ScriptValue type as a generic variant type for scripts and similar situations.

This commit is contained in:
Patrick 2024-07-26 23:28:39 +02:00
parent 3a8602edcc
commit 9b4425c495

View File

@ -0,0 +1,208 @@
#pragma once
#ifndef MIJIN_TYPES_SCRIPT_VALUE_HPP_INCLUDED
#define MIJIN_TYPES_SCRIPT_VALUE_HPP_INCLUDED 1
#include <charconv>
#include <string>
#include <variant>
#include <vector>
#include "../container/optional.hpp"
namespace mijin
{
//
// public types
//
struct ArrayValue
{
std::vector<class ScriptValue> values;
bool operator==(const ArrayValue& other) const;
inline bool operator!=(const ArrayValue& other) const {
return !(*this == other);
}
std::partial_ordering operator<=>(const ArrayValue& other) const;
};
struct UndefinedValue
{
auto operator<=>(const UndefinedValue&) const = default;
};
inline constexpr UndefinedValue UNDEFINED_VALUE;
using script_int_t = long long;
using script_float_t = double;
using script_value_base_t = std::variant<
UndefinedValue,
bool,
script_int_t,
script_float_t,
std::string,
ArrayValue
>;
class ScriptValue
{
private:
script_value_base_t base_;
public:
ScriptValue() noexcept = default;
ScriptValue(const ScriptValue&) noexcept = default;
ScriptValue(ScriptValue&&) noexcept = default;
template<typename TValue>
ScriptValue(TValue&& value);
ScriptValue& operator=(const ScriptValue&) = default;
ScriptValue& operator=(ScriptValue&&) noexcept = default;
std::partial_ordering operator<=>(const ScriptValue& other) const = default;
template<typename TFunction>
auto visit(TFunction&& function) const
{
using result_t = std::invoke_result_t<TFunction, long long>;
if constexpr (std::is_same_v<result_t, void>)
{
std::visit(function, base_);
}
else
{
return std::visit(function, base_);
}
}
[[nodiscard]]
Optional<script_int_t> toInt() const noexcept
{
return visit([&](auto&& value) -> Optional<script_int_t>
{
using type_t = std::decay_t<decltype(value)>;
if constexpr (std::is_same_v<type_t, bool> || std::is_same_v<type_t, script_int_t> || std::is_same_v<type_t, script_float_t>)
{
return static_cast<script_int_t>(value);
}
else if constexpr (std::is_same_v<type_t, std::string>)
{
script_int_t result = 0;
const std::from_chars_result fromCharsResult = std::from_chars(&*value.begin(), &*value.end(), result);
if (fromCharsResult.ec != std::errc{})
{
return NULL_OPTIONAL;
}
return result;
}
else
{
return NULL_OPTIONAL;
}
});
}
[[nodiscard]]
Optional<script_float_t> toFloat() const noexcept
{
return visit([&](auto&& value) -> Optional<script_float_t>
{
using type_t = std::decay_t<decltype(value)>;
if constexpr (std::is_same_v<type_t, bool> || std::is_same_v<type_t, script_int_t> || std::is_same_v<type_t, script_float_t>)
{
return static_cast<script_float_t>(value);
}
else if constexpr (std::is_same_v<type_t, std::string>)
{
script_float_t result = 0;
const std::from_chars_result fromCharsResult = std::from_chars(&*value.begin(), &*value.end(), result);
if (fromCharsResult.ec != std::errc{})
{
return NULL_OPTIONAL;
}
return result;
}
else
{
return NULL_OPTIONAL;
}
});
}
[[nodiscard]]
Optional<std::string> toString() const noexcept
{
return visit([](auto&& value) -> Optional<std::string>
{
using type_t = std::decay_t<decltype(value)>;
if constexpr (std::is_same_v<type_t, UndefinedValue>)
{
return std::string("");
}
else if constexpr (std::is_same_v<type_t, bool>)
{
return std::string(value ? "true" : "false");
}
else if constexpr (std::is_same_v<type_t, script_int_t> || std::is_same_v<type_t, script_float_t>)
{
return std::to_string(value);
}
else if constexpr (std::is_same_v<type_t, std::string>)
{
return value;
}
else
{
return NULL_OPTIONAL;
}
});
}
template<typename T>
[[nodiscard]]
Optional<std::decay_t<T>> to() const noexcept
{
using type_t = std::decay_t<T>;
if constexpr (std::is_same_v<type_t, script_int_t>)
{
return toInt();
}
else if constexpr (std::is_integral_v<type_t>)
{
return toInt().then([](script_int_t val) { return static_cast<type_t>(val); });
}
else if constexpr (std::is_same_v<type_t, script_float_t>)
{
return toFloat();
}
else if constexpr (std::is_floating_point_v<type_t>)
{
return toFloat().then([](script_float_t val) { return static_cast<type_t>(val); });
}
else if constexpr (std::is_same_v<type_t, std::string>)
{
return toString();
}
else if constexpr (std::is_same_v<type_t, ScriptValue>)
{
return *this;
}
else
{
static_assert(mijin::always_false_v<T>, "Cannot convert to this type.");
}
}
};
//
// public functions
//
template<typename TValue>
ScriptValue::ScriptValue(TValue&& value) : base_(std::forward<TValue>(value))
{
}
} // namespace mijin
#endif // MIJIN_TYPES_SCRIPT_VALUE_HPP_INCLUDED