Fix bugs. Add object handler for `Complex`.

This commit is contained in:
CismonX 2019-03-15 23:25:24 +08:00
parent 44bd1dbcce
commit 88136e9900
5 changed files with 127 additions and 50 deletions

2
.gitattributes vendored
View File

@ -1 +1 @@
*.h linguist-language=C
stubs/* linguist-documentation

View File

@ -4,7 +4,7 @@ PHP_ARG_ENABLE(arma, for armadillo support,
if test "$PHP_ARMA" != "no"; then
PHP_REQUIRE_CXX()
ARMA_SRC=" \
src/arma.cc \
src/php_arma.cc \
src/interfaces.cc \
src/fill.cc \
src/complex.cc"

View File

@ -4,15 +4,10 @@
// @Author CismonX
//
#include "php_arma.hh"
#include "complex.hh"
namespace php_arma
{
PHP_ARMA_START_METHOD_ENTRY_1(complex, T)
PHP_ARMA_ME_1(Complex, T, __construct, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)
PHP_ARMA_END_METHOD_ENTRY_1(complex, T)
template <typename T>
PHP_METHOD(Complex, __construct)
{
@ -24,7 +19,7 @@ namespace php_arma
ZEND_PARSE_PARAMETERS_END();
auto num_args = EX_NUM_ARGS();
auto has_imag = num_args > 1;
auto has_imag = num_args == 2;
auto has_real = num_args > 0;
zval zero_val;
@ -33,10 +28,14 @@ namespace php_arma
zval *current = getThis();
if (EXPECTED(has_real)) {
zval_check_scalar<T>(real);
if (UNEXPECTED(!zval_check_scalar<T>(real))) {
return;
};
object_set_property(Z_OBJ_P(current), 0, real);
if (EXPECTED(has_imag)) {
zval_check_scalar<T>(imag);
if (UNEXPECTED(!zval_check_scalar<T>(imag))) {
return;
};
object_set_property(Z_OBJ_P(current), 1, imag);
} else {
object_set_property(Z_OBJ_P(current), 1, &zero_val);
@ -45,17 +44,54 @@ namespace php_arma
object_set_property(Z_OBJ_P(current), 0, &zero_val);
object_set_property(Z_OBJ_P(current), 1, &zero_val);
}
Z_OBJ_P(current)->handlers = PHP_ARMA_HANDLERS(complex, T);
}
PHP_ARMA_START_ME_1(complex, T)
PHP_ARMA_ME(Complex, __construct, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR, T)
PHP_ARMA_END_ME_1(complex, T)
template <typename T>
void complex_write_property(zval* obj, zval* member, zval* value, void** unused)
{
// Theoretically we won't get non-string values here, add type check just in case.
if (UNEXPECTED(Z_TYPE_P(member) != IS_STRING)) {
zend_throw_exception(zend_ce_error, "Unexpected error. Please contact developer.", -2);
return;
}
if (UNEXPECTED(!zval_check_scalar<T>(value))) {
return;
};
if (strcmp(Z_STRVAL_P(member), "real") == 0) {
object_set_property(Z_OBJ_P(obj), 0, value);
return;
}
if (strcmp(Z_STRVAL_P(member), "imag") == 0) {
object_set_property(Z_OBJ_P(obj), 1, value);
return;
}
zend_throw_exception_ex(zend_ce_exception, -2,
"Bad property name for %s, expected 'real' or 'imag', '%s' given.", Z_OBJNAME_P(obj), Z_STRVAL_P(member));
}
void complex_init()
{
complex_ce = interface_register("Complex");
cx_double_ce = class_register("CxDouble", PHP_ARMA_METHOD_1(complex, double), complex_ce);
property_declare(cx_double_ce, "real");
property_declare(cx_double_ce, "imag");
PHP_ARMA_CE(complex, double) = class_register("CxDouble", PHP_ARMA_METHODS(complex, double), complex_ce);
property_declare(PHP_ARMA_CE(complex, double), "real");
property_declare(PHP_ARMA_CE(complex, double), "imag");
object_handlers_init(PHP_ARMA_HANDLERS(complex, double));
PHP_ARMA_HANDLER_SET(complex, write_property, complex_write_property, double);
}
zend_class_entry *complex_ce;
zend_class_entry *cx_double_ce;
template <typename T>
PHP_ARMA_DEFINE_CE(complex, T);
template <typename T>
PHP_ARMA_DEFINE_OBJECT_HANDLER(complex, T);
}

View File

@ -7,7 +7,7 @@
#ifndef PHP_ARMA_COMPLEX_HH
#define PHP_ARMA_COMPLEX_HH
#include <php.h>
#include "php_arma.hh"
namespace php_arma
{
@ -17,7 +17,21 @@ namespace php_arma
void complex_init();
extern zend_class_entry *complex_ce;
extern zend_class_entry *cx_double_ce;
template <typename T>
PHP_ARMA_DECLARE_CE_HANDLERS(complex);
template <> zend_always_inline
bool zval_is_scalar<cx_double>(zval *zv)
{
return Z_TYPE_P(zv) == IS_OBJECT && instanceof_function(Z_OBJCE_P(zv), PHP_ARMA_CE(complex, double));
}
template <> zend_always_inline
const char *scalar_type_name<cx_double>()
{
return ZSTR_VAL(PHP_ARMA_CE(complex, double)->name);
}
}
#endif //!PHP_ARMA_COMPLEX_HH

View File

@ -11,8 +11,6 @@
#include "config.h"
#endif
#include "complex.hh"
#include <php.h>
#include <zend_exceptions.h>
#include <complex>
@ -27,26 +25,59 @@ extern zend_module_entry arma_module_entry;
typedef std::complex<double> cx_double;
/// Helper functions.
/// Helper macros for method entry.
#define PHP_ARMA_START_METHOD_ENTRY_1(name, t1) \
template <typename t1> \
struct name##_methods \
{ \
#define Z_OBJNAME_P(zval_p) \
ZSTR_VAL(Z_OBJCE_P(zval_p)->name)
#define PHP_ARMA_START_ME_1(name, t1) \
template <typename t1> \
struct name##_me \
{ \
static constexpr zend_function_entry const val[] = {
#define PHP_ARMA_END_METHOD_ENTRY_1(name, t1) \
PHP_FE_END \
}; \
}; \
template <typename t1> \
constexpr zend_function_entry const name##_methods<t1>::val[];
#define PHP_ARMA_END_ME_1(name, t1) \
PHP_FE_END \
}; \
}; \
template <typename t1> \
constexpr zend_function_entry const name##_me<t1>::val[];
#define PHP_ARMA_ME_1(cls, t1, name, flags) \
ZEND_FENTRY(name, ZEND_MN(cls##_##name<t1>), nullptr, flags)
#define PHP_ARMA_ME(cls, func, flags, ...) \
ZEND_FENTRY(func, ZEND_MN(cls##_##func<__VA_ARGS__>), nullptr, flags)
#define PHP_ARMA_METHOD_1(name, t1) \
name##_methods<t1>::val
#define PHP_ARMA_METHODS(name, ...) \
name##_me<__VA_ARGS__>::val
/// Helper macros for class entry and object handlers
#define PHP_ARMA_DECLARE_CE(name) \
struct name##_components \
{ \
static zend_class_entry *ce; \
}
#define PHP_ARMA_DECLARE_CE_HANDLERS(name) \
struct name##_components \
{ \
static zend_class_entry *ce; \
static zend_object_handlers handlers; \
}
#define PHP_ARMA_DEFINE_CE(name, ...) \
zend_class_entry *name##_components<__VA_ARGS__>::ce
#define PHP_ARMA_DEFINE_OBJECT_HANDLER(name, ...) \
zend_object_handlers name##_components<__VA_ARGS__>::handlers
#define PHP_ARMA_CE(name, ...) \
name##_components<__VA_ARGS__>::ce
#define PHP_ARMA_HANDLERS(name, ...) \
&name##_components<__VA_ARGS__>::handlers
#define PHP_ARMA_HANDLER_SET(name, handler, func, ...) \
(PHP_ARMA_HANDLERS(name, __VA_ARGS__))->handler = func<__VA_ARGS__>
namespace php_arma
{
@ -107,6 +138,12 @@ namespace php_arma
zend_declare_property_null(ce, name, strlen(name), ZEND_ACC_PUBLIC);
}
zend_always_inline
void object_handlers_init(zend_object_handlers *handlers)
{
memcpy(handlers, &std_object_handlers, sizeof(zend_object_handlers));
}
template <typename T>
zend_object *to_zend_object(T *obj)
{
@ -125,10 +162,10 @@ namespace php_arma
{
auto obj = reinterpret_cast<T*>(ecalloc(1,
sizeof(T) + sizeof(zend_object) + zend_object_properties_size(ce)));
init(obj);
auto handlers = init(obj);
auto zobj = to_zend_object(obj);
zend_object_std_init(zobj, ce);
zobj->handlers = &std_object_handlers;
zobj->handlers = handlers ? handlers : &std_object_handlers;
return zobj;
}
@ -192,12 +229,6 @@ namespace php_arma
return Z_TYPE_P(zv) == IS_LONG;
}
template <> zend_always_inline
bool zval_is_scalar<cx_double>(zval *zv)
{
return Z_TYPE_P(zv) == IS_OBJECT && instanceof_function(Z_OBJCE_P(zv), cx_double_ce);
}
template <typename>
struct scalar_type {};
@ -234,12 +265,6 @@ namespace php_arma
return zend_get_type_by_const(IS_LONG);
}
template <> zend_always_inline
const char *scalar_type_name<cx_double>()
{
return ZSTR_VAL(cx_double_ce->name);
}
zend_always_inline
const char *zval_get_type_name(zval *zv)
{
@ -253,15 +278,17 @@ namespace php_arma
zend_always_inline
void ex_bad_type(const char *expected, const char *got)
{
zend_throw_exception_ex(zend_ce_type_error, -1, "Bad type. Expected %s, got %s.", expected, got);
zend_throw_exception_ex(zend_ce_type_error, -1, "Bad type, expected %s, %s given.", expected, got);
}
template <typename T>
void zval_check_scalar(zval *zv)
bool zval_check_scalar(zval *zv)
{
if (!zval_is_scalar<T>(zv)) {
ex_bad_type(scalar_type_name<T>(), zval_get_type_name(zv));
return false;
}
return true;
}
zend_always_inline