To illustrate, suppose you had a simple C function like this:
To access this function from Python, a pair of Python API functions are used to convert integer values. For example:int factorial(int n);
The first function is used to convert the input argument from a Python integer object to C long. The second function is used to convert a value from C back into a Python integer object.long PyInt_AsLong(PyObject *obj); /* Python --> C */ PyObject *PyInt_FromLong(long x); /* C --> Python */
Inside the wrapper function, you might see these functions used like this:
PyObject *wrap_factorial(PyObject *self, PyObject *args) { int arg1; int result; PyObject *obj1; PyObject *resultobj; if (!PyArg_ParseTuple("O:factorial", &obj1)) return NULL; arg1 = PyInt_AsLong(obj1); result = factorial(arg1); resultobj = PyInt_FromLong(result); return resultobj; }
Every target language supported by SWIG has functions that work in a similar manner. For example, in Perl, the following functions are used:
In Tcl:IV SvIV(SV *sv); /* Perl --> C */ void sv_setiv(SV *sv, IV val); /* C --> Perl */
The precise details are not so important. What is important is that all of the underlying type conversion is handled by collections of utility functions and short bits of C code like this---you simply have to read the extension documentation for your favorite language to know how it works (an exercise left to the reader).int Tcl_GetLongFromObj(Tcl_Interp *interp, Tcl_Obj *obj, long *value); Tcl_Obj *Tcl_NewIntObj(long value);
At first glance, this code will look a little confusing. However, there is really not much to it. The first typemap (the "in" typemap) is used to convert a value from the target language to C. The second typemap (the "out" typemap) is used to convert in the other direction. The content of each typemap is a small fragment of C code that is inserted directly into the SWIG generated wrapper functions. Within this code, a number of special variables prefixed with a $ are expanded. These are really just placeholders for C variables that are generated in the course of creating the wrapper function. In this case, $input refers to an input object that needs to be converted to C and $result refers to an object that is going to be returned by a wrapper function. $1 refers to a C variable that has the same type as specified in the typemap declaration (an int in this example)./* Convert from Python --> C */ %typemap(in) int { $1 = PyInt_AsLong($input); } /* Convert from C --> Python */ %typemap(out) int { $result = PyInt_FromLong($1); }
A short example might make this a little more clear. If you were wrapping a function like this:
A wrapper function would look approximately like this:int gcd(int x, int y);
In this code, you can see how the typemap code has been inserted into the function. You can also see how the special $ variables have been expanded to match certain variable names inside the wrapper function. This is really the whole idea behind typemaps--they simply let you insert arbitrary code into different parts of the generated wrapper functions. Because arbitrary code can be inserted, it possible to completely change the way in which values are converted.PyObject *wrap_gcd(PyObject *self, PyObject *args) { int arg1; int arg2; int result; PyObject *obj1; PyObject *obj2; PyObject *resultobj; if (!PyArg_ParseTuple("OO:gcd", &obj1, &obj2)) return NULL; /* "in" typemap, argument 1 */ { arg1 = PyInt_AsLong(obj1); } /* "in" typemap, argument 2 */ { arg2 = PyInt_AsLong(obj2); } result = gcd(arg1,arg2); /* "out" typemap, return value */ { resultobj = PyInt_FromLong(result); } return resultobj; }
The matching of typemaps to C datatypes is more than a simple textual match. In fact, typemaps are fully built into the underlying type system. Therefore, typemaps are unaffected by typedef, namespaces, and other declarations that might hide the underlying type. For example, you could have code like this:/* Convert from Perl --> C */ %typemap(in) int { $1 = SvIV($input); } ... int factorial(int n); int gcd(int x, int y); int count(char *s, char *t, int max);
In this case, the typemap is still applied to the proper arguments even though typenames don't always match the text "int". This ability to track types is a critical part of SWIG--in fact, all of the target language modules work merely define a set of typemaps for the basic types. Yet, it is never necessary to write new typemaps for typenames introduced by typedef./* Convert from Ruby--> C */ %typemap(in) int { $1 = NUM2INT($input); } ... typedef int Integer; namespace foo { typedef Integer Number; }; int foo(int x); int bar(Integer y); int spam(foo::Number a, foo::Number b);
In addition to tracking typenames, typemaps may also be specialized to match against a specific argument name. For example, you could write a typemap like this:
For certain tasks such as input argument conversion, typemaps can be defined for sequences of consecutive arguments. For example:%typemap(in) double nonnegative { $1 = PyFloat_AsDouble($input); if ($1 < 0) { PyErr_SetString(PyExc_ValueError,"argument must be nonnegative."); return NULL; } } ... double sin(double x); double cos(double x); double sqrt(double nonnegative); typedef double Real; double log(Real nonnegative); ...
In this case, a single input object is expanded into a pair of C arguments. This example also provides a hint to the unusual variable naming scheme involving $1, $2, and so forth.%typemap(in) (char *str, int len) { $1 = PyString_AsString($input); /* char *str */ $2 = PyString_Size($input); /* int len */ } ... int count(char *str, int len, char c);
A more general form of copying is found in the %apply directive like this:%typemap(in) Integer = int; %typemap(in) (char *buffer, int size) = (char *str, int len);
%apply merely takes all of the typemaps that are defined for one type and applies them to other types. Note: you can include a comma separated set of types in the { ... } part of %apply.%typemap(in) int { /* Convert an integer argument */ ... } %typemap(out) int { /* Return an integer value */ ... } /* Apply all of the integer typemaps to size_t */ %apply int { size_t };
It should be noted that it is not necessary to copy typemaps for types that are related by typedef. For example, if you have this,
then SWIG already knows that the int typemaps apply. You don't have to do anything.typedef int size_t;
Argument handling
int foo(int x, double y, char *s);
Return value handling
int foo(int x, double y, char *s);
Exception handling
int foo(int x, double y, char *s) throw(MemoryError, IndexError);
Global variables
int foo;
Member variables
struct Foo { int x[20]; };
Constant creation
#define FOO 3 %constant int BAR = 42; enum { ALE, LAGER, STOUT };
and you wanted to tell SWIG that make_Foo() returned a newly allocated object (for the purposes of providing better memory management). Clearly, this property of make_Foo() is not a property that would be associated with the datatype Foo * by itself. Therefore, a completely different SWIG customization mechanism (%feature) is used for this purpose. Consult the Customization Features chapter for more information about that.Foo *make_Foo();
Typemaps also can't be used to rearrange or transform the order of arguments. For example, if you had a function like this:
you can't use typemaps to interchange the arguments, allowing you to call the function like this:void foo(int, char *);
If you want to change the calling conventions of a function, write a helper function instead. For example:foo("hello",3) # Reversed arguments
%rename(foo) wrap_foo; %inline %{ void wrap_foo(char *s, int x) { foo(x,s); } %}
Since typemaps are strongly tied to the underlying C++ type system, subsequent sections assume that you are reasonably familiar with the basic details of values, pointers, references, arrays, type qualifiers (e.g., const), structures, namespaces, templates, and memory management in C/C++. If not, you would be well-advised to consult a copy of "The C Programming Language" by Kernighan and Ritchie or "The C++ Programming Language" by Stroustrup before going any further.
method is a simply a name that specifies what kind of typemap is being defined. It is usually a name like "in", "out", or "argout". The purpose of these methods is described later.%typemap(method [, modifiers]) typelist code ;
modifiers is an optional comma separated list of name="value" values. These are sometimes to attach extra information to a typemap and is often target-language dependent.
typelist is a list of the C++ type patterns that the typemap will match. The general form of this list is as follows:
Each type pattern is either a simple type, a simple type and argument name, or a list of types in the case of multi-argument typemaps. In addition, each type pattern can be parameterized with a list of temporary variables (parms). The purpose of these variables will be explained shortly.typelist : typepattern [, typepattern, typepattern, ... ] ; typepattern : type [ (parms) ] | type name [ (parms) ] | ( typelist ) [ (parms) ]
code specifies the C code used in the typemap. It can take any one of the following forms:
Here are some examples of valid typemap specifications:code : { ... } | " ... " | %{ ... %}
Admittedly, it's not the most readable syntax at first glance. However, the purpose of the individual pieces will become clear./* Simple typemap declarations */ %typemap(in) int { $1 = PyInt_AsLong($input); } %typemap(in) int "$1 = PyInt_AsLong($input);"; %typemap(in) int %{ $1 = PyInt_AsLong($input); %} /* Typemap with extra argument name */ %typemap(in) int nonnegative { ... } /* Multiple types in one typemap */ %typemap(in) int, short, long { $1 = SvIV($input); } /* Typemap with modifiers */ %typemap(in,doc="integer") int "$1 = gh_scm2int($input);"; /* Typemap applied to patterns of multiple arguments */ %typemap(in) (char *str, int len), (char *buffer, int size) { $1 = PyString_AsString($input); $2 = PyString_Size($input); } /* Typemap with extra pattern parameters */ %typemap(in, numinputs=0) int *output (int temp), long *output (long temp) { $1 = &temp; }
One exception to the typemap scoping rules pertains to the %extend declaration. %extend is used to attach new declarations to a class or structure definition. Because of this, all of the declarations in an %extend block are subject to the typemap rules that are in effect at the point where the class itself is defined. For example:// typemap1 %typemap(in) int { ... } int fact(int); // typemap1 int gcd(int x, int y); // typemap1 // typemap2 %typemap(in) int { ... } int isprime(int); // typemap2
class Foo { ... }; %typemap(in) int { ... } %extend Foo { int blah(int x); // typemap has no effect. Declaration is attached to Foo which // appears before the %typemap declaration. };
or this:%typemap(in) Integer = int;
Types are often managed by a collection of different typemaps. For example:%typemap(in) Integer, Number, int32_t = int;
To copy all of these typemaps to a new type, use %apply. For example:%typemap(in) int { ... } %typemap(out) int { ... } %typemap(varin) int { ... } %typemap(varout) int { ... }
The patterns for %apply follow the same rules as for %typemap. For example:%apply int { Integer }; // Copy all int typemaps to Integer %apply int { Integer, Number }; // Copy all int typemaps to both Integer and Number
%apply int *output { Integer *output }; // Typemap with name %apply (char *buf, int len) { (char *buffer, int size) }; // Multiple arguments
The %clear directive clears all typemaps for a given type. For example:%typemap(in) int; // Clears typemap for int %typemap(in) int, long, short; // Clears typemap for int, long, short %typemap(in) int *output;
Note: Since SWIG's default behavior is defined by typemaps, clearing a fundamental type like int will make that type unusable unless you also define a new set of typemaps immediately after the clear operation.%clear int; // Removes all types for int %clear int *output, long *output;
When a typemap appears inside a namespace or class, it stays in effect until the end of the SWIG input (just like before). However, the typemap takes the local scope into account. Therefore, this code%typemap(in) int { ... } namespace std { class string; %typemap(in) string { ... } } class Bar { public: typedef const int & const_reference; %typemap(out) const_reference { ... } };
is really defining a typemap for the type std::string. You could have code like this:namespace std { class string; %typemap(in) string { ... } }
In this case, there are two completely distinct typemaps that apply to two completely different types (std::string and Foo::string).namespace std { class string; %typemap(in) string { /* std::string */ ... } } namespace Foo { class string; %typemap(in) string { /* Foo::string */ ... } }
It should be noted that for scoping to work, SWIG has to know that string is a typename defined within a particular namespace. In this example, this is done using the class declaration class string.
To find a typemap for the argument const char *s, SWIG will search for the following typemaps:int foo(const char *s);
When more than one typemap rule might be defined, only the first match found is actually used. Here is an example that shows how some of the basic rules are applied:const char *s Exact type and name match const char * Exact type match char *s Type and name match (stripped qualifiers) char * Type match (stripped qualifiers)
%typemap(in) int *x { ... typemap 1 } %typemap(in) int * { ... typemap 2 } %typemap(in) const int *z { ... typemap 3 } %typemap(in) int [4] { ... typemap 4 } %typemap(in) int [ANY] { ... typemap 5 } void A(int *x); // int *x rule (typemap 1) void B(int *y); // int * rule (typemap 2) void C(const int *x); // int *x rule (typemap 1) void D(const int *z); // int * rule (typemap 3) void E(int x[4]); // int [4] rule (typemap 4) void F(int x[1000]); // int [ANY] rule (typemap 5)
To find the typemap for Integer x, SWIG will first search for the following typemaps:%typemap(in) int { ... typemap 1 } typedef int Integer; void blah(Integer x);
Finding no match, it then applies a reduction Integer -> int to the type and repeats the search.Integer x Integer
Even though two types might be the same via typedef, SWIG allows typemaps to be defined for each typename independently. This allows for interesting customization possibilities based solely on the typename itself. For example, you could write code like this:int x int --> match: typemap 1
When reducing the type, only one typedef reduction is applied at a time. The search process continues to apply reductions until a match is found or until no more reductions can be made.typedef double pdouble; // Positive double // typemap 1 %typemap(in) double { ... get a double ... } // typemap 2 %typemap(in) pdouble { ... get a positive double ... } double sin(double x); // typemap 1 pdouble sqrt(pdouble x); // typemap 2
For complicated types, the reduction process can generate a long list of patterns. Consider the following:
To find a match for the Row4 rows[10] argument, SWIG would check the following patterns, stopping only when it found a match:typedef int Integer; typedef Integer Row4[4]; void foo(Row4 rows[10]);
For parametized types like templates, the situation is even more complicated. Suppose you had some declarations like this:Row4 rows[10] Row4 [10] Row4 rows[ANY] Row4 [ANY] # Reduce Row4 --> Integer[4] Integer rows[10][4] Integer [10][4] Integer rows[ANY][ANY] Integer [ANY][ANY] # Reduce Integer --> int int rows[10][4] int [10][4] int rows[ANY][ANY] int [ANY][ANY]
In this case, the following typemap patterns are searched for the argument fooii *x:typedef int Integer; typedef foo<Integer,Integer> fooii; void blah(fooii *x);
Typemap reductions are always applied to the left-most type that appears. Only when no reductions can be made to the left-most type are reductions made to other parts of the type. This behavior means that you could define a typemap for foo<int,Integer>, but a typemap for foo<Integer,int> would never be matched. Admittedly, this is rather esoteric--there's little practical reason to write a typemap quite like that. Of course, you could rely on this to confuse your coworkers even more.fooii *x fooii * # Reduce fooii --> foo<Integer,Integer> foo<Integer,Integer> *x foo<Integer,Integer> * # Reduce Integer -> int foo<int, Integer> *x foo<int, Integer> * # Reduce Integer -> int foo<int, int> *x foo<int, int> *
Since typemap matching follows all typedef declarations, any sort of type that is mapped to a primitive type through typedef will be picked up by one of these primitive typemaps.%typemap(in) int "convert an int"; %typemap(in) short "convert a short"; %typemap(in) float "convert a float"; ...
The default behavior for pointers, arrays, references, and other kinds of types are handled by specifying rules for variations of the reserved SWIGTYPE type. For example:
These rules match any kind of pointer, reference, or array--even when multiple levels of indirection or multiple array dimensions are used. Therefore, if you wanted to change SWIG's default handling for all types of pointers, you would simply redefine the rule for SWIGTYPE *.%typemap(in) SWIGTYPE * { ... default pointer handling ... } %typemap(in) SWIGTYPE & { ... default reference handling ... } %typemap(in) SWIGTYPE [] { ... default array handling ... } %typemap(in) enum SWIGTYPE { ... default handling for enum values ... } %typemap(in) SWIGTYPE (CLASS::*) { ... default pointer member handling ... }
Finally, the following typemap rule is used to match against simple types that don't match any other rules:
This typemap is important because it is the rule that gets triggered when call or return by value is used. For instance, if you have a declaration like this:%typemap(in) SWIGTYPE { ... handle an unknown type ... }
The Vector type will usually just get matched against SWIGTYPE. The default implementation of SWIGTYPE is to convert the value into pointers (as described in chapter 3).double dot_product(Vector a, Vector b);
By redefining SWIGTYPE it may be possible to implement other behavior. For example, if you cleared all typemaps for SWIGTYPE, SWIG simply won't wrap any unknown datatype (which might be useful for debugging). Alternatively, you might modify SWIGTYPE to marshal objects into strings instead of converting them to pointers.
The best way to explore the default typemaps is to look at the ones already defined for a particular language module. Typemaps definitions are usually found in the SWIG library in a file such as python.swg, tcl8.swg, etc.
When multi-argument typemaps are specified, they take precedence over any typemaps specified for a single type. For example:
Multi-argument typemaps are also more restrictive in the way that they are matched. Currently, the first argument follows the matching rules described in the previous section, but all subsequent arguments must match exactly.%typemap(in) (char *buffer, int len) { // typemap 1 } %typemap(in) char *buffer { // typemap 2 } void foo(char *buffer, int len, int count); // (char *buffer, int len) void bar(char *buffer, int blah); // char *buffer
the typemap code is inserted into the wrapper function using a new block scope. In other words, the wrapper code will look like this:%typemap(in) int { $1 = PyInt_AsLong($input); }
Because the typemap code is enclosed in its own block, it is legal to declare temporary variables for use during typemap execution. For example:wrap_whatever() { ... // Typemap code { arg1 = PyInt_AsLong(obj1); } ... }
Of course, any variables that you declare inside a typemap are destroyed as soon as the typemap code has executed (they are not visible to other parts of the wrapper function or other typemaps that might use the same variable names).%typemap(in) short { long temp; /* Temporary value */ if (Tcl_GetLongFromObj(interp, $input, &temp) != TCL_OK) { return TCL_ERROR; } $1 = (short) temp; }
Occasionally, typemap code will be specified using a few alternative forms. For example:
These two forms are mainly used for cosmetics--the specified code is not enclosed inside a block scope when it is emitted. This sometimes results in a less complicated looking wrapper function.%typemap(in) int "$1 = PyInt_AsLong($input);"; %typemap(in) int %{ $1 = PyInt_AsLong($input); %}
and you wanted to pass a native string in the target language as an argument. For instance, in Perl, you wanted the function to work like this:int foo(std::string *s);
To do this, you can't just pass a raw Perl string as the std::string * argument. Instead, you have to create a temporary std::string object, copy the Perl string data into it, and then pass a pointer to the object. To do this, simply specify the typemap with an extra parameter like this:$x = foo("Hello World");
In this case, temp becomes a local variable in the scope of the entire wrapper function. For example:%typemap(in) std::string * (std::string temp) { unsigned int len; char *s; s = SvPV($input,len); /* Extract string data */ temp.assign(s,len); /* Assign to temp */ $1 = &temp; /* Set argument to point to temp */ }
When you set temp to a value, it persists for the duration of the wrapper function and gets cleaned up automatically on exit.wrap_foo() { std::string temp; <--- Declaration of temp goes here ... /* Typemap code */ { ... temp.assign(s,len); ... } ... }
It is perfectly safe to use more than one typemap involving local variables in the same declaration. For example, you could declare a function as :
This is safely handled because SWIG actually renames all local variable references by appending an argument number suffix. Therefore, the generated code would actually look like this:void foo(std::string *x, std::string *y, std::string *z);
wrap_foo() { int *arg1; /* Actual arguments */ int *arg2; int *arg3; std::string temp1; /* Locals declared in the typemap */ std::string temp2; std::string temp3; ... { char *s; unsigned int len; ... temp1.assign(s,len); arg1 = *temp1; } { char *s; unsigned int len; ... temp2.assign(s,len); arg2 = &temp2; } { char *s; unsigned int len; ... temp3.assign(s,len); arg3 = &temp3; } ... }
Some typemaps do not recognize local variables (or they may simply not apply). At this time, only typemaps that apply to argument conversion support this.
Variable | Meaning |
---|---|
$n | A C local variable corresponding to type n in the typemap pattern. |
$argnum | Argument number. Only available in typemaps related to argument conversion |
$n_name | Argument name |
$n_type | Real C datatype of type n. |
$n_ltype | ltype of type n |
$n_mangle | Mangled form of type n. For example _p_Foo |
$n_descriptor | Type descriptor structure for type n. For example SWIGTYPE_p_Foo. This is primarily used when interacting with the run-time type checker (described later). |
$*n_type | Real C datatype of type n with one pointer removed. |
$*n_ltype | ltype of type n with one pointer removed. |
$*n_mangle | Mangled form of type n with one pointer removed. |
$*n_descriptor | Type descriptor structure for type n with one pointer removed. |
$&n_type | Real C datatype of type n with one pointer added. |
$&n_ltype | ltype of type n with one pointer added. |
$&n_mangle | Mangled form of type n with one pointer added. |
$&n_descriptor | Type descriptor structure for type n with one pointer added. |
$n_basetype | Base typename with all pointers and qualifiers stripped. |
Within the table, $n refers to a specific type within the typemap specification. For example, if you write this
then $1 refers to int *INPUT. If you have a typemap like this,%typemap(in) int *INPUT { }
then $1 refers to int argc and $2 refers to char *argv[].%typemap(in) (int argc, char *argv[]) { ... }
Substitutions related to types and names always fill in values from the actual code that was matched. This is useful when a typemap might match multiple C datatype. For example:
In this case, $1_ltype is replaced with the datatype that is actually matched.%typemap(in) int, short, long { $1 = ($1_ltype) PyInt_AsLong($input); }
When typemap code is emitted, the C/C++ datatype of the special variables $1 and $2 is always an "ltype." An "ltype" is simply a type that can legally appear on the left-hand side of a C assignment operation. Here are a few examples of types and ltypes:
In most cases a ltype is simply the C datatype with qualifiers stripped off. In addition, arrays are converted into pointers.type ltype ------ ---------------- int int const int int conts int * int * int [4] int * int [4][5] int (*)[5]
Variables such as $&1_type and $*1_type are used to safely modify the type by removing or adding pointers. Although not needed in most typemaps, these substitutions are sometimes needed to properly work with typemaps that convert values between pointers and values.
If necessary, type related substitutions can also be used when declaring locals. For example:
%typemap(in) int * ($*1_type temp) { temp = PyInt_AsLong($input); $1 = &temp; }
There is one word of caution about declaring local variables in this manner. If you declare a local variable using a type substitution such as $1_ltype temp, it won't work like you expect for arrays and certain kinds of pointers. For example, if you wrote this,
then the declaration of temp will be expanded as%typemap(in) int [10][20] { $1_ltype temp; }
This is illegal C syntax and won't compile. There is currently no straightforward way to work around this problem in SWIG due to the way that typemap code is expanded and processed. However, one possible workaround is to simply pick an alternative type such as void * and use casts to get the correct type when needed. For example:int (*)[20] temp;
Another approach, which only works for arrays is to use the $1_basetype substitution. For example:%typemap(in) int [10][20] { void *temp; ... (($1_ltype) temp)[i][j] = x; /* set a value */ ... }
%typemap(in) int [10][20] { $1_basetype temp[10][20]; ... temp[i][j] = x; /* set a value */ ... }
The following special variables are available:%typemap(in) int { $1 = PyInt_AsLong($input); }
This is probably the most commonly redefined typemap because it can be used to implement customized conversions.$input - Input object holding value to be converted. $symname - Name of function/method being wrapped
In addition, the "in" typemap allows the number of converted arguments to be specified. For example:
At this time, only zero or one arguments may be converted.// Ignored argument. %typemap(in, numinputs=0) int *out (int temp) { $1 = &temp; }
Compatibility note: Specifying numinputs=0 is the same as the old "ignore" typemap.
For typechecking, the $1 variable is always a simple integer that is set to 1 or 0 depending on whether or not the input argument is the correct type.%typemap(typecheck,precedence=SWIG_TYPECHECK_INTEGER) int { $1 = PyInt_Check($input) ? 1 : 0; }
If you define new "in" typemaps and your program uses overloaded methods, you should also define a collection of "typecheck" typemaps. More details about this follow in a later section on "Typemaps and Overloading."
The following special variables are available.%typemap(out) int { $result = PyInt_FromLong($1); }
$result - Result object returned to target language. $symname - Name of function/method being wrapped
// Set argument to NULL before any conversion occurs %typemap(arginit) int *data { $1 = NULL; }
The primary use of this typemap is to either change the wrapping of default arguments or specify a default argument in a language where they aren't supported (like C).%typemap(default) int flags { $1 = DEFAULT_FLAGS; } ... int foo(int x, int y, int flags);
Once a default typemap has been applied to an argument, all arguments that follow must have default values.
%typemap(check) int positive { if ($1 <= 0) { SWIG_exception(SWIG_ValueError,"Expected positive value."); } }
The following special variables are available./* Set the input argument to point to a temporary variable */ %typemap(in, numinputs=0) int *out (int temp) { $1 = &temp; } %typemap(argout) int *out { // Append output value $1 to $result ... }
The code supplied to the "argout" typemap is always placed after the "out" typemap. If multiple return values are used, the extra return values are often appended to return value of the function.$result - Result object returned to target language. $input - The original input object passed. $symname - Name of function/method being wrapped
See the typemaps.i library for examples.
The "freearg" typemap inserted at the end of the wrapper function, just before control is returned back to the target language. This code is also placed into a special variable $cleanup that may be used in other typemaps whenever a wrapper function needs to abort prematurely.// Get a list of integers %typemap(in) int *items { int nitems = Length($input); $1 = (int *) malloc(sizeof(int)*nitems); } // Free the list %typemap(freearg) int *items { free($1); }
%typemap(newfree) string * { delete $1; } %typemap(out) string * { $result = PyString_FromString($1->c_str()); } ... %newobject foo; ... string *foo();
It is rarely necessary to write "memberin" typemaps---SWIG already provides a default implementation for arrays, strings, and other objects.%typemap(memberin) int [4] { memmove($1, $input, 4*sizeof(int)); }
For example, suppose you had a function like this:
If you wanted to handle float value[4] as a list of floats, you might write a typemap similar to this:void set_vector(int type, float value[4]);
In this example, the variable temp allocates a small array on the C stack. The typemap then populates this array and passes it to the underlying C function.%typemap(in) float value[4] (float temp[4]) { int i; if (!PySequence_Check($input)) { PyErr_SetString(PyExc_ValueError,"Expected a sequence"); return NULL; } if (PySequence_Length($input) != 4) { PyErr_SetString(PyExc_ValueError,"Size mismatch. Expected 4 elements"); return NULL; } for (i = 0; i < 4; i++) { PyObject *o = PySequence_GetItem($input,i); if (PyNumber_Check(o)) { temp[i] = (float) PyFloat_AsDouble(o); } else { PyErr_SetString(PyExc_ValueError,"Sequence elements must be numbers"); return NULL; } } $1 = temp; }
When used from Python, the typemap allows the following type of function call:
>>> set_vector(type, [ 1, 2.5, 5, 20 ])
If you wanted to generalize the typemap to apply to arrays of all dimensions you might write this:
In this example, the special variable $1_dim0 is expanded with the actual array dimensions. Multidimensional arrays can be matched in a similar manner. For example:%typemap(in) float value[ANY] (float temp[$1_dim0]) { int i; if (!PySequence_Check($input)) { PyErr_SetString(PyExc_ValueError,"Expected a sequence"); return NULL; } if (PySequence_Length($input) != $1_dim0) { PyErr_SetString(PyExc_ValueError,"Size mismatch. Expected $1_dim0 elements"); return NULL; } for (i = 0; i < $1_dim0; i++) { PyObject *o = PySequence_GetItem($input,i); if (PyNumber_Check(o)) { temp[i] = (float) PyFloat_AsDouble(o); } else { PyErr_SetString(PyExc_ValueError,"Sequence elements must be numbers"); return NULL; } } $1 = temp; }
For large arrays, it may be impractical to allocate storage on the stack using a temporary variable as shown. To work with heap allocated data, the following technique can be used.%typemap(python,in) float matrix[ANY][ANY] (float temp[$1_dim0][$1_dim1]) { ... convert a 2d array ... }
In this case, an array is allocated using malloc. The freearg typemap is then used to release the argument after the function has been called.%typemap(in) float value[ANY] { int i; if (!PySequence_Check($input)) { PyErr_SetString(PyExc_ValueError,"Expected a sequence"); return NULL; } if (PySequence_Length($input) != $1_dim0) { PyErr_SetString(PyExc_ValueError,"Size mismatch. Expected $1_dim0 elements"); return NULL; } $1 = (float *) malloc($1_dim0*sizeof(float)); for (i = 0; i < $1_dim0; i++) { PyObject *o = PySequence_GetItem($input,i); if (PyNumber_Check(o)) { $1[i] = (float) PyFloat_AsDouble(o); } else { PyErr_SetString(PyExc_ValueError,"Sequence elements must be numbers"); return NULL; } } } %typemap(freearg) float value[ANY] { if ($1) free($1); }
Another common use of array typemaps is to provide support for array structure members. Due to subtle differences between pointers and arrays in C, you can't just "assign" to a array structure member. Instead, you have to explicitly copy elements into the array. For example, suppose you had a structure like this:
When SWIG runs, it won't produce any code to set the vec member. You may even get a warning message like this:struct SomeObject { float value[4]; ... };
swig -python example.i Generating wrappers for Python example.i:10. Warning. Array member value will be read-only.
These warning messages indicate that SWIG does not know how you want to set the vec field.
To fix this, you can supply a special "memberin" typemap like this:
%typemap(memberin) float [ANY] { int i; for (i = 0; i < $1_dim0; i++) { $1[i] = $input[i]; } }
The memberin typemap is used to set a structure member from data that has already been converted from the target language to C. In this case, $input is the local variable in which converted input data is stored. This typemap then copies this data into the structure.
When combined with the earlier typemaps for arrays, the combination of the "in" and "memberin" typemap allows the following usage:
>>> s = SomeObject() >>> s.x = [1, 2.5, 5, 10]
Related to structure member input, it may be desirable to return structure members as a new kind of object. For example, in this example, you will get very odd program behavior where the structure member can be set nicely, but reading the member simply returns a pointer:
To fix this, you can write an "out" typemap. For example:>>> s = SomeObject() >>> s.x = [1, 2.5, 5. 10] >>> print s.x _1008fea8_p_float >>>
Now, you will find that member access is quite nice:%typemap(out) float [ANY] { int i; $result = PyList_New($1_dim0); for (i = 0; i < $1_dim0; i++) { PyObject *o = PyFloat_FromDouble((double) $1[i]); PyList_SetItem($result,i,o); } }
Compatibility Note: SWIG1.1 used to provide a special "memberout" typemap. However, it was mostly useless and has since been eliminated. To return structure members, simply use the "out" typemap.>>> s = SomeObject() >>> s.x = [1, 2.5, 5, 10] >>> print s.x [ 1, 2.5, 5, 10]
This provides a sanity check to your wrapper function. If a negative number is passed to this function, a Perl exception will be raised and your program terminated with an error message.%module math %typemap(check) double posdouble { if ($1 < 0) { croak("Expecting a positive number"); } } ... double sqrt(double posdouble);
This kind of checking can be particularly useful when working with pointers. For example :
will prevent any function involving a Vector * from accepting a NULL pointer. As a result, SWIG can often prevent a potential segmentation faults or other run-time problems by raising an exception rather than blindly passing values to the underlying C/C++ program.%typemap(check) Vector * { if ($1 == 0) { PyErr_SetString(PyExc_TypeError,"NULL Pointer not allowed"); return NULL; } }
Note: A more advanced constraint checking system is in development. Stay tuned.
Suppose that you wanted to wrap this function so that it accepted a single list of strings like this:int foo(int argc, char *argv[]);
To do this, you not only need to map a list of strings to char *argv[], but the value of int argc is implicitly determined by the length of the list. Using only simple typemaps, this type of conversion is possible, but extremely painful. Therefore, SWIG1.3 introduces the notion of multi-argument typemaps.>>> foo(["ale","lager","stout"])
A multi-argument typemap is a conversion rule that specifies how to convert a single object in the target language to set of consecutive function arguments in C/C++. For example, the following multi-argument maps perform the conversion described for the above example:
A multi-argument map is always specified by surrounding the arguments with parentheses as shown. For example:%typemap(in) (int argc, char *argv[]) { int i; if (!PyList_Check($input)) { PyErr_SetString(PyExc_ValueError, "Expecting a list"); return NULL; } $1 = PyList_Size($input); $2 = (char **) malloc(($1+1)*sizeof(char *)); for (i = 0; i < $1; i++) { PyObject *s = PyList_GetItem($input,i); if (!PyString_Check(s)) { free($2); PyErr_SetString(PyExc_ValueError, "List items must be strings"); return NULL; } $2[i] = PyString_AsString(s); } $2[i] = 0; } %typemap(freearg) (int argc, char *argv[]) { if ($2) free($2); }
Within the typemap code, the variables $1, $2, and so forth refer to each type in the map. All of the usual substitutions apply--just use the appropriate $1 or $2 prefix on the variable name (e.g., $2_type, $1_ltype, etc.)%typemap(in) (int argc, char *argv[]) { ... }
Multi-argument typemaps always have precedence over simple typemaps and SWIG always performs longest-match searching. Therefore, you will get the following behavior:
It should be stressed that multi-argument typemaps can appear anywhere in a function declaration and can appear more than once. For example, you could write this:%typemap(in) int argc { ... typemap 1 ... } %typemap(in) (int argc, char *argv[]) { ... typemap 2 ... } %typemap(in) (int argc, char *argv[], char *env[]) { ... typemap 3 ... } int foo(int argc, char *argv[]); // Uses typemap 2 int bar(int argc, int x); // Uses typemap 1 int spam(int argc, char *argv[], char *env[]); // Uses typemap 3
Other directives such as %apply and %clear also work with multi-argument maps. For example:%typemap(in) (int scount, char *swords[]) { ... } %typemap(in) (int wcount, char *words[]) { ... } void search_words(int scount, char *swords[], int wcount, char *words[], int maxcount);
Although multi-argument typemaps may seem like an exotic, little used feature, there are several situations where they make sense. First, suppose you wanted to wrap functions similar to the low-level read() and write() system calls. For example:%apply (int argc, char *argv[]) { (int scount, char *swords[]), (int wcount, char *words[]) }; ... %clear (int scount, char *swords[]), (int wcount, char *words[]); ...
As is, the only way to use the functions would be to allocate memory and pass some kind of pointer as the second argument---a process that might require the use of a helper function. However, using multi-argument maps, the functions can be transformed into something more natural. For example, you might write typemaps like this:typedef unsigned int size_t; int read(int fd, void *rbuffer, size_t len); int write(int fd, void *wbuffer, size_t len);
(note: In the above example, $result and result are two different variables. result is the real C datatype that was returned by the function. $result is the scripting language object being returned to the interpreter.).// typemap for an outgoing buffer %typemap(in) (void *wbuffer, size_t len) { if (!PyString_Check($input)) { PyErr_SetString(PyExc_ValueError, "Expecting a string"); return NULL; } $1 = (void *) PyString_AsString($input); $2 = PyString_Size($input); } // typemap for an incoming buffer %typemap(in) (void *rbuffer, size_t len) { if (!PyInt_Check($input)) { PyErr_SetString(PyExc_ValueError, "Expecting an integer"); return NULL; } $2 = PyInt_AsLong($input); if ($2 < 0) { PyErr_SetString(PyExc_ValueError, "Positive integer expected"); return NULL; } $1 = (void *) malloc($2); } // Return the buffer. Discarding any previous return result %typemap(argout) (void *rbuffer, size_t len) { Py_XDECREF($result); /* Blow away any previous result */ if (result < 0) { /* Check for I/O error */ free($1); PyErr_SetFromErrno(PyExc_IOError); return NULL; } $result = PyString_FromStringAndSize($1,result); free($1); }
Now, in a script, you can write code that simply passes buffers as strings like this:
A number of multi-argument typemap problems also arise in libraries that perform matrix-calculations--especially if they are mapped onto low-level Fortran or C code. For example, you might have a function like this:>>> f = example.open("Makefile") >>> example.read(f,40) 'TOP = ../..\nSWIG = $(TOP)/.' >>> example.read(f,40) './swig\nSRCS = example.c\nTARGET ' >>> example.close(f) 0 >>> g = example.open("foo", example.O_WRONLY | example.O_CREAT, 0644) >>> example.write(g,"Hello world\n") 12 >>> example.write(g,"This is a test\n") 15 >>> example.close(g) 0 >>>
In this case, you might want to pass some kind of higher-level object as an matrix. To do this, you could write a multi-argument typemap like this:int is_symmetric(double *mat, int rows, int columns);
This kind of technique can be used to hook into scripting-language matrix packages such as Numeric Python. However, it should also be stressed that some care is in order. For example, when crossing languages you may need to worry about issues such as row-major vs. column-major ordering (and perform conversions if needed).%typemap(in) (double *mat, int rows, int columns) { MatrixObject *a; a = GetMatrixFromObject($input); /* Get matrix somehow */ /* Get matrix properties */ $1 = GetPointer(a); $2 = GetRows(a); $3 = GetColumns(a); }
At a basic level, the type checker simply restores some type-safety to extension modules. However, the type checker is also responsible for making sure that wrapped C++ classes are handled correctly---especially when inheritance is used. This is especially important when an extension module makes use of multiple inheritance. For example:_108e688_p_Foo
When the class FooBar is organized in memory, it contains the contents of the classes Foo and Bar as well as its own data members. For example:class Foo { int x; }; class Bar { int y; }; class FooBar : public Foo, public Bar { int z; };
Because of the way that base class data is stacked together, the casting of a Foobar * to either of the base classes may change the actual value of the pointer. This means that it is generally not safe to represent pointers using a simple integer or a bare void *---type tags are needed to implement correct handling of pointer values (and to make adjustments when needed).FooBar --> | -----------| <-- Foo | int x | |------------| <-- Bar | int y | |------------| | int z | |------------|
In the wrapper code generated for each language, pointers are handled through the use of special type descriptors and conversion functions. For example, if you look at the wrapper code for Python, you will see code like this:
In this code, SWIGTYPE_p_Foo is the type descriptor that describes Foo *. The type descriptor is actually a pointer to a structure that contains information about the type name to use in the target language, a list of equivalent typenames (via typedef or inheritance), and pointer value handling information (if applicable). The SWIG_ConvertPtr() function is simply a utility function that takes a pointer object in the target language and a type-descriptor objects and uses this information to generate a C++ pointer. However, the exact name and calling conventions of the conversion function depends on the target language (see language specific chapters for details).if ((SWIG_ConvertPtr(obj0,(void **) &arg1, SWIGTYPE_p_Foo,1)) == -1) return NULL;
When pointers are converted in a typemap, the typemap code often looks similar to this:
The most critical part is the typemap is the use of the $1_descriptor special variable. When placed in a typemap, this is expanded into the SWIGTYPE_* type descriptor object above. As a general rule, you should always use $1_descriptor instead of trying to hard-code the type descriptor name directly.%typemap(in) Foo * { if ((SWIG_ConvertPtr($input, (void **) &$1, $1_descriptor)) == -1) return NULL; }
There is another reason why you should always use the $1_descriptor variable. When this special variable is expanded, SWIG marks the corresponding type as "in use." When type-tables and type information is emitted in the wrapper file, descriptor information is only generated for those datatypes that were actually used in the interface. This greatly reduces the size of the type tables and improves efficiency.
Occassionally, you might need to write a typemap that needs to convert pointers of other types. To handle this, a special macro substition $descriptor(type) can be used to generate the SWIG type descriptor name for any C datatype. For example:
The primary use of $descriptor(type) is when writing typemaps for container objects and other complex data structures. There are some restrictions on the argument---namely it must be a fully defined C datatype. It can not be any of the special typemap variables.%typemap(in) Foo * { if ((SWIG_ConvertPtr($input, (void **) &$1, $1_descriptor)) == -1) { Bar *temp; if ((SWIG_ConvertPtr($input), (void **) &temp, $descriptor(Bar *)) == -1) { return NULL; } $1 = (Foo *) temp; } }
In certain cases, SWIG may not generate type-descriptors like you expect. For example, if you are converting pointers in some non-standard way or working with an unusual combination of interface files and modules, you may find that SWIG omits information for a specific type descriptor. To fix this, you may need to use the %types directive. For example:
When %types is used, SWIG generates type-descriptor information even if those datatypes never appear elsewhere in the interface file.%types(int *, short *, long *, float *, double *);
A final problem related to the type-checker is the conversion of types in code that is external to the SWIG wrapper file. This situation is somewhat rare in practice, but occasionally a programmer may want to convert a typed pointer object into a C++ pointer somewhere else in their program. The only problem is that the SWIG type descriptor objects are only defined in the wrapper code and not normally accessible.
To correctly deal with this situation, the following technique can be used:
Further details about the run-time type checking can be found in the documentation for individual language modules. Reading the source code may also help. The file common.swg in the SWIG library contains all of the source code for type-checking. This code is also included in every generated wrapped file so you probably just look at the output of SWIG to get a better sense for how types are managed./* Some non-SWIG file */ /* External declarations */ extern void *SWIG_TypeQuery(const char *); extern int SWIG_ConvertPtr(PyObject *, void **ptr, void *descr); void foo(PyObject *o) { Foo *f; static void *descr = 0; if (!descr) { descr = SWIG_TypeQuery("Foo *"); /* Get the type descriptor structure for Foo */ assert(descr); } if ((SWIG_ConvertPtr(o,(void **) &f, descr) == -1)) { abort(); } ... }
You can access the functions in a normal way from the scripting interpreter:int foo(int x); int foo(double x); int foo(char *s, int y);
To implement overloading, SWIG generates a separate wrapper function for each overloaded method. For example, the above functions would produce something roughly like this:# Python foo(3) # foo(int) foo(3.5) # foo(double) foo("hello",5) # foo(char *, int) # Tcl foo 3 # foo(int) foo 3.5 # foo(double) foo hello 5 # foo(char *, int)
Next, a dynamic dispatch function is generated:// wrapper pseudocode _wrap_foo_0(argc, args[]) { // foo(int) int arg1; int result; ... arg1 = FromInteger(args[0]); result = foo(arg1); return ToInteger(result); } _wrap_foo_1(argc, args[]) { // foo(double) double arg1; int result; ... arg1 = FromDouble(args[0]); result = foo(arg1); return ToInteger(result); } _wrap_foo_2(argc, args[]) { // foo(char *, int) char *arg1; int arg2; int result; ... arg1 = FromString(args[0]); arg2 = FromInteger(args[1]); result = foo(arg1,arg2); return ToInteger(result); }
The purpose of the dynamic dispatch function is to select the appropriate C++ function based on argument types---a task that must be performed at runtime in most of SWIG's target languages._wrap_foo(argc, args[]) { if (argc == 1) { if (IsInteger(args[0])) { return _wrap_foo_0(argc,args); } if (IsDouble(args[0])) { return _wrap_foo_1(argc,args); } } if (argc == 2) { if (IsString(args[0]) && IsInteger(args[1])) { return _wrap_foo_2(argc,args); } } error("No matching function!\n"); }
The generation of the dynamic dispatch function is a relatively tricky affair. Not only must input typemaps be taken into account (these typemaps can radically change the types of arguments accepted), but overloaded methods must also be sorted and checked in a very specific order to resolve potential ambiguity. A high-level overview of this ranking process is found in the "SWIG and C++" chapter. What isn't mentioned in that chapter is the mechanism by which it is implemented---as a collection of typemaps.
To support dynamic dispatch, SWIG first defines a general purpose type hierarchy as follows:
(These precedence levels are defined in swig.swg, a library file that's included by all target language modules.)Symbolic Name Precedence Value ------------------------------ ------------------ SWIG_TYPECHECK_POINTER 0 SWIG_TYPECHECK_VOIDPTR 10 SWIG_TYPECHECK_BOOL 15 SWIG_TYPECHECK_UINT8 20 SWIG_TYPECHECK_INT8 25 SWIG_TYPECHECK_UINT16 30 SWIG_TYPECHECK_INT16 35 SWIG_TYPECHECK_UINT32 40 SWIG_TYPECHECK_INT32 45 SWIG_TYPECHECK_UINT64 50 SWIG_TYPECHECK_INT64 55 SWIG_TYPECHECK_UINT128 60 SWIG_TYPECHECK_INT128 65 SWIG_TYPECHECK_INTEGER 70 SWIG_TYPECHECK_FLOAT 80 SWIG_TYPECHECK_DOUBLE 90 SWIG_TYPECHECK_COMPLEX 100 SWIG_TYPECHECK_UNICHAR 110 SWIG_TYPECHECK_UNISTRING 120 SWIG_TYPECHECK_CHAR 130 SWIG_TYPECHECK_STRING 140 SWIG_TYPECHECK_BOOL_ARRAY 1015 SWIG_TYPECHECK_INT8_ARRAY 1025 SWIG_TYPECHECK_INT16_ARRAY 1035 SWIG_TYPECHECK_INT32_ARRAY 1045 SWIG_TYPECHECK_INT64_ARRAY 1055 SWIG_TYPECHECK_INT128_ARRAY 1065 SWIG_TYPECHECK_FLOAT_ARRAY 1080 SWIG_TYPECHECK_DOUBLE_ARRAY 1090 SWIG_TYPECHECK_CHAR_ARRAY 1130 SWIG_TYPECHECK_STRING_ARRAY 1140
In this table, the precedence-level determines the order in which types are going to be checked. Low values are always checked before higher values. For example, integers are checked before floats, single values are checked before arrays, and so forth.
Using the above table as a guide, each target language defines a collection of "typecheck" typemaps. The follow excerpt from the Python module illustrates this:
It might take a bit of contemplation, but this code has merely organized all of the basic C++ types, provided some simple type-checking code, and assigned each type a precedence value./* Python type checking rules */ /* Note: %typecheck(X) is a macro for %typemap(typecheck,precedence=X) */ %typecheck(SWIG_TYPECHECK_INTEGER) int, short, long, unsigned int, unsigned short, unsigned long, signed char, unsigned char, long long, unsigned long long, const int &, const short &, const long &, const unsigned int &, const unsigned short &, const unsigned long &, const long long &, const unsigned long long &, enum SWIGTYPE, bool, const bool & { $1 = (PyInt_Check($input) || PyLong_Check($input)) ? 1 : 0; } %typecheck(SWIG_TYPECHECK_DOUBLE) float, double, const float &, const double & { $1 = (PyFloat_Check($input) || PyInt_Check($input) || PyLong_Check($input)) ? 1 : 0; } %typecheck(SWIG_TYPECHECK_CHAR) char { $1 = (PyString_Check($input) && (PyString_Size($input) == 1)) ? 1 : 0; } %typecheck(SWIG_TYPECHECK_STRING) char * { $1 = PyString_Check($input) ? 1 : 0; } %typecheck(SWIG_TYPECHECK_POINTER) SWIGTYPE *, SWIGTYPE &, SWIGTYPE [] { void *ptr; if (SWIG_ConvertPtr($input, (void **) &ptr, $1_descriptor, 0) == -1) { $1 = 0; PyErr_Clear(); } else { $1 = 1; } } %typecheck(SWIG_TYPECHECK_POINTER) SWIGTYPE { void *ptr; if (SWIG_ConvertPtr($input, (void **) &ptr, $&1_descriptor, 0) == -1) { $1 = 0; PyErr_Clear(); } else { $1 = 1; } } %typecheck(SWIG_TYPECHECK_VOIDPTR) void * { void *ptr; if (SWIG_ConvertPtr($input, (void **) &ptr, 0, 0) == -1) { $1 = 0; PyErr_Clear(); } else { $1 = 1; } } %typecheck(SWIG_TYPECHECK_POINTER) PyObject * { $1 = ($input != 0); }
Finally, to generate the dynamic dispatch function, SWIG uses the following algorithm:
The bottom line: If you are writing new typemaps and you are using overloaded methods, you will probably have to write typecheck code or copy existing code. Since this is a relatively new SWIG feature, there are few examples to work with. However, you might look at some of the existing library files likes 'typemaps.i' for a guide.// Typemap for a C++ string %typemap(in) std::string { if (PyString_Check($input)) { $1 = std::string(PyString_AsString($input)); } else { SWIG_exception(SWIG_TypeError, "string expected"); } } // Copy the typecheck code for "char *". %typemap(typecheck) std::string = char *;
Notes:
To make it easier to apply the typemap to different argument types and names, the %apply directive performs a copy of all typemaps from one type to another. For example, if you specify this,%typemap(in,numinputs=0) int *OUTPUT (int temp) { $1 = &temp; } %typemap(argout) int *OUTPUT { // return value somehow }
then all of the int *OUTPUT typemaps are copied to int *retvalue and int32 *output.%apply int *OUTPUT { int *retvalue, int32 *output };
However, there is a subtle aspect of %apply that needs more description. Namely, %apply does not overwrite a typemap rule if it is already defined for the target datatype. This behavior allows you to do two things:
Since %apply does not overwrite or replace any existing rules, the only way to reset behavior is to use the %clear directive. %clear removes all typemap rules defined for a specific datatype. For example:%typemap(in) int *INPUT (int temp) { temp = ... get value from $input ...; $1 = &temp; } %typemap(check) int *POSITIVE { if (*$1 <= 0) { SWIG_exception(SWIG_ValueError,"Expected a positive number!\n"); return NULL; } } ... %apply int *INPUT { int *invalue }; %apply int *POSITIVE { int *invalue };
%clear int *invalue;
If you had a large interface with hundreds of functions all accepting array parameters, this typemap would be replicated repeatedly--generating a huge amount of huge. A better approach might be to consolidate some of the typemap into a function. For example:%typemap(in) float [ANY] { int i; if (!PySequence_Check($input)) { PyErr_SetString(PyExc_ValueError,"Expected a sequence"); return NULL; } if (PySequence_Length($input) != $1_dim0) { PyErr_SetString(PyExc_ValueError,"Size mismatch. Expected $1_dim0 elements"); return NULL; } $1 = (float) malloc($1_dim0*sizeof(float)); for (i = 0; i < $1_dim0; i++) { PyObject *o = PySequence_GetItem($input,i); if (PyNumber_Check(o)) { $1[i] = (float) PyFloat_AsDouble(o); } else { PyErr_SetString(PyExc_ValueError,"Sequence elements must be numbers"); return NULL; } } }
%{ /* Define a helper function */ static float * convert_float_array(PyObject *input, int size) { int i; float *result; if (!PySequence_Check(input)) { PyErr_SetString(PyExc_ValueError,"Expected a sequence"); return NULL; } if (PySequence_Length(input) != size) { PyErr_SetString(PyExc_ValueError,"Size mismatch. "); return NULL; } result = (float) malloc(size*sizeof(float)); for (i = 0; i < size; i++) { PyObject *o = PySequence_GetItem(input,i); if (PyNumber_Check(o)) { result[i] = (float) PyFloat_AsDouble(o); } else { PyErr_SetString(PyExc_ValueError,"Sequence elements must be numbers"); free(result); return NULL; } } return result; } %} %typemap(in) float [ANY] { $1 = convert_float_array($input, $1_dim0); if (!$1) return NULL; } %}
It is also important to note that the primary use of local variables is to create stack-allocated objects for temporary use inside a wrapper function (this is faster and less-prone to error than allocating data on the heap). In general, the variables are not intended to pass information between different types of typemaps. However, this can be done if you realize that local names have the argument number appended to them. For example, you could do this:
In this case, the $argnum variable is expanded into the argument number. Therefore, the code will reference the appropriate local such as temp1 and temp2. It should be noted that there are plenty of opportunities to break the universe here and that accessing locals in this manner should probably be avoided. At the very least, you should make sure that the typemaps sharing information have exactly the same types and names.%typemap(in) int *(int temp) { temp = (int) PyInt_AsLong($input); $1 = &temp; } %typemap(argout) int * { PyObject *o = PyInt_FromLong(temp$argnum); ... }