David M. Beazley
beazley@cs.uchicago.edu
$Header$
(Note : This is a work in progress.)
The audience is assumed to be SWIG developers (who should also read the
SWIG Engineering Manual before starting
to code).
1.1 Directory Guide
| Doc | HTML documentation. If you find a documentation bug, please let us know. | ||||||||||||||||||||
| Examples | This subdir tree contains examples of using SWIG w/ different scripting languages, including makefiles. Typically, there are the "simple" and "matrix" examples, w/ some languages offering additional examples. The GIFPlot example has its own set of per-language subdirectories. See the README more index.html file in each directory for more info. [FIXME: Ref SWIG user manual.] | ||||||||||||||||||||
| Lib | These are the .i (interface) files that form the SWIG installed library. Language-specific files are in subdirectories (for example, guile/typemaps.i). Each language also has a .swg file implementing runtime type support for that language. The SWIG library is not versioned. | ||||||||||||||||||||
| Misc | Currently this subdir only contains file fileheader. See the Engineering Manual for more info. | ||||||||||||||||||||
| Runtime | This subdir contains scripts and a makefile for creating runtime shared-object libraries used by various languages. Runtime/make.sh says: "The runtime libraries are only needed if you are building multiple extension modules that need to share information." | ||||||||||||||||||||
| Source | SWIG source code is in this subdir tree. Directories marked w/ "(*)"
are used in building the swig executable.
|
||||||||||||||||||||
| Tools | Libtool support and the mkdist.py script. | ||||||||||||||||||||
| Win | This improperly-named (spit spit) subdir only has README.txt. |
DOH takes a different approach to tackling the complexity problem. First, rather than going overboard with dozens of types and class definitions, DOH only defines a handful of simple yet very useful objects that are easy to remember. Second, DOH uses dynamic typing---one of the features that make scripting languages so useful and which make it possible to accomplish things with much less code. Finally, DOH utilizes a few coding tricks that allow it to perform a limited form of function overloading for certain C datatypes (more on that a little later).
The key point to using DOH is that instead of thinking about code in
terms of highly specialized C data structures, just about everything
ends up being represented in terms of a just a few datatypes. For
example, structures are replaced by DOH hash tables whereas arrays are
replaced by DOH lists. At first, this is probably a little strange to
most C/C++ programmers, but in the long run in makes the system
extremely flexible and highly extensible. Also, in terms of coding,
many of the newly DOH-based subsystems are less than half the size (in
lines of code) of the earlier C++ implementation.
2.2 Basic Types
The following built-in types are currently provided by DOH:
DOH *a, *b, *c, *d;
a = NewString("Hello World");
b = NewList();
c = Copy(a); /* Copy the string a */
d = Copy(b); /* Copy the list b */
Copies of lists and hash tables are shallow. That is, their contents are only copied by reference.
Objects can be deleted using the Delete() function. For example:
DOH *a = NewString("Hello World");
...
Delete(a); /* Destroy a */
All objects are referenced counted and given a reference count of 1 when initially created. The
Delete() function only destroys an object when the reference count reaches zero. When
an object is placed in a list or hash table, it's reference count is automatically increased. For example:
DOH *a, *b;
a = NewString("Hello World");
b = NewList();
Append(b,a); /* Increases refcnt of a to 2 */
Delete(a); /* Decreases refcnt of a to 1 */
Delete(b); /* Destroys b, and destroys a */
Should it ever be necessary to manually increase the reference count of an object, the DohIncref() function
can be used:
DOH *a = NewString("Hello");
DohIncref(a);
The representation and manipulation of types is currently in the process of being reorganized and (hopefully) simplified. The following list describes the current set of functions that are used to manipulate datatypes. These functions are different than in SWIG1.1 and may change names in the final SWIG1.3 release.
The intent of the lstr() function is to produce local variables inside wrapper functions--all of which must be reassignable types since they are the targets of conversions from a scripting representation.Original Datatype lstr() ------------------ -------- const char *a char *a double a[20] double *a double a[20][30] double *a double &a double *a
Original Datatype rcaststr() ------------------ --------- char *a const char *a (const char *) name double a[20] (double *) name double a[20][30] (double (*)[30]) name double &a (double &) *name
Original Datatype lcaststr() ------------------ --------- char *a const char *a (char *) name double a[20] (double *) name double a[20][30] (double *) name double &a (double *) &name
Here's how a wrapper function would be generated using the type generation functions above:int foo(int a, double b[20][30], const char *c, double &d);
wrapper_foo() {
lstr("int","result")
lstr("int","arg0")
lstr("double [20][30]", "arg1")
lstr("const char *", "arg2")
lstr("double &", "arg3")
...
get arguments
...
result = (lcaststr("int")) foo(rcaststr("int","arg0"),
rcaststr("double [20][30]","arg1"),
rcaststr("const char *", "arg2"),
rcaststr("double &", "arg3"))
...
}
Here's how it would look with the corresponding output filled in:
wrapper_foo() {
int result;
int arg0;
double *arg1;
char *arg2;
double *arg3;
...
get arguments
...
result = (int) foo(arg0,
(double (*)[30]) arg1,
(const char *) arg2,
(double &) *arg3);
...
}
Notes:
When SWIG generates wrappers, it tries to provide a mostly seamless integration with the original code. However, there are a number of problematic features of C/C++ programs that complicate this interface.
gets turned into a wrapper like this:double dot_product(Vector a, Vector b);
double wrap_dot_product(Vector *a, Vector *b) {
return dot_product(*a,*b);
}
Functions that return by value require a memory allocation to store the result. For example:
becomeVector cross_product(Vector *a, Vector *b);
Vector *wrap_cross_product(Vector *a, Vector *b) {
Vector *result = (Vector *) malloc(sizeof(Vector));
*result = cross_product(a,b);
return result;
}
Note: If C++ is being wrapped, the default copy constructor is used
instead of malloc() to create a copy of the return result.
class Foo {
public:
double bar(double);
};
The "bar" method is wrapped by a function like this:
double Foo_bar(Foo *self, double arg0) {
return self->bar(arg0);
}
struct Foo {
int x;
};
gets wrapped as follows:
int Foo_x_get(Foo *self) {
return self->x;
}
int Foo_x_set(Foo *self, int value) {
return (self->x = value);
}
Foo *new_Foo() {
return new Foo;
}
Note: For C, new objects are created using the calloc() function.
void delete_Foo(Foo *self) {
delete self;
}
Note: For C, objects are destroyed using free().
Here's how you might write a really simple wrapper functiondouble dot_product(Vector a, Vector b);
ParmList *l = ... parameter list of the function ...
DataType *t = ... return type of the function ...
char *name = ... name of the function ...
Wrapper *w = NewWrapper();
Printf(w->def,"void wrap_%s() {\n", name);
/* Declare all of the local variables */
Swig_cargs(w, l);
/* Convert all of the arguments */
...
/* Make the function call and declare the result variable */
Swig_cresult(w,t,"result",Swig_cfunction(name,l));
/* Convert the result into whatever */
...
Printf(w->code,"}\n");
Wrapper_print(w,out);
The output of this would appear as follows:
void wrap_dot_product() {
Vector *arg0;
Vector *arg1;
double result;
...
result = dot_product(*arg0, *arg1);
...
}
Notice that the Swig_cargs(), Swig_cresult(), and Swig_cfunction() functions
have taken care of the type conversions for the Vector type automatically.
Notes:
This section details guile-specific support in SWIG.
10.1 Meaning of "Module"
There are three different concepts of "module" involved, defined
separately for SWIG, Guile, and Libtool. To avoid horrible confusion,
we explicitly prefix the context, e.g., "guile-module".
10.2 Linkage
Guile support is complicated by a lack of user community cohesiveness,
which manifests in multiple shared-library usage conventions. A set of
policies implementing a usage convention is called a linkage.
The default linkage is the simplest; nothing special is done. In this
case SWIG_init() is provided and users must do something
like this:
At this time, the name(define my-so (dynamic-link "./example.so")) (dynamic-call "SWIG_init" my-so)
SWIG_init is hardcoded; this
approach does not work with multiple swig-modules. NOTE: The "simple"
and "matrix" examples under Examples/guile include guilemain.i; the
resulting standalone interpreter does not require calls to
dynamic-link and dynamic-call, as shown here.
A second linkage creates "libtool dl module" wrappers, and currently is broken. Whoever fixes this needs to track Guile's libtool dl module convention, since that is not finalized.
The only other linkage supported at this time creates shared object
libraries suitable for use by hobbit's (hobbit4d link)
guile module. This is called the "hobbit" linkage, and requires also
using the "-package" command line option to set the part of the module
name before the last symbol. For example, both command lines:
[checkme:ttn]
would create moduleswig -guile -package my/lib foo.i swig -guile -package my/lib -module foo foo.i
(my lib foo) (assuming in the first
case foo.i declares the module to be "foo"). The installed files are
my/lib/libfoo.so.X.Y.Z and friends. This scheme is still very
experimental; the (hobbit4d link) conventions are not well understood.
There are no other linkage types planned, but that could change... To
add a new type, add the name to the enum in guile.h and add the case to
GUILE::emit_linkage().
10.3 Underscore Folding
Underscores are converted to dashes in identifiers. Guile support may
grow an option to inhibit this folding in the future, but no one has
complained so far.
10.4 Typemaps
It used to be that the mappings for "native" types were included in guile.cxx. This information is now in Lib/guile/typemaps.i, which presents a new challenge: how to have SWIG include typemaps.i before processing the user's foo.i. At this time, we must say:
in foo.i. This may change in the future.%include guile/typemaps.i
For pointer types, SWIG uses Guile smobs. This feature used to be available only SWIG was invoked with the command-line option "-with-smobs", but as of 2000/07/06 (swig-1.3a4) this is the default behavior; the command-line option "-with-smobs" is ignored silently. We plan to drop silent recognition of this option after 1.3a4.
Currently, one wrapper module must be generated without
-c and compiled with -DSWIG_GLOBAL, all the
other wrapper modules must be generated with -c. Maybe
one should move all the global helper functions that come from
guile.swg into a library, which is built by make
runtime.
In earlier versions of SWIG, C pointers were represented as Scheme strings containing a hexadecimal rendering of the pointer value and a mangled type name. As Guile allows registering user types, so-called "smobs" (small objects), a much cleaner representation has been implemented now. The details will be discussed in the following.
A smob is a cons cell where the lower half of the CAR contains the
smob type tag, while the upper half of the CAR and the whole CDR are
available. SWIG_Guile_Init() registers a smob type named
"swig" with Guile; its type tag is stored in the variable
swig_tag. The upper half of the CAR store an index into
a table of all C pointer types seen so far, to which new types seen
are appended. The CDR stores the pointer value. SWIG smobs print
like this: #<swig struct xyzzy * 0x1234affe> Two of
them are equal? if and only if they have the same type
and value.
To construct a Scheme object from a C pointer, the wrapper code calls
the function SWIG_Guile_MakePtr_Str(), passing both a
mangled type string and a pretty type string. The former is looked up
in the type table to get the type index to store in the upper half of
the CAR. If the type is new, it is appended to type table.
To get the pointer represented by a smob, the wrapper code calls the
function SWIG_Guile_GetPtr_Str, passing the mangled name
of the expected pointer type, which is used for looking up the type in
the type table and accessing the list of compatible types. If the
Scheme object passed was not a SWIG smob representing a compatible
pointer, a wrong-type-arg exception is raised.
10.6 Exception Handling
SWIG code calls scm_error on exception, using the following
mapping:
MAP(SWIG_MemoryError, "swig-memory-error");
MAP(SWIG_IOError, "swig-io-error");
MAP(SWIG_RuntimeError, "swig-runtime-error");
MAP(SWIG_IndexError, "swig-index-error");
MAP(SWIG_TypeError, "swig-type-error");
MAP(SWIG_DivisionByZero, "swig-division-by-zero");
MAP(SWIG_OverflowError, "swig-overflow-error");
MAP(SWIG_SyntaxError, "swig-syntax-error");
MAP(SWIG_ValueError, "swig-value-error");
MAP(SWIG_SystemError, "swig-system-error");
The default when not specified here is to use "swig-error".
See Lib/exception.i for details.
10. Python Support
[TODO]
10. Perl Support
[TODO]
10. Java Support
[TODO]