swipl - pwig wrapper generator for SWI-Prolog
$ pwig -swipl -c++ foo.i $ g++ -c foo_wrap.cxx $ g++ foo_wrap.o foo.o -o foo.so $ pl -g '[config, foo], foo_bar(hello(world))'
The swipl module generates SWI-Prolog wrappers for C/C++ code. C structs and C++ classes can be wrapped as Logtalk objects.
The pwig swipl module is invoked as:
$ pwig -swipl foo.i
for C++ code the c++
option has to be used:
$ pwig -swipl -c++ foo.i
and to activate Logtalk proxy generation use the -proxy
option:
$ pwig -swipl -c++ -proxy foo.i
Usually you copy this file as config.pl
and edit it to suite your
installation. Particularly, you have to change the Logtalk path to
point to the place where you have unpacked it.
If you want to distribute the generated prolog module as an stand
alone one (without pwig dependencies) you have to copy the runtime
files from the pwig library and change the config.pl
file
accordingly.
%module
pwig directive)
is created. It is used to bootstrap the C/C++ module and load all the
runtime components (also Logtalk when needed).
.lgt
files are generated under a
directory with the module name (the one used on the %module
pwig
directive).
For every C++ class, two Logtalk classes are generated, a class object proxying the normal method calls and a metaclass one for the constructors and the C++ static methods.
.c
or .cxx
file with the wrapper code is generated, you have
to compile this file and link it with the rest of your object files to
contruct the loadable .so
module (or whatever dynamic modules are
called on your system).
Look at the foo
example Makefile to see some working rules for
module building.
Compile the generated .c
or .cxx
file as any other C/C++ file, i.e:
$ g++ -c foo_wrap.cxx
You will have to use the linker adequate for the language used to write the extension, C or C++. If you use the C linker for a C++ written extension, C++ support libraries will not be included and later the prolog proccess will refuse to load the module with ``undefined symbol'' errors.
Remember also to add the linker objects for your own code and any required library. If you forget some library, the linker will succeed anyway but later the module will not load on the prolog proccess.
A sample linking session:
$ g++ -Xlinker -shared foo.o foo_wrap.o -lbar -o foo.so
First, load the configuration file:
[config].
then load the prolog module:
[foo].
or in one step:
[config, foo].
When using Logtalk, a lot of information about modules being compiled is generated. This output will get (optionally) suppresed on a future release when swipl becomes more stable!
constant support is still unimplemented
C/C++ wrapped functions appear on prolog as predicates with the same name.
Predicates wrapping a function with a non void type (returning a value) get an additional argument appended for the result:
C function...
int strlen(char *str)
is wrapped as prolog predicate...
strlen(Str, Result)
but...
void foo(char *bar)
becomes...
foo(Bar)
Result values are not assigned but unified, so the previously defined
strlen
predicate can be called as...
strlen(hello, 5).
and will succeed (for most strlen
implementations!)
Overloaded C++ functions as wrapped on prolog as a common predicate and the adequate C++ function will be called... most of the time!
SWI-Prolog doesn't provide any support for foreign objects so they have to be encoded as atoms, but them there is not infallible way to know if an atom is really an object reference or a normal atom.
A simple heuristic is used to differentiate:
if it looks like a pwig object reference it is a pwig object reference!
or if you prefer it expressed this way...
is_object_reference(Atom) :- looks_like_object_reference(Atom).
What this means is that unless you start using atoms like _p_foo_73879_ on your apps, function overloading it's going to work as expected.
Anyway, you can use the %rename
directive to stop the overloading
dispatching from happening at all (consult the SWIG documentation
about overloading).
Structs and classes are represented in prolog as atoms with the type and the pointer value encoded on it (read the SWIG documentation for the details).
C++ class methods are wrapped as prolog predicates with the class name as prefix and a first argument containing the object reference:
C method...
int foo::hello(int a)
is wrapped as...
foo_hello(Foo, A, Result)
Static class methods are wrapped as normal functions but with the class name prepended:
C method...
static void foo::bye(int b)
is wrapped as...
foo_bye(B)
Constructor and destructors are made available on prolog:
for...
struct foo { int slot; };
on prolog...
foo_new(Foo).
is used to allocate a new struct foo
, and Foo
will contain an
encoded reference to it.
foo_delete(Foo).
is used to destroy and deallocate the object.
For C++ classes, constructors are made available if they are public and the class is not abstract. Constructors accepting arguments return the created object as its *LAST* argument:
for...
class bar { public: bar(int a, int b=5); char *data; };
predicates...
bar_new(A, Bar) bar_new(A, B, Bar) bar_delete(Bar)
are created.
Accessor predicates are created for public data members, i.e:
foo_slot_set(new_slot_value) foo_slot_get(Slot_value) bar_data_set(v) bar_data_get(V)
Logtalk is used as an additional layer to wrap C++ classes (and C structs) as objects in prolog.
The new
class method predicate is used to create new objects:
for C++ class...
class foo { public: foo(int arg1=4); foo(char *hello); void say(char *str); int croak(char *str); static int get_number(); };
objects are created on prolog as
foo::new(Foo). foo::new(Foo, 4). foo::new(Foo, hello_me).
notice, that with Logtalk, the object reference is passed as the *FIRST* argument on the constructors.
A nonvar term can also be used as the first argument, then the object gets binded to this term:
foo::new(my_foo), my_foo::say(bye).
Objects are deleted with the delete predicate:
my_foo::delete.
Methods are invoked as normal Logtalk methods. If the C++ method has a return value, an additional last predicate argument is used to return it:
my_foo::say(hello). my_foo::croak('I am a frog!', Out).
static class methods are exported to prolog as methods of the class object:
foo::get_number(Result).
Objects have to be explicitely deallocated as in C++, there is not garbage collection for Logtalk objects.
Object creation and destruction on Logtalk is extremely inefficient. Trace from prolog the creation of a C++ wrapped object like...
:- trace. [trace] :- foo::new(Foo).
... and see what I'm talking about!!!
The problem is that Logtalk is not optimized to work with dynamic objects but static ones.
I think that the solution is to replace Logtalk with another lighter OO layer... like plo ;-)
ideas and contributions are welcome!
Exception handling is still unimplemented.
Actually, when a predicate is called with the wrong types it just fails.
To be written
pwig project home at http://pwig.sourceforge.net.
SWIG home at http://www.swig.org.
SWI-Prolog home at http://www.swi-prolog.org.
Logtalk home at http://www.logtalk.org.
Salvador Fandiņo <sfandino@yahoo.com>
Copyright 2003, 2004 by Salvador Fandiņo
This module is free software; you can redistribute it and/or modify it under the same terms as pwig itself.