wrappy - a Python wrapper generator for C++ classes

Author:Greg Couch
Email:gregc@cgl.ucsf.edu
Lab:UCSF Computer Graphics Lab
Copyright:

Copyright (c) 1996-2003 The Regents of the University of California. All rights reserved.

Redistribution and use in source and binary forms are permitted provided that the above copyright notice and this paragraph are duplicated in all such forms and that any documentation, distribution and/or use acknowledge that the software was developed by the Computer Graphics Laboratory, University of California, San Francisco. The name of the University may not be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE.

Overview

wrappy reads annotated C++ header files and generates a Python module containing a Python class or a Python type for each C++ class. Global functions are placed in the module as well.

C++ classes that subclass from otf::WrapPy<class> default to generating Python classes. C++ structs default to generating Python types. These defaults may be overridden by class annotations (see below). You should include <otf/WrapPy.h> to get the otf::WrapPy template base class.

Static C++ member functions are made in to Python module functions. The names are prepended with the C++ class name and an underscore. C++ class constants for Python types are likewise placed in the Python module. C++ constants in Python classes are accessed as part of the class.

Command Line Options

-d Generate a wrappy_doc dictionary in the module with information about the various classes and types in module.
-e tag Put the given tag before every exportable symbol in the generated header files. The tag is used for Windows OSes dynamic libraries.
-g minimum Set the minimum number of names in a class (attributes, member functions) needed to use a perfect hash function (from the gperf(1) program).
-h file Add extra header file to module.h
-i token Ignore token (e.g., a tag from a different module).
-m module-name Name of the Python module generated.
-n Don't generate any output, just check the input file(s).
-N namespace Place the generated code (except for init module!) into the given namespace.
-s class-name Generate code for a single class. The special name __module__ can be given to get the module level code.
-w Turn on warnings about get/set attribute types not matching. Not all mismatches are errors.
Deprecated:
-B Turn on fixes for Borland 5.[45] compilers (-DIT).
-D Turn on explicit naming of destructors.
-I Turn off inline in one case.
-T Turn on explict naming of template functions.

Caveats

Wrappy parses C++ as simply as it can. Consequently, some legal C++ header files can not be parsed correctly.

  • Global/namespace variables are not supported.
  • All function arguments must have a parameter name -- the name is used as the Python argument keyword.
  • Overloaded functions are supported but they can't have default arguments (this may change, the problem is interplay with keyword arguments).
  • Only one declaration per type (e.g., no int x, y;).
  • No combination typedef and struct declaration (i.e., separate typedef struct _t { ... } T; into struct _t { ... } and typedef struct _t T;)
  • No using namespace ``|NAME|;``.
  • Virtual functions repeated in subclasses will generate another entry point in the subclass.
  • Private base classes become public in Python.

Most other restrictions are bugs.

Attributes

C++ class public member variables are made into Python class/type attributes.

Paired class member functions are collapsed into Python attributes as well: [Gg]etNAME/[Ss]etNAME creates an NAME attribute. Likewise for NAME/[Ss]etNAME.

If you have only one half of the paired member functions, it can still be an attribute if it annotated as such (see below).

Annotations

All annotations are placed in C/C++ comments.

C++ Function Arguments

Arguments that are used return values must be annotated. Typically, these annotations are placed in the argument list as C-style comments so they be placed inline:

/*IN*/
This argument is input to the function (default).
/*OUT*/
This argument is output from the function.
/*INOUT*/
This argument is used for input to and output from the function.

C++ Class Annotations

// #include filename
Use filename as include file for class instead of the file the class definition was read from.
// WRAP CLASS
Generate a Python type or class for this C++ class even though it is in another namespace.
// PYTHON CLASS
Generate a Python class for this C++ class.
// PYTHON TYPE
Generate a Python type for this C++ class.
// ABSTRACT
Don't create a Python constructor for this C++ class. Don't generate a Python type for this class, but allow function arguments and return values to be of this class. Good for C++ abstract base classes.
// NUMBER METHODS
Generate Python interface for operator+, operator+, etc.
// SEQUENCE METHODS
Generate Python interface for len: size(), concat: operator+(?), repeat: operator*(?), getitem: operator[](int), setitem: ?& operator[](int). delitem is not supported. getslice and setslice are not supported. Python 1.6's contains is not supported.
// MAPPING METHODS
Generate Python interface for len: size(), getitem: operator[](?), setitem: ?& operator[](?). delitem is not supported.
// BUFFER PROCS
Recognized but not implemented.
// DON'T CACHE
Don't cache attributes of this C++ class.

Attribute Annotations

These annotations also go in the C++ class definition.

// ATTRIBUTE: name
Make the corresponding get/set function to name an attribute. (Not needed if both a get and a set function are present.)
// READONLY: name
Ignore the set function for named attribute.
// WRITEONLY: name
Ignore the get function for named attribute.
// WEAKREF: name
Don't cache references for named attribute.

Global/Namespace Annotations

// #include filename
Use filename as include file for following classes instead of the file the class definition was read from.

C Language preprocessor

wrappy understands that it should ignore declarations within:

#ifndef WrapPy
or
#if 0

and ending with an #else clause or #endif. All of the declarations inside the #if*/(#else|#endif) must parse. #elif clauses are recognized, but not recommended, because the contents of all clauses are added to the parsed declarations.

Nested #if*'s are recognized.

Bugs

Containers are not supported as input to functions.

using directive only takes names, not function prototypes;

Constructor try blocks are not recognized.

Function exception specifications are not used.

Function pointers are not supported.

Wrappy finds the operator<<(?, type) form and trys to create a __rlshift__ operator for unwrapped ?.

All of the clauses of an #if``* must be in the same scope (*e.g.*, if the ``#endif is inside a class declaration, then the corresponding #if* must be too).

And more....

Extra Software Wrappy Uses

Gperf
Wrappy uses the gperf perfect hash function generator to speed up attribute access in Python types. See http://www.gnu.org/directory/GNU/gperf.html.

Example

Example command line usage:

wrappy -g 4 -N example -n example example.h

Example input file:

namespace example {

class Info {
        // #include "Example.h"
public:
        ....
};

typedef std::vector<Info *> InfoList;

class DB {
        // #include "Example.h"
public:
        // infos will be an attribute
        void infos(/*OUT*/ const InfoList *infolist) const;
        void setInfos(const InfoList &infolist);
};