#ifndef FUNCTION_ARGUMENT_CONVERTER_HPP #define FUNCTION_ARGUMENT_CONVERTER_HPP #include <language/node_processor/ExecutionPolicy.hpp> #include <language/utils/DataVariant.hpp> #include <utils/Demangle.hpp> #include <utils/Exceptions.hpp> #include <utils/PugsTraits.hpp> #include <sstream> class IFunctionArgumentConverter { public: virtual DataVariant convert(ExecutionPolicy& exec_policy, DataVariant&& value) = 0; IFunctionArgumentConverter() = default; IFunctionArgumentConverter(const IFunctionArgumentConverter&) = delete; IFunctionArgumentConverter(IFunctionArgumentConverter&&) = delete; virtual ~IFunctionArgumentConverter() = default; }; class FunctionArgumentToStringConverter final : public IFunctionArgumentConverter { private: size_t m_argument_id; public: DataVariant convert(ExecutionPolicy& exec_policy, DataVariant&& value) { std::visit( [&](auto&& v) { using T = std::decay_t<decltype(v)>; if constexpr (std::is_arithmetic_v<T>) { exec_policy.currentContext()[m_argument_id] = std::to_string(v); } else if constexpr (std::is_same_v<T, std::string>) { exec_policy.currentContext()[m_argument_id] = v; } else { std::ostringstream sout; sout << value << std::ends; exec_policy.currentContext()[m_argument_id] = sout.str(); } }, value); return {}; } FunctionArgumentToStringConverter(size_t argument_id) : m_argument_id{argument_id} {} }; template <typename ExpectedValueType, typename ProvidedValueType> class FunctionArgumentConverter final : public IFunctionArgumentConverter { private: size_t m_argument_id; public: DataVariant convert(ExecutionPolicy& exec_policy, DataVariant&& value) { static_assert(not std::is_same_v<ExpectedValueType, std::string>, "use FunctionArgumentToStringConverter"); if constexpr (std::is_same_v<ExpectedValueType, ProvidedValueType>) { exec_policy.currentContext()[m_argument_id] = std::move(value); } else { exec_policy.currentContext()[m_argument_id] = std::move(static_cast<ExpectedValueType>(std::get<ProvidedValueType>(value))); } return {}; } FunctionArgumentConverter(size_t argument_id) : m_argument_id{argument_id} {} }; template <typename ExpectedValueType, typename ProvidedValueType> class FunctionTinyVectorArgumentConverter final : public IFunctionArgumentConverter { private: size_t m_argument_id; public: DataVariant convert(ExecutionPolicy& exec_policy, DataVariant&& value) { if constexpr (std::is_same_v<ExpectedValueType, ProvidedValueType>) { std::visit( [&](auto&& v) { using ValueT = std::decay_t<decltype(v)>; if constexpr (std::is_same_v<ValueT, ExpectedValueType>) { exec_policy.currentContext()[m_argument_id] = std::move(value); } else if constexpr (std::is_same_v<ValueT, AggregateDataVariant>) { ExpectedValueType vector_value{}; for (size_t i = 0; i < vector_value.dimension(); ++i) { std::visit( [&](auto&& v_i) { using Vi_T = std::decay_t<decltype(v_i)>; if constexpr (std::is_arithmetic_v<Vi_T>) { vector_value[i] = v_i; } else { throw UnexpectedError(demangle<Vi_T>() + " unexpected aggregate value type"); } }, v[i]); } exec_policy.currentContext()[m_argument_id] = std::move(vector_value); } }, value); } else if constexpr (std::is_same_v<ProvidedValueType, ZeroType>) { exec_policy.currentContext()[m_argument_id] = ExpectedValueType{ZeroType::zero}; } else { static_assert(std::is_same_v<ExpectedValueType, TinyVector<1>>); exec_policy.currentContext()[m_argument_id] = std::move(static_cast<ExpectedValueType>(std::get<ProvidedValueType>(value))); } return {}; } FunctionTinyVectorArgumentConverter(size_t argument_id) : m_argument_id{argument_id} {} }; template <typename ContentType, typename ProvidedValueType> class FunctionTupleArgumentConverter final : public IFunctionArgumentConverter { private: size_t m_argument_id; public: DataVariant convert(ExecutionPolicy& exec_policy, DataVariant&& value) { using TupleType = std::vector<ContentType>; if constexpr (std::is_convertible_v<ProvidedValueType, ContentType>) { std::visit( [&](auto&& v) { using ValueT = std::decay_t<decltype(v)>; if constexpr (std::is_same_v<ValueT, ContentType>) { exec_policy.currentContext()[m_argument_id] = std::move(TupleType{std::move(v)}); } else if constexpr (is_std_vector_v<ValueT>) { using ContentT = typename ValueT::value_type; if constexpr (std::is_same_v<ContentT, ContentType>) { TupleType list_value; list_value.reserve(v.size()); for (size_t i = 0; i < v.size(); ++i) { list_value.emplace_back(std::move(v[i])); } exec_policy.currentContext()[m_argument_id] = std::move(list_value); } else if constexpr ((std::is_convertible_v<ContentT, ContentType>)and not is_tiny_vector_v<ContentType>) { TupleType list_value; list_value.reserve(v.size()); for (size_t i = 0; i < v.size(); ++i) { list_value.push_back(static_cast<ContentType>(v[i])); } exec_policy.currentContext()[m_argument_id] = std::move(list_value); } else { // LCOV_EXCL_START throw UnexpectedError(std::string{"cannot convert '"} + demangle<ValueT>() + "' to '" + demangle<ContentType>() + "'"); // LCOV_EXCL_STOP } } else if constexpr (std::is_convertible_v<ValueT, ContentType> and not is_tiny_vector_v<ContentType>) { exec_policy.currentContext()[m_argument_id] = std::move(TupleType{static_cast<ContentType>(v)}); } else { throw UnexpectedError(std::string{"cannot convert '"} + demangle<ValueT>() + "' to '" + demangle<ContentType>() + "'"); } }, value); } else { // LCOV_EXCL_START throw UnexpectedError(std::string{"cannot convert '"} + demangle<ProvidedValueType>() + "' to '" + demangle<ContentType>() + "'"); // LCOV_EXCL_STOP } return {}; } FunctionTupleArgumentConverter(size_t argument_id) : m_argument_id{argument_id} {} }; template <typename ContentType, typename ProvidedValueType> class FunctionListArgumentConverter final : public IFunctionArgumentConverter { private: size_t m_argument_id; public: DataVariant convert(ExecutionPolicy& exec_policy, DataVariant&& value) { using TupleType = std::vector<ContentType>; if constexpr (std::is_same_v<ContentType, ProvidedValueType>) { std::visit( [&](auto&& v) { using ValueT = std::decay_t<decltype(v)>; if constexpr (std::is_same_v<ValueT, AggregateDataVariant>) { TupleType list_value; list_value.reserve(v.size()); for (size_t i = 0; i < v.size(); ++i) { std::visit( [&](auto&& vi) { using Vi_T = std::decay_t<decltype(vi)>; if constexpr (std::is_same_v<Vi_T, ContentType>) { list_value.emplace_back(vi); } else if constexpr (is_tiny_vector_v<ContentType>) { // LCOV_EXCL_START throw UnexpectedError(std::string{"invalid conversion of '"} + demangle<Vi_T>() + "' to '" + demangle<ContentType>() + "'"); // LCOV_EXCL_STOP } else if constexpr (std::is_convertible_v<Vi_T, ContentType>) { list_value.emplace_back(vi); } else { // LCOV_EXCL_START throw UnexpectedError("unexpected types"); // LCOV_EXCL_STOP } }, (v[i])); } exec_policy.currentContext()[m_argument_id] = std::move(list_value); } else if constexpr (is_std_vector_v<ValueT>) { using ContentT = typename ValueT::value_type; if constexpr (std::is_same_v<ContentT, ContentType>) { exec_policy.currentContext()[m_argument_id] = v; } else { // LCOV_EXCL_START throw UnexpectedError(std::string{"invalid conversion of '"} + demangle<ContentT>() + "' to '" + demangle<ContentType>() + "'"); // LCOV_EXCL_STOP } } else if constexpr (std::is_same_v<ValueT, ContentType>) { exec_policy.currentContext()[m_argument_id] = std::move(TupleType{v}); } else if constexpr (std::is_convertible_v<ValueT, ContentType> and not is_tiny_vector_v<ValueT> and not is_tiny_vector_v<ContentType>) { exec_policy.currentContext()[m_argument_id] = std::move(TupleType{static_cast<ContentType>(v)}); } else { // LCOV_EXCL_START throw UnexpectedError(demangle<ValueT>() + " unexpected value type"); // LCOV_EXCL_STOP } }, value); } static_assert(std::is_same_v<ContentType, ProvidedValueType>, "conversion is not implemented"); return {}; } FunctionListArgumentConverter(size_t argument_id) : m_argument_id{argument_id} {} }; class FunctionArgumentToFunctionSymbolIdConverter final : public IFunctionArgumentConverter { private: size_t m_argument_id; std::shared_ptr<SymbolTable> m_symbol_table; public: DataVariant convert(ExecutionPolicy& exec_policy, DataVariant&& value) { exec_policy.currentContext()[m_argument_id] = FunctionSymbolId{std::get<uint64_t>(value), m_symbol_table}; return {}; } FunctionArgumentToFunctionSymbolIdConverter(size_t argument_id, const std::shared_ptr<SymbolTable>& symbol_table) : m_argument_id{argument_id}, m_symbol_table{symbol_table} {} }; #endif // FUNCTION_ARGUMENT_CONVERTER_HPP