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?
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.
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.