Merging 'master' into 'wrap'
commit
31635bedbd
|
@ -35,17 +35,19 @@ configure_package_config_file(
|
|||
INSTALL_INCLUDE_DIR
|
||||
INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX})
|
||||
|
||||
message(STATUS "Package config : ${CMAKE_INSTALL_PREFIX}/${INSTALL_CMAKE_DIR}")
|
||||
# Set all the install paths
|
||||
set(GTWRAP_CMAKE_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/${INSTALL_CMAKE_DIR})
|
||||
set(GTWRAP_LIB_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/${INSTALL_LIB_DIR})
|
||||
set(GTWRAP_BIN_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/${INSTALL_BIN_DIR})
|
||||
set(GTWRAP_INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/${INSTALL_INCLUDE_DIR})
|
||||
|
||||
# ##############################################################################
|
||||
# Install the package
|
||||
|
||||
message(STATUS "CMake : ${CMAKE_INSTALL_PREFIX}/${INSTALL_CMAKE_DIR}")
|
||||
# Install CMake scripts to the standard CMake script directory.
|
||||
install(
|
||||
FILES ${CMAKE_CURRENT_BINARY_DIR}/cmake/gtwrapConfig.cmake
|
||||
cmake/MatlabWrap.cmake cmake/PybindWrap.cmake cmake/GtwrapUtils.cmake
|
||||
DESTINATION "${CMAKE_INSTALL_PREFIX}/${INSTALL_CMAKE_DIR}")
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/cmake/gtwrapConfig.cmake
|
||||
cmake/MatlabWrap.cmake cmake/PybindWrap.cmake
|
||||
cmake/GtwrapUtils.cmake DESTINATION "${GTWRAP_CMAKE_INSTALL_DIR}")
|
||||
|
||||
# Configure the include directory for matlab.h This allows the #include to be
|
||||
# either gtwrap/matlab.h, wrap/matlab.h or something custom.
|
||||
|
@ -60,24 +62,26 @@ configure_file(${PROJECT_SOURCE_DIR}/templates/matlab_wrapper.tpl.in
|
|||
|
||||
# Install the gtwrap python package as a directory so it can be found by CMake
|
||||
# for wrapping.
|
||||
message(STATUS "Lib path : ${CMAKE_INSTALL_PREFIX}/${INSTALL_LIB_DIR}")
|
||||
install(DIRECTORY gtwrap
|
||||
DESTINATION "${CMAKE_INSTALL_PREFIX}/${INSTALL_LIB_DIR}")
|
||||
install(DIRECTORY gtwrap DESTINATION "${GTWRAP_LIB_INSTALL_DIR}")
|
||||
|
||||
# Install pybind11 directory to `CMAKE_INSTALL_PREFIX/lib/gtwrap/pybind11` This
|
||||
# will allow the gtwrapConfig.cmake file to load it later.
|
||||
install(DIRECTORY pybind11
|
||||
DESTINATION "${CMAKE_INSTALL_PREFIX}/${INSTALL_LIB_DIR}")
|
||||
install(DIRECTORY pybind11 DESTINATION "${GTWRAP_LIB_INSTALL_DIR}")
|
||||
|
||||
# Install wrapping scripts as binaries to `CMAKE_INSTALL_PREFIX/bin` so they can
|
||||
# be invoked for wrapping. We use DESTINATION (instead of TYPE) so we can
|
||||
# support older CMake versions.
|
||||
message(STATUS "Bin path : ${CMAKE_INSTALL_PREFIX}/${INSTALL_BIN_DIR}")
|
||||
install(PROGRAMS scripts/pybind_wrap.py scripts/matlab_wrap.py
|
||||
DESTINATION "${CMAKE_INSTALL_PREFIX}/${INSTALL_BIN_DIR}")
|
||||
DESTINATION "${GTWRAP_BIN_INSTALL_DIR}")
|
||||
|
||||
# Install the matlab.h file to `CMAKE_INSTALL_PREFIX/lib/gtwrap/matlab.h`.
|
||||
message(
|
||||
STATUS "Header path : ${CMAKE_INSTALL_PREFIX}/${INSTALL_INCLUDE_DIR}")
|
||||
install(FILES matlab.h
|
||||
DESTINATION "${CMAKE_INSTALL_PREFIX}/${INSTALL_INCLUDE_DIR}")
|
||||
install(FILES matlab.h DESTINATION "${GTWRAP_INCLUDE_INSTALL_DIR}")
|
||||
|
||||
string(ASCII 27 Esc)
|
||||
set(gtwrap "${Esc}[1;36mgtwrap${Esc}[m")
|
||||
message(STATUS "${gtwrap} Package config : ${GTWRAP_CMAKE_INSTALL_DIR}")
|
||||
message(STATUS "${gtwrap} version : ${PROJECT_VERSION}")
|
||||
message(STATUS "${gtwrap} CMake path : ${GTWRAP_CMAKE_INSTALL_DIR}")
|
||||
message(STATUS "${gtwrap} library path : ${GTWRAP_LIB_INSTALL_DIR}")
|
||||
message(STATUS "${gtwrap} binary path : ${GTWRAP_BIN_INSTALL_DIR}")
|
||||
message(STATUS "${gtwrap} header path : ${GTWRAP_INCLUDE_INSTALL_DIR}")
|
||||
|
|
|
@ -11,10 +11,12 @@ Author: Duy Nguyen Ta, Fan Jiang, Matthew Sklar, Varun Agrawal, and Frank Dellae
|
|||
"""
|
||||
|
||||
import sys
|
||||
|
||||
import pyparsing
|
||||
|
||||
from .classes import *
|
||||
from .declaration import *
|
||||
from .enum import *
|
||||
from .function import *
|
||||
from .module import *
|
||||
from .namespace import *
|
||||
|
|
|
@ -12,13 +12,15 @@ Author: Duy Nguyen Ta, Fan Jiang, Matthew Sklar, Varun Agrawal, and Frank Dellae
|
|||
|
||||
from typing import Iterable, List, Union
|
||||
|
||||
from pyparsing import Optional, ZeroOrMore, Literal
|
||||
from pyparsing import Literal, Optional, ZeroOrMore
|
||||
|
||||
from .enum import Enum
|
||||
from .function import ArgumentList, ReturnType
|
||||
from .template import Template
|
||||
from .tokens import (CLASS, COLON, CONST, IDENT, LBRACE, LPAREN, RBRACE,
|
||||
RPAREN, SEMI_COLON, STATIC, VIRTUAL, OPERATOR)
|
||||
from .type import TemplatedType, Type, Typename
|
||||
from .tokens import (CLASS, COLON, CONST, IDENT, LBRACE, LPAREN, OPERATOR,
|
||||
RBRACE, RPAREN, SEMI_COLON, STATIC, VIRTUAL)
|
||||
from .type import TemplatedType, Typename
|
||||
from .utils import collect_namespaces
|
||||
from .variable import Variable
|
||||
|
||||
|
||||
|
@ -200,21 +202,6 @@ class Operator:
|
|||
)
|
||||
|
||||
|
||||
def collect_namespaces(obj):
|
||||
"""
|
||||
Get the chain of namespaces from the lowest to highest for the given object.
|
||||
|
||||
Args:
|
||||
obj: Object of type Namespace, Class or InstantiatedClass.
|
||||
"""
|
||||
namespaces = []
|
||||
ancestor = obj.parent
|
||||
while ancestor and ancestor.name:
|
||||
namespaces = [ancestor.name] + namespaces
|
||||
ancestor = ancestor.parent
|
||||
return [''] + namespaces
|
||||
|
||||
|
||||
class Class:
|
||||
"""
|
||||
Rule to parse a class defined in the interface file.
|
||||
|
@ -230,9 +217,13 @@ class Class:
|
|||
"""
|
||||
Rule for all the members within a class.
|
||||
"""
|
||||
rule = ZeroOrMore(Constructor.rule ^ StaticMethod.rule ^ Method.rule
|
||||
^ Variable.rule ^ Operator.rule).setParseAction(
|
||||
lambda t: Class.Members(t.asList()))
|
||||
rule = ZeroOrMore(Constructor.rule #
|
||||
^ StaticMethod.rule #
|
||||
^ Method.rule #
|
||||
^ Variable.rule #
|
||||
^ Operator.rule #
|
||||
^ Enum.rule #
|
||||
).setParseAction(lambda t: Class.Members(t.asList()))
|
||||
|
||||
def __init__(self,
|
||||
members: List[Union[Constructor, Method, StaticMethod,
|
||||
|
@ -242,6 +233,7 @@ class Class:
|
|||
self.static_methods = []
|
||||
self.properties = []
|
||||
self.operators = []
|
||||
self.enums = []
|
||||
for m in members:
|
||||
if isinstance(m, Constructor):
|
||||
self.ctors.append(m)
|
||||
|
@ -253,6 +245,8 @@ class Class:
|
|||
self.properties.append(m)
|
||||
elif isinstance(m, Operator):
|
||||
self.operators.append(m)
|
||||
elif isinstance(m, Enum):
|
||||
self.enums.append(m)
|
||||
|
||||
_parent = COLON + (TemplatedType.rule ^ Typename.rule)("parent_class")
|
||||
rule = (
|
||||
|
@ -275,6 +269,7 @@ class Class:
|
|||
t.members.static_methods,
|
||||
t.members.properties,
|
||||
t.members.operators,
|
||||
t.members.enums
|
||||
))
|
||||
|
||||
def __init__(
|
||||
|
@ -288,6 +283,7 @@ class Class:
|
|||
static_methods: List[StaticMethod],
|
||||
properties: List[Variable],
|
||||
operators: List[Operator],
|
||||
enums: List[Enum],
|
||||
parent: str = '',
|
||||
):
|
||||
self.template = template
|
||||
|
@ -312,6 +308,8 @@ class Class:
|
|||
self.static_methods = static_methods
|
||||
self.properties = properties
|
||||
self.operators = operators
|
||||
self.enums = enums
|
||||
|
||||
self.parent = parent
|
||||
|
||||
# Make sure ctors' names and class name are the same.
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
"""
|
||||
GTSAM Copyright 2010-2020, Georgia Tech Research Corporation,
|
||||
Atlanta, Georgia 30332-0415
|
||||
All Rights Reserved
|
||||
|
||||
See LICENSE for the license information
|
||||
|
||||
Parser class and rules for parsing C++ enums.
|
||||
|
||||
Author: Varun Agrawal
|
||||
"""
|
||||
|
||||
from pyparsing import delimitedList
|
||||
|
||||
from .tokens import ENUM, IDENT, LBRACE, RBRACE, SEMI_COLON
|
||||
from .type import Typename
|
||||
from .utils import collect_namespaces
|
||||
|
||||
|
||||
class Enumerator:
|
||||
"""
|
||||
Rule to parse an enumerator inside an enum.
|
||||
"""
|
||||
rule = (
|
||||
IDENT("enumerator")).setParseAction(lambda t: Enumerator(t.enumerator))
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def __repr__(self):
|
||||
return "Enumerator: ({0})".format(self.name)
|
||||
|
||||
|
||||
class Enum:
|
||||
"""
|
||||
Rule to parse enums defined in the interface file.
|
||||
|
||||
E.g.
|
||||
```
|
||||
enum Kind {
|
||||
Dog,
|
||||
Cat
|
||||
};
|
||||
```
|
||||
"""
|
||||
|
||||
rule = (ENUM + IDENT("name") + LBRACE +
|
||||
delimitedList(Enumerator.rule)("enumerators") + RBRACE +
|
||||
SEMI_COLON).setParseAction(lambda t: Enum(t.name, t.enumerators))
|
||||
|
||||
def __init__(self, name, enumerators, parent=''):
|
||||
self.name = name
|
||||
self.enumerators = enumerators
|
||||
self.parent = parent
|
||||
|
||||
def namespaces(self) -> list:
|
||||
"""Get the namespaces which this class is nested under as a list."""
|
||||
return collect_namespaces(self)
|
||||
|
||||
def cpp_typename(self):
|
||||
"""
|
||||
Return a Typename with the namespaces and cpp name of this
|
||||
class.
|
||||
"""
|
||||
namespaces_name = self.namespaces()
|
||||
namespaces_name.append(self.name)
|
||||
return Typename(namespaces_name)
|
||||
|
||||
def __repr__(self):
|
||||
return "Enum: {0}".format(self.name)
|
|
@ -50,6 +50,9 @@ class Argument:
|
|||
# This means a tuple has been passed so we convert accordingly
|
||||
elif len(default) > 1:
|
||||
default = tuple(default.asList())
|
||||
else:
|
||||
# set to None explicitly so we can support empty strings
|
||||
default = None
|
||||
self.default = default
|
||||
|
||||
self.parent: Union[ArgumentList, None] = None
|
||||
|
|
|
@ -12,14 +12,11 @@ Author: Duy Nguyen Ta, Fan Jiang, Matthew Sklar, Varun Agrawal, and Frank Dellae
|
|||
|
||||
# pylint: disable=unnecessary-lambda, unused-import, expression-not-assigned, no-else-return, protected-access, too-few-public-methods, too-many-arguments
|
||||
|
||||
import sys
|
||||
|
||||
import pyparsing # type: ignore
|
||||
from pyparsing import (ParserElement, ParseResults, ZeroOrMore,
|
||||
cppStyleComment, stringEnd)
|
||||
from pyparsing import ParseResults, ZeroOrMore, cppStyleComment, stringEnd
|
||||
|
||||
from .classes import Class
|
||||
from .declaration import ForwardDeclaration, Include
|
||||
from .enum import Enum
|
||||
from .function import GlobalFunction
|
||||
from .namespace import Namespace
|
||||
from .template import TypedefTemplateInstantiation
|
||||
|
@ -44,6 +41,7 @@ class Module:
|
|||
^ Class.rule #
|
||||
^ TypedefTemplateInstantiation.rule #
|
||||
^ GlobalFunction.rule #
|
||||
^ Enum.rule #
|
||||
^ Variable.rule #
|
||||
^ Namespace.rule #
|
||||
).setParseAction(lambda t: Namespace('', t.asList())) +
|
||||
|
|
|
@ -18,6 +18,7 @@ from pyparsing import Forward, ParseResults, ZeroOrMore
|
|||
|
||||
from .classes import Class, collect_namespaces
|
||||
from .declaration import ForwardDeclaration, Include
|
||||
from .enum import Enum
|
||||
from .function import GlobalFunction
|
||||
from .template import TypedefTemplateInstantiation
|
||||
from .tokens import IDENT, LBRACE, NAMESPACE, RBRACE
|
||||
|
@ -68,6 +69,7 @@ class Namespace:
|
|||
^ Class.rule #
|
||||
^ TypedefTemplateInstantiation.rule #
|
||||
^ GlobalFunction.rule #
|
||||
^ Enum.rule #
|
||||
^ Variable.rule #
|
||||
^ rule #
|
||||
)("content") # BR
|
||||
|
|
|
@ -46,6 +46,7 @@ CONST, VIRTUAL, CLASS, STATIC, PAIR, TEMPLATE, TYPEDEF, INCLUDE = map(
|
|||
"#include",
|
||||
],
|
||||
)
|
||||
ENUM = Keyword("enum") ^ Keyword("enum class") ^ Keyword("enum struct")
|
||||
NAMESPACE = Keyword("namespace")
|
||||
BASIS_TYPES = map(
|
||||
Keyword,
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
"""
|
||||
GTSAM Copyright 2010-2020, Georgia Tech Research Corporation,
|
||||
Atlanta, Georgia 30332-0415
|
||||
All Rights Reserved
|
||||
|
||||
See LICENSE for the license information
|
||||
|
||||
Various common utilities.
|
||||
|
||||
Author: Varun Agrawal
|
||||
"""
|
||||
|
||||
|
||||
def collect_namespaces(obj):
|
||||
"""
|
||||
Get the chain of namespaces from the lowest to highest for the given object.
|
||||
|
||||
Args:
|
||||
obj: Object of type Namespace, Class, InstantiatedClass, or Enum.
|
||||
"""
|
||||
namespaces = []
|
||||
ancestor = obj.parent
|
||||
while ancestor and ancestor.name:
|
||||
namespaces = [ancestor.name] + namespaces
|
||||
ancestor = ancestor.parent
|
||||
return [''] + namespaces
|
|
@ -46,6 +46,8 @@ class Variable:
|
|||
self.name = name
|
||||
if default:
|
||||
self.default = default[0]
|
||||
else:
|
||||
self.default = None
|
||||
|
||||
self.parent = parent
|
||||
|
||||
|
|
|
@ -47,11 +47,15 @@ class PybindWrapper:
|
|||
if names:
|
||||
py_args = []
|
||||
for arg in args_list.args_list:
|
||||
if arg.default and isinstance(arg.default, str):
|
||||
arg.default = "\"{arg.default}\"".format(arg=arg)
|
||||
if isinstance(arg.default, str) and arg.default is not None:
|
||||
# string default arg
|
||||
arg.default = ' = "{arg.default}"'.format(arg=arg)
|
||||
elif arg.default: # Other types
|
||||
arg.default = ' = {arg.default}'.format(arg=arg)
|
||||
else:
|
||||
arg.default = ''
|
||||
argument = 'py::arg("{name}"){default}'.format(
|
||||
name=arg.name,
|
||||
default=' = {0}'.format(arg.default) if arg.default else '')
|
||||
name=arg.name, default='{0}'.format(arg.default))
|
||||
py_args.append(argument)
|
||||
return ", " + ", ".join(py_args)
|
||||
else:
|
||||
|
@ -61,7 +65,10 @@ class PybindWrapper:
|
|||
"""Define the method signature types with the argument names."""
|
||||
cpp_types = args_list.to_cpp(self.use_boost)
|
||||
names = args_list.args_names()
|
||||
types_names = ["{} {}".format(ctype, name) for ctype, name in zip(cpp_types, names)]
|
||||
types_names = [
|
||||
"{} {}".format(ctype, name)
|
||||
for ctype, name in zip(cpp_types, names)
|
||||
]
|
||||
|
||||
return ', '.join(types_names)
|
||||
|
||||
|
@ -69,14 +76,20 @@ class PybindWrapper:
|
|||
"""Wrap the constructors."""
|
||||
res = ""
|
||||
for ctor in my_class.ctors:
|
||||
res += (self.method_indent + '.def(py::init<{args_cpp_types}>()'
|
||||
res += (
|
||||
self.method_indent + '.def(py::init<{args_cpp_types}>()'
|
||||
'{py_args_names})'.format(
|
||||
args_cpp_types=", ".join(ctor.args.to_cpp(self.use_boost)),
|
||||
py_args_names=self._py_args_names(ctor.args),
|
||||
))
|
||||
return res
|
||||
|
||||
def _wrap_method(self, method, cpp_class, prefix, suffix, method_suffix=""):
|
||||
def _wrap_method(self,
|
||||
method,
|
||||
cpp_class,
|
||||
prefix,
|
||||
suffix,
|
||||
method_suffix=""):
|
||||
py_method = method.name + method_suffix
|
||||
cpp_method = method.to_cpp()
|
||||
|
||||
|
@ -92,17 +105,20 @@ class PybindWrapper:
|
|||
|
||||
if cpp_method == "pickle":
|
||||
if not cpp_class in self._serializing_classes:
|
||||
raise ValueError("Cannot pickle a class which is not serializable")
|
||||
raise ValueError(
|
||||
"Cannot pickle a class which is not serializable")
|
||||
pickle_method = self.method_indent + \
|
||||
".def(py::pickle({indent} [](const {cpp_class} &a){{ /* __getstate__: Returns a string that encodes the state of the object */ return py::make_tuple(gtsam::serialize(a)); }},{indent} [](py::tuple t){{ /* __setstate__ */ {cpp_class} obj; gtsam::deserialize(t[0].cast<std::string>(), obj); return obj; }}))"
|
||||
return pickle_method.format(cpp_class=cpp_class, indent=self.method_indent)
|
||||
return pickle_method.format(cpp_class=cpp_class,
|
||||
indent=self.method_indent)
|
||||
|
||||
is_method = isinstance(method, instantiator.InstantiatedMethod)
|
||||
is_static = isinstance(method, parser.StaticMethod)
|
||||
return_void = method.return_type.is_void()
|
||||
args_names = method.args.args_names()
|
||||
py_args_names = self._py_args_names(method.args)
|
||||
args_signature_with_names = self._method_args_signature_with_names(method.args)
|
||||
args_signature_with_names = self._method_args_signature_with_names(
|
||||
method.args)
|
||||
|
||||
caller = cpp_class + "::" if not is_method else "self->"
|
||||
function_call = ('{opt_return} {caller}{function_name}'
|
||||
|
@ -136,7 +152,9 @@ class PybindWrapper:
|
|||
if method.name == 'print':
|
||||
# Redirect stdout - see pybind docs for why this is a good idea:
|
||||
# https://pybind11.readthedocs.io/en/stable/advanced/pycpp/utilities.html#capturing-standard-output-from-ostream
|
||||
ret = ret.replace('self->print', 'py::scoped_ostream_redirect output; self->print')
|
||||
ret = ret.replace(
|
||||
'self->print',
|
||||
'py::scoped_ostream_redirect output; self->print')
|
||||
|
||||
# Make __repr__() call print() internally
|
||||
ret += '''{prefix}.def("__repr__",
|
||||
|
@ -156,7 +174,11 @@ class PybindWrapper:
|
|||
|
||||
return ret
|
||||
|
||||
def wrap_methods(self, methods, cpp_class, prefix='\n' + ' ' * 8, suffix=''):
|
||||
def wrap_methods(self,
|
||||
methods,
|
||||
cpp_class,
|
||||
prefix='\n' + ' ' * 8,
|
||||
suffix=''):
|
||||
"""
|
||||
Wrap all the methods in the `cpp_class`.
|
||||
|
||||
|
@ -169,7 +191,8 @@ class PybindWrapper:
|
|||
if method.name == 'insert' and cpp_class == 'gtsam::Values':
|
||||
name_list = method.args.args_names()
|
||||
type_list = method.args.to_cpp(self.use_boost)
|
||||
if type_list[0].strip() == 'size_t': # inserting non-wrapped value types
|
||||
# inserting non-wrapped value types
|
||||
if type_list[0].strip() == 'size_t':
|
||||
method_suffix = '_' + name_list[1].strip()
|
||||
res += self._wrap_method(method=method,
|
||||
cpp_class=cpp_class,
|
||||
|
@ -186,15 +209,18 @@ class PybindWrapper:
|
|||
|
||||
return res
|
||||
|
||||
def wrap_variable(self, module, module_var, variable, prefix='\n' + ' ' * 8):
|
||||
def wrap_variable(self,
|
||||
module,
|
||||
module_var,
|
||||
variable,
|
||||
prefix='\n' + ' ' * 8):
|
||||
"""Wrap a variable that's not part of a class (i.e. global)
|
||||
"""
|
||||
return '{prefix}{module_var}.attr("{variable_name}") = {module}{variable_name};'.format(
|
||||
prefix=prefix,
|
||||
module=module,
|
||||
module_var=module_var,
|
||||
variable_name=variable.name
|
||||
)
|
||||
variable_name=variable.name)
|
||||
|
||||
def wrap_properties(self, properties, cpp_class, prefix='\n' + ' ' * 8):
|
||||
"""Wrap all the properties in the `cpp_class`."""
|
||||
|
@ -203,7 +229,8 @@ class PybindWrapper:
|
|||
res += ('{prefix}.def_{property}("{property_name}", '
|
||||
'&{cpp_class}::{property_name})'.format(
|
||||
prefix=prefix,
|
||||
property="readonly" if prop.ctype.is_const else "readwrite",
|
||||
property="readonly"
|
||||
if prop.ctype.is_const else "readwrite",
|
||||
cpp_class=cpp_class,
|
||||
property_name=prop.name,
|
||||
))
|
||||
|
@ -227,7 +254,8 @@ class PybindWrapper:
|
|||
op.operator))
|
||||
return res
|
||||
|
||||
def wrap_instantiated_class(self, instantiated_class):
|
||||
def wrap_instantiated_class(
|
||||
self, instantiated_class: instantiator.InstantiatedClass):
|
||||
"""Wrap the class."""
|
||||
module_var = self._gen_module_var(instantiated_class.namespaces())
|
||||
cpp_class = instantiated_class.cpp_class()
|
||||
|
@ -287,6 +315,18 @@ class PybindWrapper:
|
|||
stl_class.properties, cpp_class),
|
||||
))
|
||||
|
||||
def wrap_enum(self, enum, prefix='\n' + ' ' * 8):
|
||||
"""Wrap an enum."""
|
||||
module_var = self._gen_module_var(enum.namespaces())
|
||||
cpp_class = enum.cpp_typename().to_cpp()
|
||||
res = '\n py::enum_<{cpp_class}>({module_var}, "{enum.name}", py::arithmetic())'.format(
|
||||
module_var=module_var, enum=enum, cpp_class=cpp_class)
|
||||
for enumerator in enum.enumerators:
|
||||
res += '{prefix}.value("{enumerator.name}", {cpp_class}::{enumerator.name})'.format(
|
||||
prefix=prefix, enumerator=enumerator, cpp_class=cpp_class)
|
||||
res += ";\n\n"
|
||||
return res
|
||||
|
||||
def _partial_match(self, namespaces1, namespaces2):
|
||||
for i in range(min(len(namespaces1), len(namespaces2))):
|
||||
if namespaces1[i] != namespaces2[i]:
|
||||
|
@ -294,6 +334,8 @@ class PybindWrapper:
|
|||
return True
|
||||
|
||||
def _gen_module_var(self, namespaces):
|
||||
"""Get the Pybind11 module name from the namespaces."""
|
||||
# We skip the first value in namespaces since it is empty
|
||||
sub_module_namespaces = namespaces[len(self.top_module_namespaces):]
|
||||
return "m_{}".format('_'.join(sub_module_namespaces))
|
||||
|
||||
|
@ -317,7 +359,10 @@ class PybindWrapper:
|
|||
if len(namespaces) < len(self.top_module_namespaces):
|
||||
for element in namespace.content:
|
||||
if isinstance(element, parser.Include):
|
||||
includes += ("{}\n".format(element).replace('<', '"').replace('>', '"'))
|
||||
include = "{}\n".format(element)
|
||||
# replace the angle brackets with quotes
|
||||
include = include.replace('<', '"').replace('>', '"')
|
||||
includes += include
|
||||
if isinstance(element, parser.Namespace):
|
||||
(
|
||||
wrapped_namespace,
|
||||
|
@ -330,34 +375,40 @@ class PybindWrapper:
|
|||
module_var = self._gen_module_var(namespaces)
|
||||
|
||||
if len(namespaces) > len(self.top_module_namespaces):
|
||||
wrapped += (' ' * 4 + 'pybind11::module {module_var} = '
|
||||
wrapped += (
|
||||
' ' * 4 + 'pybind11::module {module_var} = '
|
||||
'{parent_module_var}.def_submodule("{namespace}", "'
|
||||
'{namespace} submodule");\n'.format(
|
||||
module_var=module_var,
|
||||
namespace=namespace.name,
|
||||
parent_module_var=self._gen_module_var(namespaces[:-1]),
|
||||
parent_module_var=self._gen_module_var(
|
||||
namespaces[:-1]),
|
||||
))
|
||||
|
||||
# Wrap an include statement, namespace, class or enum
|
||||
for element in namespace.content:
|
||||
if isinstance(element, parser.Include):
|
||||
includes += ("{}\n".format(element).replace('<', '"').replace('>', '"'))
|
||||
include = "{}\n".format(element)
|
||||
# replace the angle brackets with quotes
|
||||
include = include.replace('<', '"').replace('>', '"')
|
||||
includes += include
|
||||
elif isinstance(element, parser.Namespace):
|
||||
(
|
||||
wrapped_namespace,
|
||||
includes_namespace,
|
||||
) = self.wrap_namespace( # noqa
|
||||
wrapped_namespace, includes_namespace = self.wrap_namespace(
|
||||
element)
|
||||
wrapped += wrapped_namespace
|
||||
includes += includes_namespace
|
||||
|
||||
elif isinstance(element, instantiator.InstantiatedClass):
|
||||
wrapped += self.wrap_instantiated_class(element)
|
||||
elif isinstance(element, parser.Variable):
|
||||
wrapped += self.wrap_variable(
|
||||
module=self._add_namespaces('', namespaces),
|
||||
module = self._add_namespaces('', namespaces)
|
||||
wrapped += self.wrap_variable(module=module,
|
||||
module_var=module_var,
|
||||
variable=element,
|
||||
prefix='\n' + ' ' * 4
|
||||
)
|
||||
prefix='\n' + ' ' * 4)
|
||||
|
||||
elif isinstance(element, parser.Enum):
|
||||
wrapped += self.wrap_enum(element)
|
||||
|
||||
# Global functions.
|
||||
all_funcs = [
|
||||
|
@ -388,7 +439,8 @@ class PybindWrapper:
|
|||
cpp_class=cpp_class,
|
||||
new_name=new_name,
|
||||
)
|
||||
boost_class_export += "BOOST_CLASS_EXPORT({new_name})\n".format(new_name=new_name, )
|
||||
boost_class_export += "BOOST_CLASS_EXPORT({new_name})\n".format(
|
||||
new_name=new_name, )
|
||||
|
||||
holder_type = "PYBIND11_DECLARE_HOLDER_TYPE(TYPE_PLACEHOLDER_DONOTUSE, " \
|
||||
"{shared_ptr_type}::shared_ptr<TYPE_PLACEHOLDER_DONOTUSE>);"
|
||||
|
@ -398,7 +450,8 @@ class PybindWrapper:
|
|||
include_boost=include_boost,
|
||||
module_name=self.module_name,
|
||||
includes=includes,
|
||||
holder_type=holder_type.format(shared_ptr_type=('boost' if self.use_boost else 'std'))
|
||||
holder_type=holder_type.format(
|
||||
shared_ptr_type=('boost' if self.use_boost else 'std'))
|
||||
if self.use_boost else "",
|
||||
wrapped_namespace=wrapped_namespace,
|
||||
boost_class_export=boost_class_export,
|
||||
|
|
|
@ -266,7 +266,7 @@ class InstantiatedClass(parser.Class):
|
|||
"""
|
||||
Instantiate the class defined in the interface file.
|
||||
"""
|
||||
def __init__(self, original, instantiations=(), new_name=''):
|
||||
def __init__(self, original: parser.Class, instantiations=(), new_name=''):
|
||||
"""
|
||||
Template <T, U>
|
||||
Instantiations: [T1, U1]
|
||||
|
@ -302,6 +302,9 @@ class InstantiatedClass(parser.Class):
|
|||
# Instantiate all operator overloads
|
||||
self.operators = self.instantiate_operators(typenames)
|
||||
|
||||
# Set enums
|
||||
self.enums = original.enums
|
||||
|
||||
# Instantiate all instance methods
|
||||
instantiated_methods = \
|
||||
self.instantiate_class_templates_in_methods(typenames)
|
||||
|
@ -330,6 +333,7 @@ class InstantiatedClass(parser.Class):
|
|||
self.static_methods,
|
||||
self.properties,
|
||||
self.operators,
|
||||
self.enums,
|
||||
parent=self.parent,
|
||||
)
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/operators.h>
|
||||
#include <pybind11/iostream.h>
|
||||
#include <pybind11/functional.h>
|
||||
#include "gtsam/base/serialization.h"
|
||||
#include "gtsam/nonlinear/utilities.h" // for RedirectCout.
|
||||
|
||||
|
|
|
@ -204,9 +204,10 @@ void DefaultFuncInt_8(int nargout, mxArray *out[], int nargin, const mxArray *in
|
|||
}
|
||||
void DefaultFuncString_9(int nargout, mxArray *out[], int nargin, const mxArray *in[])
|
||||
{
|
||||
checkArguments("DefaultFuncString",nargout,nargin,1);
|
||||
checkArguments("DefaultFuncString",nargout,nargin,2);
|
||||
string& s = *unwrap_shared_ptr< string >(in[0], "ptr_string");
|
||||
DefaultFuncString(s);
|
||||
string& name = *unwrap_shared_ptr< string >(in[1], "ptr_string");
|
||||
DefaultFuncString(s,name);
|
||||
}
|
||||
void DefaultFuncObj_10(int nargout, mxArray *out[], int nargin, const mxArray *in[])
|
||||
{
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
|
||||
|
||||
#include <pybind11/eigen.h>
|
||||
#include <pybind11/stl_bind.h>
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/operators.h>
|
||||
#include "gtsam/nonlinear/utilities.h" // for RedirectCout.
|
||||
|
||||
|
||||
#include "wrap/serialization.h"
|
||||
#include <boost/serialization/export.hpp>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace py = pybind11;
|
||||
|
||||
PYBIND11_MODULE(enum_py, m_) {
|
||||
m_.doc() = "pybind11 wrapper of enum_py";
|
||||
|
||||
|
||||
py::enum_<Kind>(m_, "Kind", py::arithmetic())
|
||||
.value("Dog", Kind::Dog)
|
||||
.value("Cat", Kind::Cat);
|
||||
|
||||
pybind11::module m_gtsam = m_.def_submodule("gtsam", "gtsam submodule");
|
||||
|
||||
py::enum_<gtsam::VerbosityLM>(m_gtsam, "VerbosityLM", py::arithmetic())
|
||||
.value("SILENT", gtsam::VerbosityLM::SILENT)
|
||||
.value("SUMMARY", gtsam::VerbosityLM::SUMMARY)
|
||||
.value("TERMINATION", gtsam::VerbosityLM::TERMINATION)
|
||||
.value("LAMBDA", gtsam::VerbosityLM::LAMBDA)
|
||||
.value("TRYLAMBDA", gtsam::VerbosityLM::TRYLAMBDA)
|
||||
.value("TRYCONFIG", gtsam::VerbosityLM::TRYCONFIG)
|
||||
.value("DAMPED", gtsam::VerbosityLM::DAMPED)
|
||||
.value("TRYDELTA", gtsam::VerbosityLM::TRYDELTA);
|
||||
|
||||
|
||||
py::class_<gtsam::Pet, std::shared_ptr<gtsam::Pet>>(m_gtsam, "Pet")
|
||||
.def(py::init<const string&, Kind>(), py::arg("name"), py::arg("type"))
|
||||
.def_readwrite("name", >sam::Pet::name)
|
||||
.def_readwrite("type", >sam::Pet::type);
|
||||
|
||||
|
||||
#include "python/specializations.h"
|
||||
|
||||
}
|
||||
|
|
@ -31,7 +31,7 @@ PYBIND11_MODULE(functions_py, m_) {
|
|||
m_.def("MultiTemplatedFunctionStringSize_tDouble",[](const T& x, size_t y){return ::MultiTemplatedFunction<string,size_t,double>(x, y);}, py::arg("x"), py::arg("y"));
|
||||
m_.def("MultiTemplatedFunctionDoubleSize_tDouble",[](const T& x, size_t y){return ::MultiTemplatedFunction<double,size_t,double>(x, y);}, py::arg("x"), py::arg("y"));
|
||||
m_.def("DefaultFuncInt",[](int a){ ::DefaultFuncInt(a);}, py::arg("a") = 123);
|
||||
m_.def("DefaultFuncString",[](const string& s){ ::DefaultFuncString(s);}, py::arg("s") = "hello");
|
||||
m_.def("DefaultFuncString",[](const string& s, const string& name){ ::DefaultFuncString(s, name);}, py::arg("s") = "hello", py::arg("name") = "");
|
||||
m_.def("DefaultFuncObj",[](const gtsam::KeyFormatter& keyFormatter){ ::DefaultFuncObj(keyFormatter);}, py::arg("keyFormatter") = gtsam::DefaultKeyFormatter);
|
||||
m_.def("TemplatedFunctionRot3",[](const gtsam::Rot3& t){ ::TemplatedFunction<Rot3>(t);}, py::arg("t"));
|
||||
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
enum Kind { Dog, Cat };
|
||||
|
||||
namespace gtsam {
|
||||
enum VerbosityLM {
|
||||
SILENT,
|
||||
SUMMARY,
|
||||
TERMINATION,
|
||||
LAMBDA,
|
||||
TRYLAMBDA,
|
||||
TRYCONFIG,
|
||||
DAMPED,
|
||||
TRYDELTA
|
||||
};
|
||||
|
||||
class Pet {
|
||||
enum Kind { Dog, Cat };
|
||||
|
||||
Pet(const string &name, Kind type);
|
||||
|
||||
string name;
|
||||
Kind type;
|
||||
};
|
||||
} // namespace gtsam
|
|
@ -29,5 +29,5 @@ typedef TemplatedFunction<gtsam::Rot3> TemplatedFunctionRot3;
|
|||
|
||||
// Check default arguments
|
||||
void DefaultFuncInt(int a = 123);
|
||||
void DefaultFuncString(const string& s = "hello");
|
||||
void DefaultFuncString(const string& s = "hello", const string& name = "");
|
||||
void DefaultFuncObj(const gtsam::KeyFormatter& keyFormatter = gtsam::DefaultKeyFormatter);
|
||||
|
|
|
@ -26,3 +26,11 @@ class SfmTrack {
|
|||
};
|
||||
|
||||
} // namespace gtsam
|
||||
|
||||
|
||||
// class VariableIndex {
|
||||
// VariableIndex();
|
||||
// // template<T = {gtsam::SymbolicFactorGraph, gtsam::GaussianFactorGraph, gtsam::NonlinearFactorGraph, gtsam::FactorGraph}>
|
||||
// VariableIndex(const T& graph);
|
||||
// VariableIndex(const T& graph, size_t nVariables);
|
||||
// };
|
||||
|
|
|
@ -19,9 +19,10 @@ import unittest
|
|||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from gtwrap.interface_parser import (
|
||||
ArgumentList, Class, Constructor, ForwardDeclaration, GlobalFunction,
|
||||
Include, Method, Module, Namespace, Operator, ReturnType, StaticMethod,
|
||||
TemplatedType, Type, TypedefTemplateInstantiation, Typename, Variable)
|
||||
ArgumentList, Class, Constructor, Enum, Enumerator, ForwardDeclaration,
|
||||
GlobalFunction, Include, Method, Module, Namespace, Operator, ReturnType,
|
||||
StaticMethod, TemplatedType, Type, TypedefTemplateInstantiation, Typename,
|
||||
Variable)
|
||||
|
||||
|
||||
class TestInterfaceParser(unittest.TestCase):
|
||||
|
@ -180,7 +181,7 @@ class TestInterfaceParser(unittest.TestCase):
|
|||
def test_default_arguments(self):
|
||||
"""Tests any expression that is a valid default argument"""
|
||||
args = ArgumentList.rule.parseString(
|
||||
"string s=\"hello\", int a=3, "
|
||||
"string c = \"\", string s=\"hello\", int a=3, "
|
||||
"int b, double pi = 3.1415, "
|
||||
"gtsam::KeyFormatter kf = gtsam::DefaultKeyFormatter, "
|
||||
"std::vector<size_t> p = std::vector<size_t>(), "
|
||||
|
@ -188,22 +189,21 @@ class TestInterfaceParser(unittest.TestCase):
|
|||
)[0].args_list
|
||||
|
||||
# Test for basic types
|
||||
self.assertEqual(args[0].default, "hello")
|
||||
self.assertEqual(args[1].default, 3)
|
||||
# '' is falsy so we can check against it
|
||||
self.assertEqual(args[2].default, '')
|
||||
self.assertFalse(args[2].default)
|
||||
self.assertEqual(args[0].default, "")
|
||||
self.assertEqual(args[1].default, "hello")
|
||||
self.assertEqual(args[2].default, 3)
|
||||
# No default argument should set `default` to None
|
||||
self.assertIsNone(args[3].default)
|
||||
|
||||
self.assertEqual(args[3].default, 3.1415)
|
||||
self.assertEqual(args[4].default, 3.1415)
|
||||
|
||||
# Test non-basic type
|
||||
self.assertEqual(repr(args[4].default.typename),
|
||||
self.assertEqual(repr(args[5].default.typename),
|
||||
'gtsam::DefaultKeyFormatter')
|
||||
# Test templated type
|
||||
self.assertEqual(repr(args[5].default.typename), 'std::vector<size_t>')
|
||||
self.assertEqual(repr(args[6].default.typename), 'std::vector<size_t>')
|
||||
# Test for allowing list as default argument
|
||||
print(args)
|
||||
self.assertEqual(args[6].default, (1, 2, 'name', "random", 3.1415))
|
||||
self.assertEqual(args[7].default, (1, 2, 'name', "random", 3.1415))
|
||||
|
||||
def test_return_type(self):
|
||||
"""Test ReturnType"""
|
||||
|
@ -424,6 +424,17 @@ class TestInterfaceParser(unittest.TestCase):
|
|||
self.assertEqual(["gtsam"],
|
||||
ret.parent_class.instantiations[0].namespaces)
|
||||
|
||||
def test_class_with_enum(self):
|
||||
"""Test for class with nested enum."""
|
||||
ret = Class.rule.parseString("""
|
||||
class Pet {
|
||||
Pet(const string &name, Kind type);
|
||||
enum Kind { Dog, Cat };
|
||||
};
|
||||
""")[0]
|
||||
self.assertEqual(ret.name, "Pet")
|
||||
self.assertEqual(ret.enums[0].name, "Kind")
|
||||
|
||||
def test_include(self):
|
||||
"""Test for include statements."""
|
||||
include = Include.rule.parseString(
|
||||
|
@ -460,12 +471,33 @@ class TestInterfaceParser(unittest.TestCase):
|
|||
self.assertEqual(variable.ctype.typename.name, "string")
|
||||
self.assertEqual(variable.default, 9.81)
|
||||
|
||||
variable = Variable.rule.parseString("const string kGravity = 9.81;")[0]
|
||||
variable = Variable.rule.parseString(
|
||||
"const string kGravity = 9.81;")[0]
|
||||
self.assertEqual(variable.name, "kGravity")
|
||||
self.assertEqual(variable.ctype.typename.name, "string")
|
||||
self.assertTrue(variable.ctype.is_const)
|
||||
self.assertEqual(variable.default, 9.81)
|
||||
|
||||
def test_enumerator(self):
|
||||
"""Test for enumerator."""
|
||||
enumerator = Enumerator.rule.parseString("Dog")[0]
|
||||
self.assertEqual(enumerator.name, "Dog")
|
||||
|
||||
enumerator = Enumerator.rule.parseString("Cat")[0]
|
||||
self.assertEqual(enumerator.name, "Cat")
|
||||
|
||||
def test_enum(self):
|
||||
"""Test for enums."""
|
||||
enum = Enum.rule.parseString("""
|
||||
enum Kind {
|
||||
Dog,
|
||||
Cat
|
||||
};
|
||||
""")[0]
|
||||
self.assertEqual(enum.name, "Kind")
|
||||
self.assertEqual(enum.enumerators[0].name, "Dog")
|
||||
self.assertEqual(enum.enumerators[1].name, "Cat")
|
||||
|
||||
def test_namespace(self):
|
||||
"""Test for namespace parsing."""
|
||||
namespace = Namespace.rule.parseString("""
|
||||
|
|
|
@ -158,6 +158,17 @@ class TestWrap(unittest.TestCase):
|
|||
|
||||
self.compare_and_diff('special_cases_pybind.cpp', output)
|
||||
|
||||
def test_enum(self):
|
||||
"""
|
||||
Test if enum generation is correct.
|
||||
"""
|
||||
with open(osp.join(self.INTERFACE_DIR, 'enum.i'), 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
output = self.wrap_content(content, 'enum_py',
|
||||
self.PYTHON_ACTUAL_DIR)
|
||||
|
||||
self.compare_and_diff('enum_pybind.cpp', output)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
Loading…
Reference in New Issue