phpc++templatesswiglanguage-binding

template type as struct data member in SWIG


I am writing a PHP wrapper for a C++ library using SWIG, but I am having trouble using a structure with an instance of a template type as a data member.

Suppose I have the following header file:

template <typename>
struct myvector
{
};

struct S
{
    myvector<int> v;
};

myvector<int> foo();
S bar();

and interface file:

%module test
%{
#include "test.hpp"
%}

%include "test.hpp"
%template(IntVec) myvector<int>;

When I try to use a function that directly returns an myvector, it works fine:

$v1 = test::foo();

However, when I try to use a function that returns an S object, and try to access its data member which is of type myvector:

$s = test::bar();
$v2 = $s->v;

I get the following error at runtime:

PHP Fatal error:  Class 'myvectorT_int_t' not found in test.php on line 145

I am probably missing something from my interface file, but I don't know what. Can anyone help?


Solution

  • As far as I can make out this is a SWIG bug. Someone else has already reported it in fact. Fortunately there's a simple, reliable workaround via PHP's class_alias:

    %module test
    %{
    #include "test.h"
    %}
    
    %pragma(php) code="
    # This code is inserted as a workaround for template bugs with SWIG
    class_alias('IntVec', 'myvectorT_int_t');
    "
    
    %include "test.h"
    %template(IntVec) myvector<int>;
    

    The pragma here inserts the code to setup the alias at the start of the generated PHP file.

    (There's another possible work around too - rather than using public member variables access via getter/setter functions works as expected)

    The bug report also mentions another possible workaround although I'm not keen on that since it requires using a rather ugly name for the template type.


    Justification for bug assumption

    The code for __get includes:

    $c=substr(get_resource_type($r), (strpos(get_resource_type($r), '__') ? strpos(get_resource_type($r), '__') + 2 : 3));
                            return new $c($r);
    

    When you get here $c is set to myvectorT_int_t which would be correct except for the %template directive.

    When we add a myvector<int> get() function to S the generated code results in:

     $c=substr(get_resource_type($r), (strpos(get_resource_type($r), '__') ? strpos(get_resource_type($r), '__') + 2 : 3));
     if (!class_exists($c)) {
         return new IntVec($r);
     }
     return new $c($r);
    

    which crucially includes the generic code that would be correct without the %template and as special check to see if it's actually an IntVec.

    There's also a comment in the Source/Modules/php.cxx:

    // FIXME: Currently we always use call_user_func for __get, so we can
    // check and wrap the result.  This is needless if all the properties
    // are primitive types.  Also this doesn't handle all the cases which
    // a method returning an object does.
    

    Finally the code generated by the same interface file for Java is correct.