#ifndef BUILTIN_FUNCTION_EMBEDDER_HPP #define BUILTIN_FUNCTION_EMBEDDER_HPP #include <language/ast/ASTNodeDataType.hpp> #include <language/utils/ASTNodeDataTypeTraits.hpp> #include <language/utils/DataHandler.hpp> #include <language/utils/DataVariant.hpp> #include <language/utils/FunctionTable.hpp> #include <utils/Demangle.hpp> #include <utils/Exceptions.hpp> #include <utils/PugsTraits.hpp> #include <functional> #include <memory> #include <vector> class IBuiltinFunctionEmbedder { public: virtual size_t numberOfParameters() const = 0; virtual ASTNodeDataType getReturnDataType() const = 0; virtual std::vector<ASTNodeDataType> getParameterDataTypes() const = 0; virtual DataVariant apply(const std::vector<DataVariant>&) const = 0; IBuiltinFunctionEmbedder() = default; IBuiltinFunctionEmbedder(const IBuiltinFunctionEmbedder&) = delete; IBuiltinFunctionEmbedder(IBuiltinFunctionEmbedder&&) = delete; virtual ~IBuiltinFunctionEmbedder() = default; }; template <typename FX, typename... Args> class BuiltinFunctionEmbedder { static_assert(std::is_class_v<BuiltinFunctionEmbedder<FX, Args...>>, "wrong template parameters do not use <FX, Args...>, use <FX(Args...)>"); }; template <typename T> inline constexpr bool is_const_ref_or_non_ref = (std::is_const_v<T> and std::is_lvalue_reference_v<T>) or (not std::is_lvalue_reference_v<T>); template <typename FX, typename... Args> class BuiltinFunctionEmbedder<FX(Args...)> : public IBuiltinFunctionEmbedder { private: std::function<FX(Args...)> m_f; using ArgsTuple = std::tuple<std::decay_t<Args>...>; template <size_t I> PUGS_INLINE void _copyValue(ArgsTuple& t, const std::vector<DataVariant>& v) const { std::visit( [&](auto&& v_i) { using Ti_Type = std::decay_t<decltype(std::get<I>(t))>; using Vi_Type = std::decay_t<decltype(v_i)>; if constexpr ((std::is_same_v<Vi_Type, Ti_Type>)) { std::get<I>(t) = v_i; } else if constexpr ((std::is_arithmetic_v<Vi_Type>)and(std::is_arithmetic_v<Ti_Type> or std::is_same_v<Ti_Type, std::string>)) { std::get<I>(t) = v_i; } else if constexpr (is_shared_ptr_v<Ti_Type>) { if constexpr (std::is_same_v<Vi_Type, EmbeddedData>) { using Ti_handeled_type = typename Ti_Type::element_type; try { auto& data_handler = dynamic_cast<const DataHandler<Ti_handeled_type>&>(v_i.get()); std::get<I>(t) = data_handler.data_ptr(); } catch (std::bad_cast&) { throw UnexpectedError("unexpected argument types while casting: invalid EmbeddedData type, expecting " + demangle<DataHandler<Ti_handeled_type>>()); } } else { throw UnexpectedError("unexpected argument types while casting: expecting EmbeddedData"); } } else if constexpr (std::is_same_v<Vi_Type, std::vector<EmbeddedData>>) { if constexpr (is_vector_v<Ti_Type>) { using Ti_value_type = typename Ti_Type::value_type; if constexpr (is_shared_ptr_v<Ti_value_type>) { static_assert(is_shared_ptr_v<Ti_value_type>, "expecting shared_ptr"); using Ti_handeled_type = typename Ti_value_type::element_type; std::get<I>(t).resize(v_i.size()); for (size_t j = 0; j < v_i.size(); ++j) { try { auto& data_handler = dynamic_cast<const DataHandler<Ti_handeled_type>&>(v_i[j].get()); std::get<I>(t)[j] = data_handler.data_ptr(); } catch (std::bad_cast&) { throw UnexpectedError( "unexpected argument types while casting: invalid EmbeddedData type, expecting " + demangle<DataHandler<Ti_handeled_type>>()); } } } else { throw UnexpectedError("unexpected argument types while casting \"" + demangle<Vi_Type>() + "\" to \"" + demangle<Ti_Type>() + '"'); } } else { throw UnexpectedError("unexpected argument types while casting \"" + demangle<Vi_Type>() + "\" to \"" + demangle<Ti_Type>() + '"'); } } else { throw UnexpectedError("unexpected argument types while casting \"" + demangle<Vi_Type>() + "\" to \"" + demangle<Ti_Type>() + '"'); } }, v[I]); } template <size_t... I> PUGS_INLINE void _copyFromVector(ArgsTuple& t, const std::vector<DataVariant>& v, std::index_sequence<I...>) const { Assert(sizeof...(Args) == v.size()); (_copyValue<I>(t, v), ...); } template <size_t I> PUGS_INLINE ASTNodeDataType _getOneParameterDataType(ArgsTuple& t) const { using ArgN_T = std::decay_t<decltype(std::get<I>(t))>; if constexpr (is_data_variant_v<ArgN_T>) { return ast_node_data_type_from<ArgN_T>; } else if constexpr (std::is_same_v<void, ArgN_T>) { return ASTNodeDataType::void_t; } else { return ASTNodeDataType::type_id_t; } } template <size_t... I> PUGS_INLINE std::vector<ASTNodeDataType> _getParameterDataTypes(ArgsTuple t, std::index_sequence<I...>) const { std::vector<ASTNodeDataType> parameter_type_list; (parameter_type_list.push_back(this->_getOneParameterDataType<I>(t)), ...); return parameter_type_list; } template <typename T> PUGS_INLINE std::shared_ptr<IDataHandler> _createHandler(std::shared_ptr<T> data) const { return std::make_shared<DataHandler<T>>(data); } template <size_t I> PUGS_INLINE void constexpr _check_arg() const { using ArgN_T = std::tuple_element_t<I, std::tuple<Args...>>; if constexpr (std::is_lvalue_reference_v<ArgN_T>) { static_assert(std::is_const_v<std::remove_reference_t<ArgN_T>>, "builtin function arguments are non mutable use 'const' when passing references"); } if constexpr (is_std_ptr_v<ArgN_T>) { static_assert(std::is_const_v<typename ArgN_T::element_type>, "builtin function arguments are non mutable. For instance use std::shared_ptr<const T>"); } } template <size_t... I> PUGS_INLINE void constexpr _check_arg_list(std::index_sequence<I...>) const { (_check_arg<I>(), ...); } public: PUGS_INLINE ASTNodeDataType getReturnDataType() const final { if constexpr (is_data_variant_v<FX>) { return ast_node_data_type_from<FX>; } else if constexpr (std::is_same_v<void, FX>) { return ASTNodeDataType::void_t; } else { return ASTNodeDataType::type_id_t; } } PUGS_INLINE std::vector<ASTNodeDataType> getParameterDataTypes() const final { constexpr size_t N = std::tuple_size_v<ArgsTuple>; ArgsTuple t; using IndexSequence = std::make_index_sequence<N>; return this->_getParameterDataTypes(t, IndexSequence{}); } PUGS_INLINE size_t numberOfParameters() const final { return sizeof...(Args); } public: PUGS_INLINE DataVariant apply(const std::vector<DataVariant>& x) const final { constexpr size_t N = std::tuple_size_v<ArgsTuple>; ArgsTuple t; using IndexSequence = std::make_index_sequence<N>; this->_copyFromVector(t, x, IndexSequence{}); if constexpr (is_data_variant_v<FX>) { return {std::apply(m_f, t)}; } else if constexpr (std::is_same_v<FX, void>) { std::apply(m_f, t); return {}; } else { return EmbeddedData(_createHandler(std::apply(m_f, t))); } } BuiltinFunctionEmbedder(std::function<FX(Args...)> f) : m_f{f} { using IndexSequence = std::make_index_sequence<std::tuple_size_v<ArgsTuple>>; this->_check_arg_list(IndexSequence{}); static_assert((std::is_same_v<Args, Args> and ...)); } BuiltinFunctionEmbedder(const BuiltinFunctionEmbedder&) = delete; BuiltinFunctionEmbedder(BuiltinFunctionEmbedder&&) = delete; ~BuiltinFunctionEmbedder() = default; }; template <typename FX> class BuiltinFunctionEmbedder<FX, void> { static_assert(std::is_class_v<BuiltinFunctionEmbedder<FX, void>>, "wrong template parameters do not use <FX, void>, use <FX(void)>"); }; template <typename FX> class BuiltinFunctionEmbedder<FX(void)> : public IBuiltinFunctionEmbedder { private: std::function<FX(void)> m_f; template <typename T> PUGS_INLINE std::shared_ptr<IDataHandler> _createHandler(std::shared_ptr<T> data) const { return std::make_shared<DataHandler<T>>(data); } public: PUGS_INLINE ASTNodeDataType getReturnDataType() const final { if constexpr (is_data_variant_v<FX>) { return ast_node_data_type_from<FX>; } else if constexpr (std::is_same_v<void, FX>) { return ASTNodeDataType::void_t; } else { return ASTNodeDataType::type_id_t; } } PUGS_INLINE std::vector<ASTNodeDataType> getParameterDataTypes() const final { return {}; } PUGS_INLINE size_t numberOfParameters() const final { return 0; } PUGS_INLINE DataVariant apply(const std::vector<DataVariant>&) const final { if constexpr (is_data_variant_v<FX>) { return {m_f()}; } else if constexpr (std::is_same_v<FX, void>) { m_f(); return {}; } else { return EmbeddedData(_createHandler(m_f())); } } BuiltinFunctionEmbedder(std::function<FX(void)> f) : m_f{f} {} BuiltinFunctionEmbedder(const BuiltinFunctionEmbedder&) = delete; BuiltinFunctionEmbedder(BuiltinFunctionEmbedder&&) = delete; ~BuiltinFunctionEmbedder() = default; }; #endif // BUILTIN_FUNCTION_EMBEDDER_HPP