/** @file DPrimitive.hpp * * @author Roland Conybeare, Jan 2025 **/ #pragma once #include "RuntimeContext.hpp" #include #include #include #include #include #include #include #include #include #include namespace xo { namespace scm { namespace detail { /** @brief Extract return type and argument types from a function type. * * Primary template (undefined) - specializations handle specific cases **/ template struct PmFnTraits; /** specialization for function pointers **/ template struct PmFnTraits rcx, Args...)> { /** function return type **/ using return_type = R; /** tuple type for function arguments (except for runtime context) **/ using args_tuple = std::tuple; /** number of arguments (except for runtime context) **/ static constexpr std::size_t n_args = sizeof...(Args); /** arg_type is the type of the i'th argument to Fn. * (starting from argument after runtime context) **/ template using arg_type = std::tuple_element_t; }; /** specialization for function references **/ template struct PmFnTraits, Args...)> : PmFnTraits, Args...)> {}; /** specialization for plain function types **/ template struct PmFnTraits, Args...)> : PmFnTraits, Args...)> {}; } /** @brief Schematika primitive with fixed number of arguments **/ template class Primitive { public: using Traits = detail::PmFnTraits; using ACollector = xo::mm::ACollector; using AAllocator = xo::mm::AAllocator; using AGCObject = xo::mm::AGCObject; using DArray = xo::scm::DArray; using Reflect = xo::reflect::Reflect; using TypeDescr = xo::reflect::TypeDescr; using ppindentinfo = xo::print::ppindentinfo; public: Primitive(std::string_view name, Fn fn) : name_{name}, fn_td_{Reflect::require()}, fn_{fn} {} TypeDescr fn_td() const noexcept { return fn_td_; } std::string_view name() const noexcept { return name_; } static constexpr std::int32_t n_args() noexcept { return Traits::n_args; } bool is_nary() const noexcept { return false; } obj apply_nocheck(obj rcx, const DArray * args) { return _apply_nocheck(rcx, args, std::make_index_sequence{}); } /** @defgroup scm-primitive-printable-facet **/ ///@{ bool pretty(const ppindentinfo & ppii) const; ///@} /** @defgroup scm-primitive-gcobject-facet **/ ///@{ std::size_t shallow_size() const noexcept; Primitive * shallow_copy(obj mm) const noexcept; std::size_t forward_children(obj gc) noexcept; ///@} private: template obj _apply_nocheck(obj rcx, const DArray * args, std::index_sequence) { using xo::facet::FacetRegistry; using R = typename Traits::return_type; assert(args); assert(args->size() > 0); obj mm = rcx.allocator(); R result = fn_(rcx, GCObjectConversion>::from_gco(mm, args->at(Is))... ); return GCObjectConversion::to_gco(mm, result); } private: /** name of this primitive **/ std::string_view name_; /** type description for function * Note that this type description will have additional first argument * for obj **/ TypeDescr fn_td_; /** function implementation **/ Fn fn_; }; /*Primitive*/ template bool Primitive::pretty(const ppindentinfo & ppii) const { return ppii.pps()->pretty_struct (ppii, "Primitive", refrtag("name", name_), refrtag("td", fn_td_), refrtag("fn", fn_)); } template std::size_t Primitive::shallow_size() const noexcept { return sizeof(*this); } template Primitive * Primitive::shallow_copy(obj mm) const noexcept { void * mem = mm.alloc_copy((std::byte *)this); if (mem) { return new (mem) Primitive(*this); } return nullptr; } template std::size_t Primitive::forward_children(obj) noexcept { // Primitive holds no GC refs (just string_view + function pointer) return this->shallow_size(); } } /*namespace scm*/ } /*namespace xo*/ /* end DPrimitive.hpp */