Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 578
HighCommander4 Avatar asked May 04 '12 21:05

HighCommander4


1 Answers

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.

like image 80
Flexo Avatar answered Nov 18 '22 23:11

Flexo