diff --git a/.travis.yml b/.travis.yml index 201e4e4..44a4744 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,6 +27,6 @@ install: script: - phpize - - ./configure --enable-arma-operator + - ./configure --enable-arma-operators - make - make test diff --git a/config.m4 b/config.m4 index d6277c2..1e790aa 100644 --- a/config.m4 +++ b/config.m4 @@ -1,8 +1,8 @@ -PHP_ARG_ENABLE(arma, for armadillo support, -[ --enable-arma Enable armadillo support]) +PHP_ARG_ENABLE(arma, for armadillo support, +[ --enable-arma Enable armadillo support]) -PHP_ARG_ENABLE(arma-operator, for operator overloading support in armadillo, -[ --enable-arma-operator Enable operator overloading for armadillo ], no, no) +PHP_ARG_ENABLE(arma-operators, for operator overloading support in armadillo, +[ --enable-arma-operators Enable operator overloading for armadillo ], no, no) if test "$PHP_ARMA" != "no"; then PHP_REQUIRE_CXX() @@ -15,9 +15,9 @@ if test "$PHP_ARMA" != "no"; then src/mapval.cc \ " - if test "$PHP_ARMA_OPERATOR" != "no"; then - AC_DEFINE(PHP_ARMA_OPERATOR, 1, [ Defined if operator overloading is enabled for armadillo. ]) - ARMA_SRC+="src/operator.cc" + if test "$PHP_ARMA_OPERATORS" != "no"; then + AC_DEFINE(PHP_ARMA_OPERATORS, 1, [ Defined if operator overloading is enabled for armadillo. ]) + ARMA_SRC+="src/operators.cc" fi PHP_NEW_EXTENSION(arma, $ARMA_SRC, $ext_shared, , -std=c++17) diff --git a/src/complex.cc b/src/complex.cc index 9d19852..bce5443 100644 --- a/src/complex.cc +++ b/src/complex.cc @@ -77,19 +77,9 @@ namespace php_arma Z_PARAM_ZVAL(other) ZEND_PARSE_PARAMETERS_END(); - auto current = zval_get_scalar(getThis()); - - if (zval_is_scalar(other)) { - zval_set_scalar(return_value, current + zval_get_scalar(other)); - return; + if (!operators::add(getThis(), other, return_value)) { + ex_bad_type(other); } - - if (zval_is_scalar(other)) { - zval_set_scalar(return_value, current + zval_get_scalar(other)); - return; - } - - ex_bad_type(other); } template @@ -100,19 +90,9 @@ namespace php_arma Z_PARAM_ZVAL(other) ZEND_PARSE_PARAMETERS_END(); - auto current = zval_get_scalar(getThis()); - - if (zval_is_scalar(other)) { - zval_set_scalar(return_value, current - zval_get_scalar(other)); - return; + if (!operators::sub(getThis(), other, return_value)) { + ex_bad_type(other); } - - if (zval_is_scalar(other)) { - zval_set_scalar(return_value, current - zval_get_scalar(other)); - return; - } - - ex_bad_type(other); } template @@ -131,19 +111,9 @@ namespace php_arma Z_PARAM_ZVAL(other) ZEND_PARSE_PARAMETERS_END(); - auto current = zval_get_scalar(getThis()); - - if (zval_is_scalar(other)) { - zval_set_scalar(return_value, current * zval_get_scalar(other)); - return; + if (!operators::mul(getThis(), other, return_value)) { + ex_bad_type(other); } - - if (zval_is_scalar(other)) { - zval_set_scalar(return_value, current * zval_get_scalar(other)); - return; - } - - ex_bad_type(other); } template @@ -154,19 +124,9 @@ namespace php_arma Z_PARAM_ZVAL(other) ZEND_PARSE_PARAMETERS_END(); - auto current = zval_get_scalar(getThis()); - - if (zval_is_scalar(other)) { - zval_set_scalar(return_value, current / zval_get_scalar(other)); - return; + if (!operators::div(getThis(), other, return_value)) { + ex_bad_type(other); } - - if (zval_is_scalar(other)) { - zval_set_scalar(return_value, current / zval_get_scalar(other)); - return; - } - - ex_bad_type(other); } template @@ -196,9 +156,7 @@ namespace php_arma template PHP_ARMA_METHOD(complex, conj, T) { - auto current = zval_get_scalar(getThis()); - - zval_set_scalar(return_value, std::conj(current)); + operators::conj(getThis(), nullptr, return_value); } template @@ -233,19 +191,9 @@ namespace php_arma Z_PARAM_ZVAL(other) ZEND_PARSE_PARAMETERS_END(); - auto current = zval_get_scalar(getThis()); - - if (zval_is_scalar(other)) { - zval_set_scalar(return_value, std::pow(current, zval_get_scalar(other))); - return; + if (!operators::pow(getThis(), other, return_value)) { + ex_bad_type(other); } - - if (zval_is_scalar(other)) { - zval_set_scalar(return_value, std::pow(current, zval_get_scalar(other))); - return; - } - - ex_bad_type(other); } template diff --git a/src/complex.hh b/src/complex.hh index 961eb11..4e06acd 100644 --- a/src/complex.hh +++ b/src/complex.hh @@ -11,6 +11,24 @@ #include +#ifdef PHP_ARMA_OPERATORS + +#define PHP_ARMA_COMPLEX_OPERATOR(type, func) \ + if (instanceof_function(_ce, complex::ce)) { \ + return complex::operators::func(zv1, zv2, retval); \ + } + +#define PHP_ARMA_COMPLEX_OPERATOR_ASSIGN(type, func) \ + if (instanceof_function(_ce, complex::ce)) { \ + auto v = complex::operators::func(zv1, zv2, zv1); \ + if (EX(opline)->result_type != IS_UNUSED) { \ + ZVAL_COPY(retval, zv1); \ + } \ + return v; \ + } + +#endif // PHP_ARMA_OPERATORS + using cx_double = std::complex; namespace php_arma @@ -20,6 +38,8 @@ namespace php_arma { using native_t = std::complex; + struct operators; + friend void complex_init(); PHP_ARMA_CE_HANDLRES_DECLARE(); @@ -114,6 +134,102 @@ namespace php_arma ex_bad_type(expected, got); free(expected); } + + template + struct complex::operators + { + zend_always_inline + static bool add(zval *zv1, zval *zv2, zval *retval) + { + auto v1 = zval_get_scalar(zv1); + + if (zval_is_scalar(zv2)) { + zval_set_scalar(retval, v1 + zval_get_scalar(zv2)); + return true; + } + if (zval_is_scalar(zv2)) { + zval_set_scalar(retval, v1 + zval_get_scalar(zv2)); + return true; + } + + return false; + } + + zend_always_inline + static bool sub(zval *zv1, zval *zv2, zval *retval) + { + auto v1 = zval_get_scalar(zv1); + + if (zval_is_scalar(zv2)) { + zval_set_scalar(retval, v1 - zval_get_scalar(zv2)); + return true; + } + if (zval_is_scalar(zv2)) { + zval_set_scalar(retval, v1 - zval_get_scalar(zv2)); + return true; + } + + return false; + } + + zend_always_inline + static bool mul(zval *zv1, zval *zv2, zval *retval) + { + auto v1 = zval_get_scalar(zv1); + + if (zval_is_scalar(zv2)) { + zval_set_scalar(retval, v1 * zval_get_scalar(zv2)); + return true; + } + if (zval_is_scalar(zv2)) { + zval_set_scalar(retval, v1 * zval_get_scalar(zv2)); + return true; + } + + return false; + } + + zend_always_inline + static bool div(zval *zv1, zval *zv2, zval *retval) + { + auto v1 = zval_get_scalar(zv1); + + if (zval_is_scalar(zv2)) { + zval_set_scalar(retval, v1 / zval_get_scalar(zv2)); + return true; + } + if (zval_is_scalar(zv2)) { + zval_set_scalar(retval, v1 / zval_get_scalar(zv2)); + return true; + } + + return false; + } + + zend_always_inline + static bool pow(zval *zv1, zval *zv2, zval *retval) + { + auto v1 = zval_get_scalar(zv1); + + if (zval_is_scalar(zv2)) { + zval_set_scalar(retval, std::pow(v1, zval_get_scalar(zv2))); + return true; + } + if (zval_is_scalar(zv2)) { + zval_set_scalar(retval, std::pow(v1, zval_get_scalar(zv2))); + return true; + } + + return false; + } + + zend_always_inline + static bool conj(zval *zv, zval *unused, zval *retval) + { + zval_set_scalar(retval, std::conj(zval_get_scalar(zv))); + return true; + } + }; } #endif // !PHP_ARMA_COMPLEX_HH diff --git a/src/constants.cc b/src/constants.cc index fd06641..cff601b 100644 --- a/src/constants.cc +++ b/src/constants.cc @@ -4,7 +4,6 @@ // @Author CismonX // -#include "php_arma.hh" #include "constants.hh" #include @@ -52,5 +51,8 @@ namespace php_arma std::tuple("APPEND", hdf5_opts::append), std::tuple("REPLACE", hdf5_opts::replace) ); + const_declare(features_ce = abstract_class_register(), + std::tuple("OPERATORS", features::operators) + ); } } diff --git a/src/constants.hh b/src/constants.hh index faa125e..14ca5a1 100644 --- a/src/constants.hh +++ b/src/constants.hh @@ -7,7 +7,7 @@ #ifndef PHP_ARMA_CONSTANTS_HH #define PHP_ARMA_CONSTANTS_HH -#include +#include "php_arma.hh" namespace php_arma { @@ -49,17 +49,28 @@ namespace php_arma static constexpr auto replace = 1u << 2; }; + struct features + { +#ifdef PHP_ARMA_OPERATORS + static constexpr auto operators = true; +#else + static constexpr auto operators = false; +#endif // PHP_ARMA_OPERATORS + }; + void constants_init(); constexpr const char fill_php_name[] = "Fill"; constexpr const char file_type_php_name[] = "FileType"; constexpr const char sort_direction_php_name[] = "SortDirection"; constexpr const char hdf5_opts_php_name[] = "HDF5Opts"; + constexpr const char features_php_name[] = "Features"; inline zend_class_entry *fill_ce; inline zend_class_entry *file_type_ce; inline zend_class_entry *sort_direction_ce; inline zend_class_entry *hdf5_opts_ce; + inline zend_class_entry *features_ce; } #endif //!PHP_ARMA_CONSTANTS_HH diff --git a/src/operators.cc b/src/operators.cc index 919eaa5..07d6165 100644 --- a/src/operators.cc +++ b/src/operators.cc @@ -5,10 +5,9 @@ // #include "php_arma.hh" +#include "operators.hh" #include "complex.hh" -#include - #if PHP_VERSION_ID >= 70300 // See https://github.com/php/php-src/blob/PHP-7.3/UPGRADING.INTERNALS#L61 section 3.1.b. #define EX_CONSTANT(op) RT_CONSTANT(EX(opline), op) @@ -19,18 +18,18 @@ namespace php_arma zend_always_inline zval *get_zval_ptr_undef( zend_uchar op_type, - znode_op op, + const znode_op *op, zend_free_op *free_op, zend_execute_data *execute_data ) { switch (op_type) { case IS_TMP_VAR: case IS_VAR: - return *free_op = EX_VAR(op.var); + return *free_op = EX_VAR(op->var); case IS_CONST: - return EX_CONSTANT(op); + return EX_CONSTANT(*op); case IS_CV: - return EX_VAR(op.var); + return EX_VAR(op->var); default: return nullptr; } @@ -55,11 +54,15 @@ namespace php_arma const zend_op *opline = EX(opline); zend_free_op free_op1 = nullptr; zend_free_op free_op2 = nullptr; - zval *op1 = get_zval_ptr_undef(opline->op1_type, opline->op1, &free_op1, execute_data); - zval *op2 = get_zval_ptr_undef(opline->op2_type, opline->op2, &free_op2, execute_data); + zval *op1 = get_zval_ptr_undef(opline->op1_type, &opline->op1, &free_op1, execute_data); + zval *op2 = get_zval_ptr_undef(opline->op2_type, &opline->op2, &free_op2, execute_data); - ZVAL_DEREF(op1); - ZVAL_DEREF(op2); + if (op1) { + ZVAL_DEREF(op1); + } + if (op2) { + ZVAL_DEREF(op2); + } if (UNEXPECTED(opcode_is_greater(opline))) { std::swap(op1, op2); @@ -83,7 +86,7 @@ namespace php_arma int add_handler(zend_execute_data *execute_data) { return op_handler(execute_data, [] (auto zv1, auto zv2, auto retval) { - if (UNEXPECTED(Z_TYPE_P(zv1)) != IS_OBJECT) { + if (UNEXPECTED(Z_TYPE_P(zv1) != IS_OBJECT)) { return false; } @@ -97,8 +100,8 @@ namespace php_arma int add_assign_handler(zend_execute_data *execute_data) { - return op_handler(execute_data, [] (auto zv1, auto zv2, auto retval) { - if (UNEXPECTED(Z_TYPE_P(zv1)) != IS_OBJECT) { + return op_handler(execute_data, [execute_data] (auto zv1, auto zv2, auto retval) { + if (UNEXPECTED(Z_TYPE_P(zv1) != IS_OBJECT)) { return false; } @@ -113,7 +116,7 @@ namespace php_arma int sub_handler(zend_execute_data *execute_data) { return op_handler(execute_data, [] (auto zv1, auto zv2, auto retval) { - if (UNEXPECTED(Z_TYPE_P(zv1)) != IS_OBJECT) { + if (UNEXPECTED(Z_TYPE_P(zv1) != IS_OBJECT)) { return false; } @@ -127,8 +130,8 @@ namespace php_arma int sub_assign_handler(zend_execute_data *execute_data) { - return op_handler(execute_data, [] (auto zv1, auto zv2, auto retval) { - if (UNEXPECTED(Z_TYPE_P(zv1)) != IS_OBJECT) { + return op_handler(execute_data, [execute_data] (auto zv1, auto zv2, auto retval) { + if (UNEXPECTED(Z_TYPE_P(zv1) != IS_OBJECT)) { return false; } @@ -143,7 +146,7 @@ namespace php_arma int mul_handler(zend_execute_data *execute_data) { return op_handler(execute_data, [] (auto zv1, auto zv2, auto retval) { - if (UNEXPECTED(Z_TYPE_P(zv1)) != IS_OBJECT) { + if (UNEXPECTED(Z_TYPE_P(zv1) != IS_OBJECT)) { return false; } @@ -157,8 +160,8 @@ namespace php_arma int mul_assign_handler(zend_execute_data *execute_data) { - return op_handler(execute_data, [] (auto zv1, auto zv2, auto retval) { - if (UNEXPECTED(Z_TYPE_P(zv1)) != IS_OBJECT) { + return op_handler(execute_data, [execute_data] (auto zv1, auto zv2, auto retval) { + if (UNEXPECTED(Z_TYPE_P(zv1) != IS_OBJECT)) { return false; } @@ -173,7 +176,7 @@ namespace php_arma int div_handler(zend_execute_data *execute_data) { return op_handler(execute_data, [] (auto zv1, auto zv2, auto retval) { - if (UNEXPECTED(Z_TYPE_P(zv1)) != IS_OBJECT) { + if (UNEXPECTED(Z_TYPE_P(zv1) != IS_OBJECT)) { return false; } @@ -187,8 +190,8 @@ namespace php_arma int div_assign_handler(zend_execute_data *execute_data) { - return op_handler(execute_data, [] (auto zv1, auto zv2, auto retval) { - if (UNEXPECTED(Z_TYPE_P(zv1)) != IS_OBJECT) { + return op_handler(execute_data, [execute_data] (auto zv1, auto zv2, auto retval) { + if (UNEXPECTED(Z_TYPE_P(zv1) != IS_OBJECT)) { return false; } @@ -203,7 +206,7 @@ namespace php_arma int pow_handler(zend_execute_data *execute_data) { return op_handler(execute_data, [] (auto zv1, auto zv2, auto retval) { - if (UNEXPECTED(Z_TYPE_P(zv1)) != IS_OBJECT) { + if (UNEXPECTED(Z_TYPE_P(zv1) != IS_OBJECT)) { return false; } @@ -215,10 +218,25 @@ namespace php_arma }); } + int pow_assign_handler(zend_execute_data *execute_data) + { + return op_handler(execute_data, [execute_data] (auto zv1, auto zv2, auto retval) { + if (UNEXPECTED(Z_TYPE_P(zv1) != IS_OBJECT)) { + return false; + } + + PHP_ARMA_OPERATOR_BEGIN(Z_OBJCE_P(zv1), complex_ce) + PHP_ARMA_COMPLEX_OPERATOR_ASSIGN(double, pow) + PHP_ARMA_OPERATOR_END(); + + return false; + }); + } + int bw_not_handler(zend_execute_data *execute_data) { return op_handler(execute_data, [] (auto zv1, auto zv2, auto retval) { - if (UNEXPECTED(Z_TYPE_P(zv1)) != IS_OBJECT) { + if (UNEXPECTED(Z_TYPE_P(zv1) != IS_OBJECT)) { return false; } @@ -230,7 +248,7 @@ namespace php_arma }); } - void operator_init() + void operators_init() { zend_set_user_opcode_handler(ZEND_ADD, add_handler); zend_set_user_opcode_handler(ZEND_ASSIGN_ADD, add_assign_handler); @@ -241,6 +259,7 @@ namespace php_arma zend_set_user_opcode_handler(ZEND_DIV, div_handler); zend_set_user_opcode_handler(ZEND_ASSIGN_DIV, div_assign_handler); zend_set_user_opcode_handler(ZEND_POW, pow_handler); + zend_set_user_opcode_handler(ZEND_ASSIGN_POW, pow_assign_handler); zend_set_user_opcode_handler(ZEND_BW_NOT, bw_not_handler); } } diff --git a/src/operators.hh b/src/operators.hh index ed84494..9fb1877 100644 --- a/src/operators.hh +++ b/src/operators.hh @@ -9,7 +9,7 @@ namespace php_arma { - void operator_init(); + void operators_init(); } #endif // !PHP_ARMA_OPERATOR_HH diff --git a/src/php_arma.cc b/src/php_arma.cc index 4578bbb..2faa730 100644 --- a/src/php_arma.cc +++ b/src/php_arma.cc @@ -10,9 +10,9 @@ #include "base.hh" #include "mapval.hh" -#ifdef PHP_ARMA_OPERATOR -#include "operator.hh" -#endif // PHP_ARMA_OPERATOR +#ifdef PHP_ARMA_OPERATORS +#include "operators.hh" +#endif // PHP_ARMA_OPERATORS #include @@ -23,9 +23,9 @@ PHP_MINIT_FUNCTION(arma) php_arma::base_init(); php_arma::mapval_init(); -#ifdef PHP_ARMA_OPERATOR - php_arma::operator_init(); -#endif // PHP_ARMA_OPERATOR +#ifdef PHP_ARMA_OPERATORS + php_arma::operators_init(); +#endif // PHP_ARMA_OPERATORS return SUCCESS; } diff --git a/src/php_arma.hh b/src/php_arma.hh index e43f5b3..5e86dcc 100644 --- a/src/php_arma.hh +++ b/src/php_arma.hh @@ -65,6 +65,22 @@ extern zend_module_entry arma_module_entry; static constexpr const char val[] = name; \ } +#ifdef PHP_ARMA_OPERATORS + +/// Helper macros for handling operator overloading. + +#define PHP_ARMA_OPERATOR_BEGIN(ce, parent_ce) \ + { \ + auto _ce = ce; \ + if (instanceof_function(_ce, parent_ce)) { + +#define PHP_ARMA_OPERATOR_END() \ + return false; \ + } \ + } + +#endif // PHP_ARMA_OPERATORS + namespace php_arma { /// Helpers for compile-time string concatenation for better module startup performance. diff --git a/stubs/Features.php b/stubs/Features.php index b3d9bbc..953dbef 100644 --- a/stubs/Features.php +++ b/stubs/Features.php @@ -1 +1,18 @@ + --FILE-- + --FILE-- +--FILE-- +add($bar), $foo + $bar], + [$foo->sub($bar), $foo - $bar], + [$foo->mul($bar), $foo * $bar], + [$foo->div($bar), $foo / $bar], + [$foo->pow($bar), $foo ** $bar], + [$foo->conj(), ~$foo] +); + +$foo1 = $foo->add($bar); +$bar1 = $bar->pow($foo1); +$foo2 = $foo1->sub($bar1); +$bar2 = $bar1->div($foo2); +$foo3 = $foo2->mul($bar2); +$foo *= $bar /= $foo -= $bar **= $foo += $bar; + +batch_assert('operator overloading of Arma\\Complex', [$foo3, $foo]); + +?> +--EXPECT-- diff --git a/tests/includes/loaded.php b/tests/includes/loaded.php index a7ebd75..0cf8195 100644 --- a/tests/includes/loaded.php +++ b/tests/includes/loaded.php @@ -1,5 +1,14 @@