Skip to content

Commit

Permalink
Squashed 'wrap/' changes from b0eb968f2..d19cda546
Browse files Browse the repository at this point in the history
d19cda546 Merge pull request #36 from borglab/fix/shared-ptr-property
13ef2485c Merge pull request #37 from borglab/feature/multiple-templates
f3f40b375 add support for multiple instantiated templates
4f33353ef support for templated return types
1244045c2 cleaned up tests and added test for Pybind property shared pointer
0f3fbc428 add support for pointers as properties, and update docs
9974a73ec Merge pull request #35 from borglab/feature/docs
e9c52421d Added DOCS with info about wrapping structure
627154f9f fix call to superclass init
8a4e61ead added more docs and fixed typo for holder_type
b1bdec933 added docstrings to interface_parser and took care of all the errors and warnings

git-subtree-dir: wrap
git-subtree-split: d19cda5467f8b5cb8d4f571d8735ede328dae02d
  • Loading branch information
varunagrawal committed Mar 13, 2021
1 parent f52b096 commit 55dade0
Show file tree
Hide file tree
Showing 19 changed files with 748 additions and 199 deletions.
167 changes: 167 additions & 0 deletions DOCS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
## Wrap Module Definition

### Important

The python wrapper supports keyword arguments for functions/methods. Hence, the argument names matter. An implementation restriction is that in overloaded methods or functions, arguments of different types *have* to have different names.

### Requirements

- Classes must start with an uppercase letter.
- The wrapper can wrap a typedef, e.g. `typedef TemplatedClass<Arg> EasyName;`.

- Only one Method/Constructor per line, though methods/constructors can extend across multiple lines.

- Methods can return
- Eigen types: `Matrix`, `Vector`.
- C/C++ basic types: `string`, `bool`, `size_t`, `int`, `double`, `char`, `unsigned char`.
- `void`
- Any class with which be copied with `boost::make_shared()`.
- `boost::shared_ptr` of any object type.

- Constructors
- Overloads are supported, but arguments of different types *have* to have different names.
- A class with no constructors can be returned from other functions but not allocated directly in MATLAB.

- Methods
- Constness has no effect.
- Specify by-value (not reference) return types, even if C++ method returns reference.
- Must start with a letter (upper or lowercase).
- Overloads are supported.

- Static methods
- Must start with a letter (upper or lowercase) and use the "static" keyword, e.g. `static void func()`.
- The first letter will be made uppercase in the generated MATLAB interface.
- Overloads are supported, but arguments of different types *have* to have different names.

- Arguments to functions can be any of
- Eigen types: `Matrix`, `Vector`.
- Eigen types and classes as an optionally const reference.
- C/C++ basic types: `string`, `bool`, `size_t`, `size_t`, `double`, `char`, `unsigned char`.
- Any class with which be copied with `boost::make_shared()` (except Eigen).
- `boost::shared_ptr` of any object type (except Eigen).

- Properties or Variables
- You can specify class variables in the interface file as long as they are in the `public` scope, e.g.

```cpp
class Sample {
double seed;
};
```

- Class variables are read-write so they can be updated directly in Python.

- Pointer types
- To declare a pointer type (including shared pointers), simply add an asterisk (i.e. `*`) to the class name.
- E.g. `gtsam::noiseModel::Base*` to define the wrapping for the `Base` noise model shared pointer.

- Comments can use either C++ or C style, with multiple lines.

- Namespace definitions
- Names of namespaces must start with a lowercase letter.
- Start a namespace with `namespace example_ns {`, where `example_ns` is the namespace name.
- End a namespace with exactly `}`
- Namespaces can be nested.

- Namespace usage
- Namespaces can be specified for classes in arguments and return values.
- In each case, the namespace must be fully specified, e.g., `namespace1::namespace2::ClassName`.

- Includes in C++ wrappers
- All includes will be collected and added in a single file.
- All namespaces must have angle brackets, e.g. `#include <path>`.
- No default includes will be added.

- Global/Namespace functions
- Functions specified outside of a class are **global**.
- Can be overloaded with different arguments.
- Can have multiple functions of the same name in different namespaces.

- Using classes defined in other modules
- If you are using a class `OtherClass` not wrapped in an interface file, add `class OtherClass;` as a forward declaration to avoid a dependency error.

- Virtual inheritance
- Specify fully-qualified base classes, i.e. `virtual class Derived : ns::Base {` where `ns` is the namespace.
- Mark with `virtual` keyword, e.g. `virtual class Base {`, and also `virtual class Derived : ns::Base {`.
- Forward declarations must also be marked virtual, e.g. `virtual class ns::Base;` and
also `virtual class ns::Derived;`.
- Pure virtual (abstract) classes should list no constructors in the interface file.
- Virtual classes must have a `clone()` function in C++ (though it does not have to be included
in the interface file). `clone()` will be called whenever an object copy is needed, instead
of using the copy constructor (which is used for non-virtual objects).
- Signature of clone function - `clone()` will be called virtually, so must appear at least at the top of the inheritance tree

```cpp
virtual boost::shared_ptr<CLASS_NAME> clone() const;
```

- Class Templates
- Basic templates are supported either with an explicit list of types to instantiate,
e.g.

```cpp
template<T = {gtsam::Pose2, gtsam::Rot2, gtsam::Point3}> class Class1 { ... };
```

or with typedefs, e.g.

```cpp
template<T, U> class Class2 { ... };
typedef Class2<Type1, Type2> MyInstantiatedClass;
```

- In the class definition, appearances of the template argument(s) will be replaced with their
instantiated types, e.g. `void setValue(const T& value);`.
- To refer to the instantiation of the template class itself, use `This`, i.e. `static This Create();`.
- To create new instantiations in other modules, you must copy-and-paste the whole class definition
into the new module, but use only your new instantiation types.
- When forward-declaring template instantiations, use the generated/typedefed name, e.g.

```cpp
class gtsam::Class1Pose2;
class gtsam::MyInstantiatedClass;
```

- `Boost.serialization` within the wrapper:
- You need to mark classes as being serializable in the markup file (see `gtsam.i` for examples).
- There are two options currently, depending on the class. To "mark" a class as serializable,
add a function with a particular signature so that `wrap` will catch it.
- Add `void serialize()` to a class to create serialization functions for a class.
Adding this flag subsumes the `serializable()` flag below.

Requirements:
- A default constructor must be publicly accessible.
- Must not be an abstract base class.
- The class must have an actual boost.serialization `serialize()` function.

- Add `void serializable()` to a class if you only want the class to be serialized as a
part of a container (such as `noiseModel`). This version does not require a publicly
accessible default constructor.

- Forward declarations and class definitions for **Pybind**:
- Need to specify the base class (both this forward class and base class are declared in an external Pybind header)
This is so that Pybind can generate proper inheritance.

Example when wrapping a gtsam-based project:

```cpp
// forward declarations
virtual class gtsam::NonlinearFactor
virtual class gtsam::NoiseModelFactor : gtsam::NonlinearFactor
// class definition
#include <MyFactor.h>
virtual class MyFactor : gtsam::NoiseModelFactor {...};
```

- **DO NOT** re-define an overriden function already declared in the external (forward-declared) base class
- This will cause an ambiguity problem in Pybind header file.


### TODO
- Default values for arguments.
- WORKAROUND: make multiple versions of the same function for different configurations of default arguments.
- Handle `gtsam::Rot3M` conversions to quaternions.
- Parse return of const ref arguments.
- Parse `std::string` variants and convert directly to special string.
- Add enum support.
- Add generalized serialization support via `boost.serialization` with hooks to MATLAB save/load.
18 changes: 12 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
The wrap library wraps the GTSAM library into a Python library or MATLAB toolbox.
It was designed to be more general than just wrapping GTSAM. For notes on creating a wrap interface, see `gtsam.h` for what features can be wrapped into a toolbox, as well as the current state of the toolbox for GTSAM.

## Prerequisites: Pybind11 and pyparsing
## Prerequisites

`Pybind11` and `pyparsing`

1. This library uses `pybind11`, which is included as a subdirectory in GTSAM.
2. The `interface_parser.py` in this library uses `pyparsing` to parse the interface file `gtsam.h`. Please install it first in your current Python environment before attempting the build.

```
```sh
python3 -m pip install pyparsing
```

Expand Down Expand Up @@ -42,6 +44,10 @@ pybind_wrap(${PROJECT_NAME}_py # target

For more information, please follow our [tutorial](https://github.com/borglab/gtsam-project-python).

## Documentation

Documentation for wrapping C++ code can be found [here](https://github.com/borglab/wrap/blob/master/DOCS.md).

## Python Wrapper

**WARNING: On macOS, you have to statically build GTSAM to use the wrapper.**
Expand All @@ -51,13 +57,13 @@ For more information, please follow our [tutorial](https://github.com/borglab/gt

1. Just run python then import GTSAM and play around:

```
```python
import gtsam
gtsam.__dir__()
```

1. Run the unittests:
```
```sh
python -m unittest discover
```
1. Edit the unittests in `python/gtsam/*.py` and simply rerun the test.
Expand All @@ -66,11 +72,11 @@ For more information, please follow our [tutorial](https://github.com/borglab/gt

1. Do `make install` and `cd <gtsam_install_folder>/python`. Here, you can:
1. Run the unittests:
```
```sh
python setup.py test
```
2. Install `gtsam` to your current Python environment.
```
```sh
python setup.py install
```
- NOTE: It's a good idea to create a virtual environment otherwise it will be installed in your system Python's site-packages.
Expand Down
Loading

0 comments on commit 55dade0

Please sign in to comment.