Using a TOML configuration file
A TOML configuration file can optionally be provided to clair-c2py to customize the generated Python bindings.
It has the same name as the module, with a .toml extension.
If this configuration file is absent, clair uses default options.
Generating a template TOML file
The command
clair-c2py --gen-default-config my_module.cpp
generates a my_module.toml file
with default configurations.
You can then edit this file to customize the behavior, and provide it with your sources.
A basic documentation of each option is provided as comments in the generated file.
TOML Configuration Options
Option |
Description |
|---|---|
|
Name of the package (optional). Module name will be |
|
The documentation string of the module. |
|
Space-separated list of namespaces (e.g. |
|
Regex pattern to match element names. Only elements whose fully qualified name matches are wrapped. |
|
Regex pattern to reject element names. Elements whose fully qualified name matches are excluded. |
|
Regex pattern to filter by source file. Only elements declared in matching files are wrapped. |
|
If |
|
If |
Warning
N::* does not match N::f, while N::.* does !::. So to match the start of a name, use ^::N::f.Example
We consider a simple module my_module.cpp which wraps some functions and classes from two files and two namespaces.
// foo.hpp
#include <iostream>
namespace foo {
inline void f1() { std::cout << "foo::f1" << std::endl; }
inline void f2() { std::cout << "foo::f2" << std::endl; }
inline void g1() { std::cout << "foo::g1" << std::endl; }
inline void g2() { std::cout << "foo::g2" << std::endl; }
class A {
int x_;
public:
int x() const { return x_; }
void set_x(int x) { x_ = x; }
};
} // namespace foo
// bar.hpp
#include <iostream>
namespace bar {
inline void h1() { std::cout << "bar::h1" << std::endl; }
inline void h2() { std::cout << "bar::h2" << std::endl; }
} // namespace bar
// my_module.cpp
#include <c2py/c2py.hpp>
#include "foo.hpp"
#include "bar.hpp"
The initial TOML file looks as follows:
# Name of the package [optional].
# Name of the module will be package_name.module_filename
package_name = "my_package"
# The documentation string of the module.
documentation = "This is an awesome module generated by c2py/clair."
# List of namespaces as a space separated string e.g. "A A::B"
# If non-empty, only the elements declared in EXACTLY these namespaces are wrapped.
# NB e.g. in previous example A::detail will be ignored, whatever match_names.
namespaces = "foo bar"
# Match/reject elements according to a regex (LLVM regex)
# If match_names/match_names are both set, the algorithm is
# for X of fully qualified name qname
# if match_names and match_names does not match Qname : skip X
# if reject_names and reject_names matches Qname : skip X
# else keep X.
#
match_names = ""
reject_names = ""
# Only keep elements declared in files matching the pattern
match_files = "" # String must be a regex
# If true, transform methods with no arguments into a read only property
wrap_no_arg_methods_as_properties = false
# [Advanced] If yes (default), all elements (class/enum/functions)
# declared in headers included with -isystem are ignored for better performance.
exclude_system_headers = true
After generating the Python bindings and compiling, we obtain:
>>> import my_module
>>> dir(my_module)
['A', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'f1', 'f2', 'g1', 'g2', 'h1', 'h2']
>>> print(my_module.__doc__)
This is an awesome module generated by c2py/clair.
Filtering by namespace
namespaces option.foo namespace, we modify the TOML file as follows:# List of namespaces as a space separated string e.g. "A A::B"
# If non-empty, only the elements declared in EXACTLY these namespaces are wrapped.
# NB e.g. in previous example A::detail will be ignored, whatever match_names.
namespaces = "foo"
Now only the names in the foo namespace are available in Python:
>>> import my_module
>>> dir(my_module)
['A', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'f1', 'f2', 'g1', 'g2']
Filtering by name
match_names and reject_names options.f, we modify the TOML file as follows:# Match/reject elements according to a regex (LLVM regex)
# If match_names/match_names are both set, the algorithm is
# for each element X of qualified name Qname:
# if match_names and not match_names matches Qname : skip X
# if reject_names and reject_names matches Qname : skip X
# else keep X.
#
match_names = "foo::f.*"
# reject_names = ""
Now only the functions f1 and f2 are available in Python:
>>> import my_module
>>> dir(my_module)
['__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'f1', 'f2']
To only wrap the function f1, we can for example reject foo::f2 explicitly:
# Match/reject elements according to a regex (LLVM regex)
# If match_names/match_names are both set, the algorithm is
# for each element X of qualified name Qname:
# if match_names and not match_names matches Qname : skip X
# if reject_names and reject_names matches Qname : skip X
# else keep X.
#
match_names = "foo::f.*"
reject_names = "foo::f2"
This results in only f1 being available in Python:
>>> import my_module
>>> dir(my_module)
['__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'f1']
Filter by file name
Finally, we can restrict the wrapping to only elements declared in files matching a given regex using the match_files option.
Let’s start again from our initial TOML file without any filtering.
To only wrap functions declared in files whose name contains foo, we modify the TOML file as follows:
# Only keep elements declared in files matching the pattern
match_files = ".*foo.*" # String must be a regex
We obtain:
>>> import my_module
>>> dir(my_module)
['A', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'f1', 'f2', 'g1', 'g2']
Wrap no argument methods as properties
wrap_no_arg_methods_as_properties is set to true, all methods of wrapped classes without argument are wrapped as Python properties.A defined in foo.hpp has the getter method A::x() with no argument.>>> import my_module
>>> a = my_module.A()
>>> a.x()
0
>>> a.set_x(5)
>>> a.x()
5
>>> a.x
<built-in method x of my_package.my_module.A object at 0x101248a10>
To wrap it as a property instead, we modify the TOML file as follows:
# If true, all methods with no argument are wrapped as properties
wrap_no_arg_methods_as_properties = true
Now the method A::x() is wrapped as a read-only property in Python:
>>> import my_module
>>> a = my_module.A()
>>> a.x
0
>>> a.set_x(5)
>>> a.x
5