From aabec2c542019a9fe03d83a9be88b4a704eecd92 Mon Sep 17 00:00:00 2001 From: Mark Gossage Date: Mon, 13 Feb 2006 02:36:05 +0000 Subject: [PATCH] update to extending document git-svn-id: https://swig.svn.sourceforge.net/svnroot/swig/trunk/SWIG@8809 626c5289-ae23-0410-ae9c-e8d60b6d4f22 --- CHANGES.current | 3 + Doc/Manual/Extending.html | 442 +++++++++++++++++++++++++++++++------- 2 files changed, 368 insertions(+), 77 deletions(-) diff --git a/CHANGES.current b/CHANGES.current index 96763fb7f..f5cec8aee 100644 --- a/CHANGES.current +++ b/CHANGES.current @@ -1,4 +1,7 @@ Version 1.3.29 (In progress) ============================ +02/13/2006: mgossage + [Documents] updated the extending documents to give a skeleton swigging code + with a few typemaps. diff --git a/Doc/Manual/Extending.html b/Doc/Manual/Extending.html index da1657787..15c9646ce 100644 --- a/Doc/Manual/Extending.html +++ b/Doc/Manual/Extending.html @@ -147,7 +147,7 @@ Code generation modules can look for these attributes to guide the wrapping proc
  • Class extension. SWIG allows classes and structures to be extended with new -methods and attributes (the %extend directive). This has the effect of altering +methods and attributes (the %extend directive). This has the effect of altering the API in the target language and can be used to generate OO interfaces to C libraries. @@ -303,8 +303,8 @@ subtle aspects of how SWIG parses declarations. Specifically, SWIG expects all datatype such as int or void. type may be optionally qualified with a qualifier such as const or volatile. declarator is a name with additional type-construction modifiers attached to it (pointers, arrays, references, -functions, etc.). Examples of declarators include *x, **x, x[20], and -(*x)(int,double). The initializer may be a value assigned using = or +functions, etc.). Examples of declarators include *x, **x, x[20], and +(*x)(int,double). The initializer may be a value assigned using = or body of code enclosed in braces { ... }.

    @@ -332,13 +332,13 @@ been previously defined as types either using a class definition or a typedef. The reasons for this are subtle, but this treatment of typenames is normally integrated at the level of the C tokenizer---when a typename appears, a different token is returned to the parser -instead of an identifier. +instead of an identifier.

    SWIG does not operate in this manner--any legal identifier can be used as a type name. The reason for this is primarily motivated by the use -of SWIG with partially defined data. Specifically, +of SWIG with partially defined data. Specifically, SWIG is supposed to be easy to use on interfaces with missing type information.

    @@ -462,10 +462,10 @@ $ swig -c++ -python -dump_tree example.i +++ module ---------------------------------------- | name - "example" - | + | +++ insert ---------------------------------------- | code - "\n#include \"example.h\"\n" - | + | +++ include ---------------------------------------- | name - "example.h" @@ -479,63 +479,63 @@ $ swig -c++ -python -dump_tree example.i +++ access ---------------------------------------- | kind - "public" - | + | +++ constructor ---------------------------------------- | sym:name - "Shape" | name - "Shape" | decl - "f()." | code - "{\n nshapes++;\n }" | sym:symtab - 0x40194140 - | + | +++ destructor ---------------------------------------- | sym:name - "~Shape" | name - "~Shape" | storage - "virtual" | code - "{\n nshapes--;\n }" | sym:symtab - 0x40194140 - | + | +++ cdecl ---------------------------------------- | sym:name - "x" | name - "x" | decl - "" | type - "double" | sym:symtab - 0x40194140 - | + | +++ cdecl ---------------------------------------- | sym:name - "y" | name - "y" | decl - "" | type - "double" | sym:symtab - 0x40194140 - | + | +++ cdecl ---------------------------------------- | sym:name - "move" | name - "move" | decl - "f(double,double)." - | parms - double ,double + | parms - double ,double | type - "void" | sym:symtab - 0x40194140 - | + | +++ cdecl ---------------------------------------- | sym:name - "area" | name - "area" | decl - "f(void)." - | parms - void + | parms - void | storage - "virtual" | value - "0" | type - "double" | sym:symtab - 0x40194140 - | + | +++ cdecl ---------------------------------------- | sym:name - "perimeter" | name - "perimeter" | decl - "f(void)." - | parms - void + | parms - void | storage - "virtual" | value - "0" | type - "double" | sym:symtab - 0x40194140 - | + | +++ cdecl ---------------------------------------- | sym:name - "nshapes" | name - "nshapes" @@ -543,7 +543,7 @@ $ swig -c++ -python -dump_tree example.i | storage - "static" | type - "int" | sym:symtab - 0x40194140 - | + | +++ class ---------------------------------------- | sym:name - "Circle" | name - "Circle" @@ -554,41 +554,41 @@ $ swig -c++ -python -dump_tree example.i +++ access ---------------------------------------- | kind - "private" - | + | +++ cdecl ---------------------------------------- | name - "radius" | decl - "" | type - "double" - | + | +++ access ---------------------------------------- | kind - "public" - | + | +++ constructor ---------------------------------------- | sym:name - "Circle" | name - "Circle" - | parms - double + | parms - double | decl - "f(double)." | code - "{ }" | sym:symtab - 0x40194538 - | + | +++ cdecl ---------------------------------------- | sym:name - "area" | name - "area" | decl - "f(void)." - | parms - void + | parms - void | storage - "virtual" | type - "double" | sym:symtab - 0x40194538 - | + | +++ cdecl ---------------------------------------- | sym:name - "perimeter" | name - "perimeter" | decl - "f(void)." - | parms - void + | parms - void | storage - "virtual" | type - "double" | sym:symtab - 0x40194538 - | + | +++ class ---------------------------------------- | sym:name - "Square" | name - "Square" @@ -599,37 +599,37 @@ $ swig -c++ -python -dump_tree example.i +++ access ---------------------------------------- | kind - "private" - | + | +++ cdecl ---------------------------------------- | name - "width" | decl - "" | type - "double" - | + | +++ access ---------------------------------------- | kind - "public" - | + | +++ constructor ---------------------------------------- | sym:name - "Square" | name - "Square" - | parms - double + | parms - double | decl - "f(double)." | code - "{ }" | sym:symtab - 0x40194788 - | + | +++ cdecl ---------------------------------------- | sym:name - "area" | name - "area" | decl - "f(void)." - | parms - void + | parms - void | storage - "virtual" | type - "double" | sym:symtab - 0x40194788 - | + | +++ cdecl ---------------------------------------- | sym:name - "perimeter" | name - "perimeter" | decl - "f(void)." - | parms - void + | parms - void | storage - "virtual" | type - "double" | sym:symtab - 0x40194788 @@ -691,18 +691,18 @@ $ swig -dump_tree example.i | sym:name - "foo_i" | name - "foo" | decl - "f(int)." - | parms - int + | parms - int | type - "void" | sym:symtab - 0x40165078 - | + | +++ cdecl ---------------------------------------- | sym:name - "foo_d" | name - "foo" | decl - "f(double)." - | parms - double + | parms - double | type - "void" | sym:symtab - 0x40165078 - | + | +++ cdecl ---------------------------------------- | sym:name - "foo" | name - "foo" @@ -783,18 +783,18 @@ You can see this when running with the -dump_tree option. For example | sym:name - "getitem" | name - "getitem" | decl - "f(int).p." - | parms - int + | parms - int | type - "Object" | feature:except - "{\n try {\n $action\n } catc..." | sym:symtab - 0x40168ac8 - | + |

    Feature names are completely arbitrary and a target language module can be -programmed to respond to any feature name that it wants to recognized. The -data stored in a feature attribute is usually just a raw unparsed string. +programmed to respond to any feature name that it wants to recognized. The +data stored in a feature attribute is usually just a raw unparsed string. For example, the exception code above is simply stored without any modifications.

    @@ -805,7 +805,7 @@ stored without any modifications.

    Language modules work by defining handler functions that know how to respond to different types of parse-tree nodes. These handlers simply look at the -attributes of each node in order to produce low-level code. +attributes of each node in order to produce low-level code.

    @@ -887,7 +887,7 @@ node is then used to generate a wrapper.

    Language modules work by registering handler functions for dealing with -various types of nodes at different stages of transformation. This is done by +various types of nodes at different stages of transformation. This is done by inheriting from a special Language class and defining a collection of virtual methods. For example, the Python module defines a class as follows: @@ -899,7 +899,7 @@ class PYTHON : public Language { protected: public : virtual void main(int, char *argv[]); - virtual int top(Node *); + virtual int top(Node *); virtual int functionWrapper(Node *); virtual int constantWrapper(Node *); virtual int variableWrapper(Node *); @@ -965,7 +965,7 @@ used:

    -In most cases, other typenames in the source are aliases for one of these +In most cases, other typenames in the source are aliases for one of these primitive types. Specifically:

    @@ -1103,7 +1103,7 @@ Chops trailing whitespace off the end of s.

    -Replaces the pattern pat with rep in string s. +Replaces the pattern pat with rep in string s. flags is a combination of the following flags:

    @@ -1248,7 +1248,7 @@ negative, the first item is returned.

    -Stores val in x. +Stores val in x. If val is not a standard object (String, Hash, or List) it is assumed to be a char * in which case it is used to construct a String that is stored in the list. @@ -1260,7 +1260,7 @@ case it is used to construct a String that is stored in the list.

    -Deletes item n from the list, shifting items down if necessary. +Deletes item n from the list, shifting items down if necessary. To delete the last item in the list, use the special value DOH_END for n.
    @@ -1400,7 +1400,7 @@ work on C FILE * objects, String objects, and special File obj
    Formatted I/O. Same as the C fprintf() function except that output -can also be directed to a string object. Note: the %s format +can also be directed to a string object. Note: the %s format specifier works with both strings and char *. All other format operators have the same meaning.
    @@ -1480,7 +1480,7 @@ Same as the C tell() function.
    Create a File object using the fopen() library call. This file differs from FILE * in that it can be placed in the standard -SWIG containers (lists, hashes, etc.). +SWIG containers (lists, hashes, etc.).

    @@ -1582,7 +1582,7 @@ Returns the parent of node n. Use this to move up the pass tree.

    -The following macros can be used to change all of the above attributes. +The following macros can be used to change all of the above attributes. Normally, these functions are only used by the parser. Changing them without knowing what you are doing is likely to be dangerous.

    @@ -1678,7 +1678,7 @@ int functionHandler(Node *n) {

    New attributes can be freely attached to a node as needed. However, when new attributes -are attached during code generation, they should be prepended with a namespace prefix. +are attached during code generation, they should be prepended with a namespace prefix. For example:

    @@ -1715,10 +1715,10 @@ the following functions are used:

    -Saves a copy of attributes name1, name2, etc. from node n. -Copies of the attributes are actually resaved in the node in a different namespace which is -set by the ns argument. For example, if you call Swig_save("foo",n,"type",NIL), -then the "type" attribute will be copied and saved as "foo:type". The namespace name itself is stored in +Saves a copy of attributes name1, name2, etc. from node n. +Copies of the attributes are actually resaved in the node in a different namespace which is +set by the ns argument. For example, if you call Swig_save("foo",n,"type",NIL), +then the "type" attribute will be copied and saved as "foo:type". The namespace name itself is stored in the "view" attribute of the node. If necessary, this can be examined to find out where previous values of attributes might have been saved.
    @@ -1812,7 +1812,7 @@ vector<int> * "p.vector<(int)>"

    Reading the SWIG encoding is often easier than figuring out the C code---just read it from left to right. For a type of "p.f(int,double).int" is -a "pointer to a function(int,double) that returns int". +a "pointer to a function(int,double) that returns int".

    @@ -2016,7 +2016,7 @@ opposite of SwigType_pop().

    -Removes all leading array operators from ty and returns them. +Removes all leading array operators from ty and returns them. ty is modified. For example, if ty is "a(20).a(10).p.int", then this function would return "a(20).a(10)." and modify ty so that it has the value "p.int". @@ -2137,7 +2137,7 @@ Checks if ty is a varargs type.

    -Checks if ty is a templatized type. +Checks if ty is a templatized type.

    32.8.4 Typedef and inheritance

    @@ -2145,7 +2145,7 @@ Checks if ty is a templatized type.

    The behavior of typedef declaration is to introduce a type alias. -For instance, typedef int Integer makes the identifier +For instance, typedef int Integer makes the identifier Integer an alias for int. The treatment of typedef in SWIG is somewhat complicated due to the pattern matching rules that get applied in typemaps and the fact that SWIG prefers to generate wrapper code @@ -2174,7 +2174,7 @@ produces two trees like this:

                      int               p.Integer
    -               ^  ^  ^                 ^ 
    +               ^  ^  ^                 ^
                   /   |   \                |
                  /    |    \               |
             Integer  Size   Number    IntegerPtr
    @@ -2196,7 +2196,7 @@ returns the new type.  If not, returns NULL.
     
     

    Typedefs are only resolved in simple typenames that appear in a type. -For example, the type base name and in function parameters. When +For example, the type base name and in function parameters. When resolving types, the process starts in the leaf nodes and moves up the tree towards the root. Here are a few examples that show how it works:

    @@ -2248,7 +2248,7 @@ will consist only of primitive typenames.

    When generating wrapper code, it is necessary to emit datatypes that can be used on the left-hand side of an assignment operator (an lvalue). However, -not all C datatypes can be used in this way---especially arrays and +not all C datatypes can be used in this way---especially arrays and const-qualified types. To generate a type that can be used as an lvalue, use the following function:

    @@ -2260,7 +2260,7 @@ use the following function:
    Converts type ty to a type that can be used as an lvalue in assignment. The resulting type is stripped of qualifiers and arrays are -converted to a pointers. +converted to a pointers.

    @@ -2488,7 +2488,7 @@ public: printf("Generating code.\n"); return SWIG_OK; } - + }; extern "C" Language * @@ -2578,7 +2578,7 @@ messages from your new module should appear.

    -When SWIG starts, the command line options are passed to your language module. This occurs +When SWIG starts, the command line options are passed to your language module. This occurs before any other processing occurs (preprocessing, parsing, etc.). To capture the command line options, simply use code similar to this:

    @@ -2648,11 +2648,11 @@ void main(int argc, char *argv[]) { ... command line options ... /* Set language-specific subdirectory in SWIG library */ - SWIG_library_directory("python"); + SWIG_library_directory("python"); /* Set language-specific preprocessing symbol */ Preprocessor_define("SWIGPYTHON 1", 0); - + /* Set language-specific configuration file */ SWIG_config_file("python.swg"); @@ -2663,7 +2663,7 @@ void main(int argc, char *argv[]) {

    -The above code does several things--it registers the name of the +The above code does several things--it registers the name of the language module with the core, it supplies some preprocessor macro definitions for use in input files (so that they can determine the target language), and it registers a start-up file. In this case, the file python.swg will @@ -2673,7 +2673,7 @@ be parsed before any part of the user-supplied input file.

    Before proceeding any further, create a directory for your module in the SWIG library (The Lib directory). Now, create a configuration file in the -directory. For example, python.swg. +directory. For example, python.swg.

    @@ -2717,7 +2717,7 @@ An outline of top() might be as follows: int Python::top(Node *n) { /* Get the module name */ - String *module = Getattr(n,"name"); + String *module = Getattr(n,"name"); /* Get the output file name */ String *outfile = Getattr(n,"outfile"); @@ -2729,7 +2729,7 @@ int Python::top(Node *n) { ... /* Emit code for children */ - Language::top(n); + Language::top(n); ... /* Cleanup files */ @@ -2742,9 +2742,297 @@ int Python::top(Node *n) {

    32.10.6 Module I/O and wrapper skeleton

    + + + +

    +Within SWIG wrappers, there are four main sections. These are (in order) + +

      +
    • runtime: This section has most of the common SWIG runtime code +
    • header: This section holds declarations and inclusions from the .i file +
    • wrapper: This section holds all the wrappering code +
    • init: This section holds the module initalisation function +(the entry point for the interpreter) +
    +

    +

    +Different parts of the SWIG code will fill different sections, +then upon completion of the wrappering all the sections will be saved +to the wrapper file. +

    +

    +To perform this will require several additions to the code in various places, +such as: +

    + +
    +
    +class PYTHON : public Language {
    +protected:
    +   /* General DOH objects used for holding the strings */
    +   File *f_runtime;
    +   File *f_header;
    +   File *f_wrappers;
    +   File *f_init;
    +
    +public:
    +   ...
    +
    +};
    +
    +int Python::top(Node *n) {
    +
    +   ...
    +
    +   /* Initialize I/O */
    +   f_runtime = NewFile(outfile, "w");
    +   if (!f_runtime) {
    +      FileErrorDisplay(outfile);
    +      SWIG_exit(EXIT_FAILURE);
    +   }
    +   f_init = NewString("");
    +   f_header = NewString("");
    +   f_wrappers = NewString("");
    +
    +   /* Register file targets with the SWIG file handler */
    +   Swig_register_filebyname("header", f_header);
    +   Swig_register_filebyname("wrapper", f_wrappers);
    +   Swig_register_filebyname("runtime", f_runtime);
    +   Swig_register_filebyname("init", f_init);
    +
    +   /* Output module initialization code */
    +   ...
    +
    +   /* Emit code for children */
    +   Language::top(n);
    +
    +   ...
    +   /* Write all to the file */
    +   Dump(f_header, f_runtime);
    +   Dump(f_wrappers, f_runtime);
    +   Wrapper_pretty_print(f_init, f_runtime);
    +
    +   /* Cleanup files */
    +   Delete(f_header);
    +   Delete(f_wrappers);
    +   Delete(f_init);
    +   Close(f_runtime);
    +   Delete(f_runtime);
    +
    +   return SWIG_OK;
    +}
    +
    +
    + +

    +Using this to process a file will generate a wrapper file, however the +wrapper will only consist of the common SWIG code as well as any inline +code which was written in the .i file. It does not contain any wrappers for +any of the functions of classes. +

    + +

    +The code to generate the wrappers are the various member functions, which +currently have not been touched. We will look at functionWrapper() as this +is the most commonly used function. In fact many of the other wrapper routines +will call this to do their work. +

    +

    +A simple modification to write some basic details to the wrapper looks like this: +

    + +
    +
    +int Python::functionWrapper(Node *n) {
    +  /* Get some useful attributes of this function */
    +  String   *name   = Getattr(n,"sym:name");
    +  SwigType *type   = Getattr(n,"type");
    +  ParmList *parms  = Getattr(n,"parms");
    +  String   *parmstr= ParmList_str_defaultargs(parms); // to string
    +  String   *func   = SwigType_str(type, NewStringf("%s(%s)", name, parmstr));
    +  String   *action = Getattr(n,"wrap:action");
    +
    +  Printf(f_wrappers,"functionWrapper   : %s\n", func);
    +  Printf(f_wrappers,"           action : %s\n", action);
    +  return SWIG_OK;
    +}
    +
    +
    + +

    +This will now produce some useful information within your wrapper file. +

    + +
    +
    +functionWrapper   : void delete_Shape(Shape *self)
    +           action : delete arg1;
    +
    +functionWrapper   : void Shape_x_set(Shape *self,double x)
    +           action : if (arg1) (arg1)->x = arg2;
    +
    +functionWrapper   : double Shape_x_get(Shape *self)
    +           action : result = (double) ((arg1)->x);
    +
    +functionWrapper   : void Shape_y_set(Shape *self,double y)
    +           action : if (arg1) (arg1)->y = arg2;
    +...
    +
    +

    32.10.7 Low-level code generators

    + + +

    +As ingenious as SWIG is, and despite all its capabilities and the power of +its parser, the Low-level code generation takes a lot of work to write +properly. Mainly because every language insists on its own manner of +interfacing to C/C++. To write the code generators you will need a good +understanding of how to manually write an interface to your chosen +language, so make sure you have your documentation handy. +

    +

    +At this point is also probably a good idea to take a very simple file +(just one function), and try letting SWIG generate up wrappers for many +different languages. Take a look at all of the wrappers generated, and decide +which one looks closest to the language you are trying to wrapper. +This may help you to decide which code to look at. +

    +

    +In general most language wrappers look a little like this: +

    +
    +
    +/* wrapper for TYPE3 some_function(TYPE1,TYPE2); */
    +RETURN_TYPE _wrap_some_function(ARGS){
    +  TYPE1 arg1;
    +  TYPE2 arg2;
    +  TYPE3 result;
    +
    +  if(ARG1 is not of TYPE1) goto fail;
    +  arg1=(convert ARG1);
    +  if(ARG2 is not of TYPE2) goto fail;
    +  arg2=(convert ARG2);
    +
    +  result=some_function(arg1,arg2);
    +
    +  convert 'result' to whatever the language wants;
    +
    +  do any tidy up;
    +
    +  return ALL_OK;
    +
    +  fail:
    +  do any tidy up;
    +  return ERROR;
    +}
    +
    +
    + +

    +Yes, it is rather vague and not very clear. But each language works differently +so this will have to do for now. +

    +

    +To tackle this problem will be done in two stages: +

      +
    • The skeleton: the function wrapper, and call, but without the conversion +
    • The conversion: converting the arguments to-from what the language wants +
    +

    +

    +The first step will be done in the code, the second will be done in typemaps. +

    +

    +Our first step will be to write the code for functionWrapper(). What is +shown below is _NOT_ the solution, merely a step in the right direction. +There are a lot of issues to address. +

    +
      +
    • Variable length and default parameters +
    • Typechecking and number of argument checks +
    • Overloaded functions +
    • Inout and Output only arguments +
    +
    +
    +virtual int functionWrapper(Node *n) {
    +  /* get useful atributes */
    +  String   *name   = Getattr(n,"sym:name");
    +  SwigType *type   = Getattr(n,"type");
    +  ParmList *parms  = Getattr(n,"parms");
    +  ...
    +
    +  /* create the wrapper object */
    +  Wrapper *wrapper = NewWrapper();
    +
    +  /* create the functions wrappered name */
    +  String *wname = Swig_name_wrapper(iname);
    +
    +  /* deal with overloading */
    +  ....
    +
    +  /* write the wrapper function definition */
    +  Printv(wrapper->def,"RETURN_TYPE ", wname, "(ARGS) {",NIL);
    +
    +  /* if any additional local variable needed, add them now */
    +  ...
    +
    +  /* write the list of locals/arguments required */
    +  emit_args(type, parms, wrapper);
    +
    +  /* check arguments */
    +  ...
    +
    +  /* write typemaps(in) */
    +  ....
    +
    +  /* write constriants */
    +  ....
    +
    +  /* Emit the function call */
    +  emit_action(n,wrapper);
    +
    +  /* return value if necessary  */
    +  ....
    +
    +  /* write typemaps(out) */
    +  ....
    +
    +  /* add cleanup code */
    +  ....
    +
    +  /* Close the function(ok) */
    +  Printv(wrapper->code, "return ALL_OK;\n", NIL);
    +
    +  /* add the failure cleanup code */
    +  ...
    +
    +  /* Close the function(error) */
    +  Printv(wrapper->code, "return ERROR;\n", "}\n", NIL);
    +
    +  /* final substititions if applicable */
    +  ...
    +
    +  /* Dump the function out */
    +  Wrapper_print(wrapper,f_wrappers);
    +
    +  /* tidy up */
    +  Delete(wname);
    +  DelWrapper(wrapper);
    +
    +  return SWIG_OK;
    +}
    +
    +
    + +

    +Executing this code will produce wrappers which have our basic skeleton +but without the typemaps, there are still work to do. +

    +

    32.10.8 Configuration files

    @@ -2827,7 +3115,7 @@ the code, ping me and I'll expand on this further. --ttn]

    Makefile.in -
    +

    Some of the variables AC_SUBSTitutued are essential to the @@ -3203,7 +3491,7 @@ A forward C++ class declaration.

    -Code insertion directive. For example, %{ ... %} or +Code insertion directive. For example, %{ ... %} or %insert(section).