Yet ANother Interface between C++ and python

Warning

Very experimental. I probably shouldn’t have written my own code to interface python and C++, but I did anyway, and here it is.

yanic: <interface file> <c++ output prefix> <python prefix> <rst prefix>

Guide for interface files

  • [] arguments are optional

  • <> arguments are required and do not have any whitespace

  • {} arguments can have whitespace but no carriage returns

Types, e.g. {type}, {parameter type}, {return type} are specified in the following way:

[static] [const] [std::shared_ptr] <type name> [*] [&] [**]

Non-alphabetic characters in class names are always converted to underscores. Other type decorators are not yet supported.

Comments (all lines beginning with # are comments). Comments may appear anywhere, including inside a class or function definition.

Header items

  • Namespace specification:

    namespace <name>
    

    The namespace is used to name the automatically name the extern C functions which access the class. The namespace is assumed to apply to all classes and functions which follow.

  • Template for class documentation, using %name% to refer to the class name. Specified either as:

    py_class_doc {}
    

    or as:

    py_class_doc |
    | {}
    | {}
    ...
    
  • Name of dll to load:

    dll_name <name>
    

    This is the name of the python data member in the link class which holds the ctypes.CDLL object.

  • Header for .rst files:

    rst_header {}
    

    or:

    rst_header |
    | {}
    | {}
    ...
    
  • Include statements for C++ header file:

    h_include <file (including quotes or angle brackets)>
    

    Many include files can be specified with many include statements and all of them will be included at the top of the generated .h files.

  • Include statement for C++ source code:

    cpp_include <file (including quotes or angle brackets)>
    

    Many include files can be specified with many include statements and all of them will be included at the top of the generated .cpp files. At least one is required to find the generated .h file.

  • Namespaces to use in C++ source code:

    cpp_using <namespace>
    
  • Additional python header lines:

    py_header {}
    

    For example, this can include import statements or definitions of other global objects.

Functions

  • Function definitions:

    function <function name>
    - {return type}
    - py_name <python name>
    - {parameter type} <parameter name> [default value]
    ...
    

    For each function, an extern C wrapper is created with the suffix _wrapper and then a python function is created to call that wrapper. The python name is optional, but when present must be after the return type and before the variable list.

Classes

  • Class definitions:

    class <class name> ["abstract"]
    

    If the abstract label is appended, then __init__() is tagged as an @abstractmethod in python. If the class is not abstract and the no_def_cons tag is not given (see below), then a create function is created to create an object. A free function is always created to destroy an object. If std_cc is specified, then a C function named copy is created along with an analogous __deepcopy__ python method which wraps around the default C++ copy constructor.

  • Python name of class (optional):

    - py_name <name>
    
  • Optional line which should be specified if the class defines both class (const class &) and class &operator=(const class &). This allows one to define the python __deepcopy__ method:

    - std_cc
    
  • Optional line which should be specified if the class has no default constructor:

    - no_def_cons
    
  • Parent class (multiple parents not currently supported):

    - parent <parent class name>
    
  • Python documentation for this class (overrides template specification above which doesn’t have a hyphen):

    - py_class_doc {}
    

    or:

    - py_class_doc |
    | {}
    | {}
    ...
    
  • Class member data:

    - {type} <name>
    

    Get and set methods for class member data are generated. For standard C types, std::string objects, and shared pointer objects, the get and set methods pass by value (i.e. they imply a copy). For classes from the interface, the get methods return references and the set methods pass by value.

  • Class member function definitions are of the following form. The return type and parameter specifications must begin with two spaces:

    - function <function name>
      - {return type}
      - {parameter type} <parameter name> [default value]
      ...
    
  • Extra python code for the class:

    - extra_py {}
    

    or:

    - extra_py |
    | {}
    | {}
    ...
    

    The extra python code is prepended by four spaces to conform with the indentation style used by yanic.

  • Class constructor with parameters. The parameter specifications must begin with two spaces:

    - cons <python constructor name>
      - py_name <python name>
      - {parameter type} <parameter name>
      ...
    

Other objects

  • Specification of a shared pointer:

    - shared_ptr <class name>
    

    Shared pointers imply the creation of a create function to create a shared pointer to a default object, a free function to free the memory associated with the shared pointer (which may or may not free the underlying object), and a pointer function which gets a raw pointer to the underlying object. Using shared pointers for objects which do not have a default constructor is not yet supported.

    • Python name of class for the shared pointer (must begin with two spaces):

      - py_name <name>
      

Constraints

  • Global functions and member functions may be overloaded, but only if they are given different python names.

Todos

Todo

In yanic:

  • Need to fix function names in case where there is no namespace.

  • Simplify code duplication in parsing: reading global and member functions should be the same

  • Allow use of numpy.arange for uniform_grid arguments

  • Document .i format

  • Make sure data members named ‘del’ are properly renamed without hacking, e.g. with a py_name argument

  • Make sure negative size_t arguments are rejected in python

Details

Handling of function arguments:

  • C type (bool, char, double, float, int, size_t): Convert from a Python object to ctypes.c_<type> in the Python wrapper.

  • reference to C-type: The handling of these references depends on whether the reference is labeled as io (input and output) or out (output only). These references are included in return values in the python wrapper function, and if the label io is given, then they are also input parameters.

  • pointer to C-type: not yet implemented

  • std::string: Use char * in C the wrapper. Convert python string to bytes object and then to char * in python code.

  • reference to std::string: Use void *& in the C wrapper …

  • std_vector & - table::line_of_data: convert to an std::vector<double>

  • std::vector<double> & - uniform_grid::vector

Return values:

  • C type bool: return Python True or False

  • C type char: return a one-character bytes object

  • C type double or float: return a Python float

  • C type int or size_t: return a Python int

  • reference to C type: this is supported currently only for operator[] and operator()

  • std::string: Return a Python bytes object

  • std::string &: Return a std_string object

Special functions:

  • Array-indexing, operator[] functions are translated to __getitem__ functions on the python side. If the operator[] function returns a non-const reference rather than a value, then a __setitem__ function is also created.