Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Enforce types SWIG

Tags:

c++

php

swig

So I have the following C++

#include <stdio.h>
#include <iostream>
#include <vector>
using namespace std;

int hello(char *str);

int hello(char *str) {
    cout << "Hello World: " << str << endl;
    return 0;
}

And the following swig interface

%module sphp

%{
extern int hello(char *str);
%}

extern int hello(char *str);

And I can compile and use this in php,

php> hello("testing!");

This all is wicked!

Only problem is

php> hello(3);

is still valid. I don't want this, it seems swig silently casts types

  /*@SWIG:/usr/share/swig2.0/php/utils.i,62,CONVERT_STRING_IN@*/
  if ((*args[0])->type==IS_NULL) {
    arg1 = (char *) 0;
  } else {
    convert_to_string_ex(args[0]);
    arg1 = (char *) Z_STRVAL_PP(args[0]);
  }
  /*@SWIG@*/;

Now I don't want to be editing the wrapper, because its auto generated. Is there a way I can turn off this silent conversion, so that hello(3) will throw exceptions or errors, or can I give hello a hint about the type of the php argument it was originally passed?

like image 455
Jakob Bowyer Avatar asked Dec 30 '13 16:12

Jakob Bowyer


1 Answers

Sadly, because of how the wrapper is generated, you can't get rid of the casting completely. You can however, intercept basic datatypes and redirect them to a template function like so:

%module sphp

%{
  #include <iostream>

  extern int hello(char *str);

  template<class T> 
  int hello(const T &)
  {
    std::cout << "unsupported data type" << std::endl; 
  }
%}

extern int hello(char *str);

template<class T> 
int hello(const T &);

%template(hello) hello<int>;

To intercept more data types, simply add a new operator overload:

%template(hello) hello<double>;

Update:

Here's a more difficult approach that works by overriding SWIG's typemaps. This is taken from SWIG's PHP typemaps and modified to prevent it from casting values.

%module sphp

%{
  extern int hello(char *str);
%}

%typemap(in) char *
{
  if ((*$input)->type != IS_STRING)
    SWIG_PHP_Error(E_ERROR, "Type error in argument $argnum of $symname. Expected string");
   $1 = ($1_ltype) Z_STRVAL_PP($input);
}

%typemap(in) (char *STRING, int LENGTH), (char *STRING, size_t LENGTH) {
  if ((*$input)->type != IS_STRING)
    SWIG_PHP_Error(E_ERROR, "Type error in argument $argnum of $symname. Expected string");
   $1 = ($1_ltype) Z_STRVAL_PP($input);
   $2 = ($2_ltype) Z_STRLEN_PP($input);
}

extern int hello(char *str);

Output:

php > include ("sphp.php");
php > hello("test");
Hello World: test
php > hello(3);
PHP Fatal error:  Type error in argument 1 of hello. Expected string in php shell code on line 1
php > hello([]);
PHP Fatal error:  Type error in argument 1 of hello. Expected string in php shell code on line 1
php > 

This is more tedious since you'll have to override every single type in the same manner if you want to get rid of automatic argument casting completely, on the other hand it gives you more control over the behaviour of argument forwarding.

like image 188
voodooattack Avatar answered Sep 27 '22 19:09

voodooattack