// // php-armadillo/php_arma.hh // // @Author CismonX // #ifndef PHP_ARMA_HH #define PHP_ARMA_HH #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #ifndef PHP_ARMA_ENABLE_PCH #include #endif #define PHP_ARMA_VERSION "0.0.1" #if PHP_VERSION_ID >= 70400 #define ZEND_ACC_CTOR 0 #endif /// Helper macros for method entry. #define PHP_ARMA_START_ME(cls, ...) \ zend_function_entry cls<__VA_ARGS__>::me[] = { #define PHP_ARMA_END_ME() \ PHP_FE_END \ } #define PHP_ARMA_ME(func, flags) \ ZEND_FENTRY(func, zif_##func, nullptr, flags) #define PHP_ARMA_ME_EX(cls, func, flags) \ ZEND_FENTRY(func, cls::zif_##func, nullptr, flags) #define PHP_ARMA_METHOD(cls, func, ...) \ void ZEND_FASTCALL cls<__VA_ARGS__>::zif_##func(INTERNAL_FUNCTION_PARAMETERS) #define PHP_ARMA_FENTRY(fentry) \ std::make_tuple(fentry_size(fentry), fentry) #define PHP_ARMA_INSTANTIATE(parent, child) \ template class parent>; \ template class parent>; \ template class parent> /// Helper macros for class entry and object handlers. #define PHP_ARMA_CE_HANDLRES_DECLARE() \ static inline zend_class_entry *ce; \ static inline zend_object_handlers handlers #define PHP_ARMA_CE(cls, ...) \ cls<__VA_ARGS__>::ce #define PHP_ARMA_HANDLERS(cls, ...) \ &cls<__VA_ARGS__>::handlers #define Z_OBJNAME_P(zval_p) \ ZSTR_VAL(Z_OBJCE_P(zval_p)->name) #define PHP_ARMA_COMMON_DECLARE() \ struct php_name; \ static zend_function_entry me[] #define PHP_ARMA_NAME_DECLARE(cls, name, ...) \ template <> \ struct cls<__VA_ARGS__>::php_name \ { \ static constexpr const char val[] = name; \ } #ifdef PHP_ARMA_OPERATORS /// Helper macros for handling operator overloading. #define PHP_ARMA_OPERATOR_BEGIN(parent_ce) \ { \ if (instanceof_function(ce, parent_ce)) { #define PHP_ARMA_OPERATOR_END() \ return false; \ } \ } #define PHP_ARMA_OPERATOR_EX2(base, child, func) \ if (instanceof_function(ce, child::ce)) { \ using base_t = util::arg_type::type; \ return base_t::operators::func(zv1, zv2, rv); \ } #define PHP_ARMA_OPERATOR_EX(cls, func) \ PHP_ARMA_OPERATOR_EX2(cls, cls, func) #define PHP_ARMA_ASSIGN_OPERATOR_EX2(base, child, func) \ if (instanceof_function(ce, child::ce)) { \ using base_t = util::arg_type::type; \ auto v = base_t::operators::func(zv1, zv2, zv1); \ if (rv) { \ ZVAL_COPY(rv, zv1); \ } \ return v; \ } #define PHP_ARMA_ASSIGN_OPERATOR_EX(cls, func) \ PHP_ARMA_ASSIGN_OPERATOR_EX2(cls, cls, func) #endif // PHP_ARMA_OPERATORS /// Helper macros for handling native objects. #define ZOBJ_NATIVE(zobj) \ to_native_object(zobj) #define Z_NATIVE_OBJ_P(zv) ZOBJ_NATIVE(Z_OBJ_P(zv)) #define THIS_NATIVE Z_NATIVE_OBJ_P(&EX(This)) /// Misc helper macros. #define RETVAL_THIS() \ ZVAL_COPY(return_value, &EX(This)) namespace php_arma { extern zend_module_entry module_entry; /// Utilities. namespace util { /// Hack for extracting type from a parentheses-enclosed macro argument. template struct arg_type; template struct arg_type { using type = U; }; template constexpr auto is_any_v = std::disjunction_v...>; } /// Helpers for compile-time string concatenation for better module startup performance. namespace str { template using seq = std::integer_sequence; template using seq_impl = std::make_integer_sequence; constexpr auto get_len(const char *str) { auto i = 0; for (; *str != 0; ++i, ++str); return i; } template struct concat_impl; template struct concat_impl, Str2, seq> { static constexpr const char value[] { Str1[Len1]..., Str2[Len2]..., 0 }; }; template constexpr auto concat { concat_impl, Str2, seq_impl>::value }; constexpr const char namespace_prefix[] = "Arma\\"; constexpr const char internal_prefix[] = "Internal\\"; constexpr const char empty[] = ""; template constexpr auto with_arma_prefix { concat }; template constexpr auto with_internal_prefix { concat }; } // Data structures namespace ds { /// Non-subview container objects struct base { const arma::uword n_rows; const arma::uword n_cols; const arma::uword n_elem; }; /// Subview objects struct subview { arma_aligned const base& parent; const arma::uword n_rows; const arma::uword n_cols; const arma::uword n_elem; }; /// Diagonal subview objects struct diagview { arma_aligned const base& parent; const arma::uword row_offset; const arma::uword col_offset; const arma::uword n_rows; const arma::uword n_elem; static constexpr const arma::uword n_cols = 1; }; } /// Helper functions for throwing exceptions when errors occur. zend_always_inline void throw_exception_ex(zend_class_entry *ce, const char *message) { zend_throw_exception(ce, message, 0); } template zend_always_inline void throw_exception_ex(zend_class_entry *ce, const char *message, Ts&&... args) { zend_throw_exception_ex(ce, 0, message, args...); } zend_always_inline void throw_exception(const char *message) { throw_exception_ex(zend_ce_exception, message); } template zend_always_inline void throw_exception(const char *message, Ts&&... args) { throw_exception_ex(zend_ce_exception, message, args...); } zend_always_inline void throw_error(const char *message) { throw_exception_ex(zend_ce_error, message); } template zend_always_inline void throw_error(const char *message, Ts&&... args) { throw_exception_ex(zend_ce_error, message, args...); } zend_always_inline void ex_bad_type(const char *expected, const char *got) { throw_exception_ex(zend_ce_type_error, "bad type, expected %s, %s given", expected, got); } /// Helper functions for initializing class entry and object handlers. template zend_always_inline zend_class_entry *ce_init(const zend_function_entry *methods, F&& init_func) { zend_class_entry tmp_ce; constexpr auto name = str::with_arma_prefix; constexpr auto name_len = str::get_len(name); INIT_CLASS_ENTRY_EX(tmp_ce, name, name_len, methods); return init_func(&tmp_ce); } template zend_always_inline zend_class_entry *ce_init(const zend_function_entry *methods, F&& init_func, Ts... interfaces) { auto ce = ce_init(methods, init_func); zend_class_implements(ce, sizeof...(Ts), interfaces...); return ce; } template zend_always_inline zend_class_entry *interface_register() { // Although methods are declared in interfaces as you see in the stubs, // nothing is declared in the internal interface implementation. return ce_init>(nullptr, zend_register_internal_interface); } template zend_always_inline zend_class_entry *interface_register(Ts... parents) { return ce_init>(nullptr, zend_register_internal_interface, parents...); } template zend_always_inline zend_class_entry *class_register(zend_class_entry *parent, const zend_function_entry *methods) { auto init_func = std::bind(zend_register_internal_class_ex, std::placeholders::_1, parent); return ce_init(methods, init_func); } template zend_always_inline zend_class_entry *abstract_class_register() { auto ce = ce_init(nullptr, zend_register_internal_class); ce->ce_flags |= ZEND_ACC_EXPLICIT_ABSTRACT_CLASS; return ce; } template zend_always_inline zend_class_entry *abstract_class_register(Ts... interfaces) { auto ce = ce_init(nullptr, zend_register_internal_class, interfaces...); ce->ce_flags |= ZEND_ACC_EXPLICIT_ABSTRACT_CLASS; return ce; } template zend_always_inline void property_declare(zend_class_entry *ce, zval *zv) { zend_declare_property(ce, Name, str::get_len(Name), zv, AccessType); } zend_always_inline void declare_subview_parent(zend_class_entry *ce) { property_declare(ce, &EG(uninitialized_zval)); } zend_always_inline void object_handlers_init(zend_object_handlers *handlers) { memcpy(handlers, &std_object_handlers, sizeof(zend_object_handlers)); } template zend_always_inline zend_object *to_zend_object(T *obj) { return reinterpret_cast(obj + 1); } template zend_always_inline T *to_native_object(zend_object *zobj) { return reinterpret_cast(zobj) - 1; } template zend_always_inline zval *get_parent_zval(zval *current) { if constexpr (std::is_same_v) { return current; } else { return OBJ_PROP_NUM(Z_OBJ_P(current), 0); } } /// Helper functions for function entry. zend_always_inline size_t fentry_size(zend_function_entry *fentry) { size_t retval = 0; while ((fentry++)->fname != nullptr) { ++retval; } return retval * sizeof(zend_function_entry); } template zend_always_inline zend_function_entry *fentry_list_concat_impl(size_t mem_size, Ts&&... function_entry) { auto retval = reinterpret_cast(malloc(mem_size)); size_t offset = 0; for (auto [size, fentry] : { function_entry... }) { memcpy(retval + offset, fentry, size); offset += size / sizeof(zend_function_entry); } zend_function_entry empty_fentry PHP_FE_END; memcpy(retval + offset, &empty_fentry, sizeof(zend_function_entry)); return retval; } template zend_always_inline zend_function_entry *fentry_list_concat(Ts&&... function_entry) { size_t mem_size = sizeof(zend_function_entry); for (auto [size, _] : { function_entry... }) { mem_size += size; } return fentry_list_concat_impl(mem_size, function_entry...); } /// Helper functions for handling objects. template zend_always_inline zend_object *object_create(zend_class_entry *ce, F&& init) { auto obj = reinterpret_cast(emalloc(sizeof(T) + sizeof(zend_object) + zend_object_properties_size(ce))); auto zobj = to_zend_object(obj); zobj->handlers = init(obj); zend_object_std_init(zobj, ce); object_properties_init(zobj, ce); return zobj; } template zend_always_inline zend_object *object_create_ctor(zend_class_entry *ce, zend_object_handlers *handlers, Ts&&... args) { // If only I can use C++20 here... return object_create(ce, [args = std::forward_as_tuple(args...), handlers](auto obj) { std::apply([obj](auto&&... args) { new(obj) T(args...); }, std::move(args)); return handlers; }); } zend_always_inline zend_object *object_create(zend_class_entry *ce, const zend_object_handlers *handlers) { auto zobj = reinterpret_cast(emalloc( sizeof(zend_object) + zend_object_properties_size(ce))); zobj->handlers = handlers; zend_object_std_init(zobj, ce); object_properties_init(zobj, ce); return zobj; } template zend_always_inline void object_destroy(zend_object *zobj) { zend_object_std_dtor(zobj); to_native_object(zobj)->~T(); } zend_always_inline void object_set_property(zend_object *obj, size_t idx, zval *val) { zval_ptr_dtor(OBJ_PROP_NUM(obj, idx)); ZVAL_COPY(OBJ_PROP_NUM(obj, idx), val); } zend_always_inline zend_object *object_non_constructible(zend_class_entry *ce) { throw_error("class %s is not explicitly constructible.", ZSTR_VAL(ce->name)); return zend_objects_new(ce); } /// Helper functions for handling scalar types. template struct false_type : std::false_type {}; template zend_always_inline constexpr void bad_scalar_type() { static_assert(false_type::value, "type should be zend_long, double or cx_double"); } template zend_always_inline bool zval_is_scalar(zval *zv) { bad_scalar_type(); return false; } template <> zend_always_inline bool zval_is_scalar(zval *zv) { return Z_TYPE_P(zv) == IS_DOUBLE; } template <> zend_always_inline bool zval_is_scalar(zval *zv) { return Z_TYPE_P(zv) == IS_LONG; } template zend_always_inline const char *scalar_type_name() { bad_scalar_type(); return nullptr; } template <> zend_always_inline const char *scalar_type_name() { return zend_get_type_by_const(IS_DOUBLE); } template <> zend_always_inline const char *scalar_type_name() { return zend_get_type_by_const(IS_LONG); } template zend_always_inline T zval_get_scalar(zval *zv) { bad_scalar_type(); } template <> zend_always_inline double zval_get_scalar(zval *zv) { return Z_DVAL_P(zv); } template <> zend_always_inline zend_long zval_get_scalar(zval *zv) { return Z_LVAL_P(zv); } zend_always_inline void zval_set_scalar(zval *zv, double val) { ZVAL_DOUBLE(zv, val); } zend_always_inline void zval_set_scalar(zval *zv, zend_long val) { ZVAL_LONG(zv, val); } zend_always_inline const char *zval_get_type_name(zval *zv) { if (Z_TYPE_P(zv) == IS_OBJECT) { return ZSTR_VAL(Z_OBJCE_P(zv)->name); } else { return zend_get_type_by_const(Z_TYPE_P(zv)); } } template zend_always_inline bool zval_check_scalar(zval *zv) { if (UNEXPECTED(!zval_is_scalar(zv))) { ex_bad_type(scalar_type_name(), zval_get_type_name(zv)); return false; } return true; } // Helper functions for handling object type names. template zend_always_inline char *get_type_name() { return ZSTR_VAL(T::ce->name); } } #endif // !PHP_ARMA_HH