Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PHP-CPP class implementation #150

Closed
taylorren opened this Issue Dec 14, 2014 · 6 comments

Comments

Projects
None yet
3 participants
@taylorren
Copy link

taylorren commented Dec 14, 2014

C++ source code:

#include <phpcpp.h>
#include <cmath>
#include <iostream>
#include <sstream>

class Complex : public Php::Base
{
    private:
        double r=0, i=0;

    public:
        Complex() {}

        virtual ~Complex() {}

        Php::Value getReal()
        {
            return r;
        }

        Php::Value getImage()
        {
            return i;
        }

        void __construct(Php::Parameters &params)
        {
            if(params.size()==2)
            {
                r=params[0];
                i=params[1];
            }
            else
            {
                r=0;
                i=0;
            }
        }

        Php::Value mod() const
        {
            return (double)sqrt(r*r+i*i);
        }

        Php::Value __toString()
        {
            std::ostringstream os;
            os<<r;
            if(i>=0)
                os<<'+';

            os<<i<<'i';

            return os.str();
        }

        Php::Value add(Php::Parameters &params)
        {
            Php::Value t=params[0];
            Complex *a=(Complex *)t.implementation();

            r+=(double)a->getReal();
            i+=(double)a->getImage();

            return this;
        }

        Php::Value sub(Php::Parameters &params)
        {
            Php::Value t=params[0];
            Complex *a=(Complex *)t.implementation();

            r-=(double)a->getReal();
            i-=(double)a->getImage();

            return this;
        }
};

/**
 *  tell the compiler that the get_module is a pure C function
 */
extern "C" {

    /**
     *  Function that is called by PHP right after the PHP process
     *  has started, and that returns an address of an internal PHP
     *  strucure with all the details and features of your extension
     *
     *  @return void*   a pointer to an address that is understood by PHP
     */
    PHPCPP_EXPORT void *get_module() 
    {
        // static(!) Php::Extension object that should stay in memory
        // for the entire duration of the process (that's why it's static)
        static Php::Extension extension("complex", "0.0.1");

        //Php::Namespace myNamespace("trComplex");
        Php::Class<Complex> complex("Complex");

        complex.method("mod", &Complex::mod, {});
        complex.method("real", &Complex::getReal, {});
        complex.method("image", &Complex::getImage, {});
        complex.method("__construct", &Complex::__construct);
        complex.method("add", &Complex::add, {
            Php::ByVal("op", "Complex", false, true)
        });
        complex.method("sub", &Complex::sub, {
            Php::ByVal("op", "Complex", false, true)
        });




        //myNamespace.add(std::move(complex));

        //extension.add(std::move(myNamespace));
        extension.add(std::move(complex));

        // return the extension
        return extension;
    }
}

And the test script:

<?php

use Complex;

$c=new Complex(-3,-4);
echo $c->mod()."\n";

$d=new Complex(4,3);
echo ((string)$d."\n");
echo $d->mod()."\n";

$e=new Complex(-4,-3);
echo ((string)$e."\n");
echo "=============================\n";

$a=new Complex(1,1);
$b=new Complex(1,2);

$res=new Complex();
echo ((string)$res)."\n";
$res=$a->add($b);

echo ($res->real())."\n";
echo ($res->image())."\n";

echo ((string)$res);
echo "=============================\n";

//echo ((string)$res);

real(), image() returns correctly, which means the add function works. But to output the string representation fails:

PHP Catchable fatal error: Object of class Complex could not be converted to string in /vagrant/complex/test/testComplex.php on line 26

Also, if I want to chain add() then sub(), it prompts a segmentation fault.

I have written an email to emiel but so far no hint yet.

Please help.

@valmat

This comment has been minimized.

Copy link
Contributor

valmat commented Dec 14, 2014

Hello, @taylorren
Error on line 26 will disappear, if you add the line

complex.method("__toString", &Complex::__toString);

I think I know why this is happening. I will be able to see more tomorrow night.
In short, __toString is defined at compile time. dynamic_cast "erases" this information.

@valmat

This comment has been minimized.

Copy link
Contributor

valmat commented Dec 14, 2014

About error PHP Catchable fatal error: Object of class Complex could not be converted to string...
I just looked.
The best that I came up with is to replace

    Class(const char *name, int flags = 0) : ClassBase(name, flags) {}

in include/class.h
on

    Class(const char *name, int flags = 0) : ClassBase(name, flags) 
    {
        method("__toString", &T::__toString);
    }

This is an ugly solution.
Correct would be to detect at compile time whether contains a custom class the method __toString.
And if contains, then in class ClassImpl assign to the (zend_class_entry)entry.(zend_function *)__tostring the appropriate value.
But it requires to rewrite too much code, so I'm not yet able to help implement this option.

For compile time detect we could use something like this:
include/classbase.h

    virtual bool castableToString() const { return false; }

include/class.h

    template <typename X>
    class HasToString
    {
        typedef char yes;
        typedef struct {char _[2];} no;
        template <typename C> static yes test( decltype(&C::__toString) ) ;
        template <typename C> static no test(...);
    public:
        static const constexpr bool value = sizeof(test<X>(0)) == sizeof(yes);
    };
    virtual bool castableToString() const override
    {
        return HasToString<T>::value;
    }
@taylorren

This comment has been minimized.

Copy link
Author

taylorren commented Dec 15, 2014

Thanks. I will do some further check later today.

@schoentoon

This comment has been minimized.

Copy link
Contributor

schoentoon commented Dec 15, 2014

The __toString() you don't have to register is marked as const, yours isn't. Therefore you aren't overriding it and it will still call the default implementation which throws that you didn't implement it. Simply marking your __toString() as const should solve this issue.

@valmat

This comment has been minimized.

Copy link
Contributor

valmat commented Dec 15, 2014

@schoentoon the method is overridden by name, regardless of its argument. Therefore const modifier does not help. You can verify this, if you check it out. Here the reason is not in this.
In this example, when we do (string)$a->add($b) Zend engine tries to find a realization __toString in the structure zend_class_entry. But it is not there.

@taylorren

This comment has been minimized.

Copy link
Author

taylorren commented Dec 15, 2014

Exporting __toString method in Complex will solve the issue described above:

$res=$a->add($b);
echo ((string)$res);

So I will close this thread and open a new one particularly on the chain of methods.

Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.