#ifndef CFUNCTION_EMBEDDER_HPP #define CFUNCTION_EMBEDDER_HPP #include <PugsAssert.hpp> #include <PugsMacros.hpp> #include <ASTNodeDataType.hpp> #include <ASTNodeDataVariant.hpp> #include <cmath> #include <functional> #include <iostream> #include <tuple> #include <vector> #include <type_traits> class ICFunctionEmbedder { public: virtual size_t numberOfArguments() const = 0; virtual ASTNodeDataType getReturnDataType() const = 0; virtual std::vector<ASTNodeDataType> getArgumentDataTypes() const = 0; virtual void apply(const std::vector<ASTNodeDataVariant>& x, ASTNodeDataVariant& f_x) const = 0; virtual ~ICFunctionEmbedder() = default; }; template <typename FX, typename... Args> class CFunctionEmbedder : public ICFunctionEmbedder { private: std::function<FX(Args...)> m_f; using ArgsTuple = std::tuple<Args...>; template <size_t I> PUGS_INLINE void _copy_value(ArgsTuple& t, const std::vector<ASTNodeDataVariant>& v) const { std::visit( [&](auto v_i) { if constexpr (std::is_arithmetic_v<decltype(v_i)>) { std::get<I>(t) = v_i; } else { throw std::runtime_error("unexpected argument type!"); } }, v[I]); } template <size_t... I> PUGS_INLINE void _copy_from_vector(ArgsTuple& t, const std::vector<ASTNodeDataVariant>& v, std::index_sequence<I...>) const { Assert(sizeof...(Args) == v.size()); (_copy_value<I>(t, v), ...); } template <size_t I> PUGS_INLINE ASTNodeDataType _getOneArgumentDataType(ArgsTuple& t) const { return ast_node_data_type_from_pod<std::decay_t<decltype(std::get<I>(t))>>; } template <size_t... I> PUGS_INLINE std::vector<ASTNodeDataType> _getArgumentDataTypes(ArgsTuple t, std::index_sequence<I...>) const { std::vector<ASTNodeDataType> argument_type_list; (argument_type_list.push_back(this->_getOneArgumentDataType<I>(t)), ...); return argument_type_list; } public: PUGS_INLINE ASTNodeDataType getReturnDataType() const final { return ast_node_data_type_from_pod<FX>; } PUGS_INLINE std::vector<ASTNodeDataType> getArgumentDataTypes() const final { constexpr size_t N = std::tuple_size_v<ArgsTuple>; ArgsTuple t; using IndexSequence = std::make_index_sequence<N>; return this->_getArgumentDataTypes(t, IndexSequence{}); } PUGS_INLINE size_t numberOfArguments() const final { return sizeof...(Args); } PUGS_INLINE void apply(const std::vector<ASTNodeDataVariant>& x, ASTNodeDataVariant& f_x) const final { constexpr size_t N = std::tuple_size_v<ArgsTuple>; ArgsTuple t; using IndexSequence = std::make_index_sequence<N>; this->_copy_from_vector(t, x, IndexSequence{}); f_x = std::apply(m_f, t); } // @note This is written in a template fashion to ensure that function type // is correct. If one uses simply CFunctionEmbedder(std::function<FX(Args...)>&&), // types seem unchecked template <typename FX2, typename... Args2> CFunctionEmbedder(std::function<FX2(Args2...)> f) : m_f(f) { static_assert(std::is_same_v<FX, FX2>, "incorrect return type"); static_assert(sizeof...(Args) == sizeof...(Args2), "invalid number of arguments"); using Args2Tuple = std::tuple<Args2...>; static_assert(std::is_same_v<ArgsTuple, Args2Tuple>, "invalid arguments type"); } }; #endif // CFUNCTION_EMBEDDER_HPP