Merging 'master' into 'wrap'
commit
f1cb194ce4
|
@ -91,6 +91,13 @@ The python wrapper supports keyword arguments for functions/methods. Hence, the
|
||||||
```cpp
|
```cpp
|
||||||
template<T, R, S>
|
template<T, R, S>
|
||||||
```
|
```
|
||||||
|
- Global variables
|
||||||
|
- Similar to global functions, the wrapper supports global variables as well.
|
||||||
|
- Currently we only support primitive types, such as `double`, `int`, `string`, etc.
|
||||||
|
- E.g.
|
||||||
|
```cpp
|
||||||
|
const double kGravity = -9.81;
|
||||||
|
```
|
||||||
|
|
||||||
- Using classes defined in other modules
|
- 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. `OtherClass` should be in the same project.
|
- 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. `OtherClass` should be in the same project.
|
||||||
|
|
|
@ -19,6 +19,7 @@ from .template import Template
|
||||||
from .tokens import (CLASS, COLON, CONST, IDENT, LBRACE, LPAREN, RBRACE,
|
from .tokens import (CLASS, COLON, CONST, IDENT, LBRACE, LPAREN, RBRACE,
|
||||||
RPAREN, SEMI_COLON, STATIC, VIRTUAL, OPERATOR)
|
RPAREN, SEMI_COLON, STATIC, VIRTUAL, OPERATOR)
|
||||||
from .type import TemplatedType, Type, Typename
|
from .type import TemplatedType, Type, Typename
|
||||||
|
from .variable import Variable
|
||||||
|
|
||||||
|
|
||||||
class Method:
|
class Method:
|
||||||
|
@ -136,32 +137,6 @@ class Constructor:
|
||||||
return "Constructor: {}".format(self.name)
|
return "Constructor: {}".format(self.name)
|
||||||
|
|
||||||
|
|
||||||
class Property:
|
|
||||||
"""
|
|
||||||
Rule to parse the variable members of a class.
|
|
||||||
|
|
||||||
E.g.
|
|
||||||
```
|
|
||||||
class Hello {
|
|
||||||
string name; // This is a property.
|
|
||||||
};
|
|
||||||
````
|
|
||||||
"""
|
|
||||||
rule = (
|
|
||||||
(Type.rule ^ TemplatedType.rule)("ctype") #
|
|
||||||
+ IDENT("name") #
|
|
||||||
+ SEMI_COLON #
|
|
||||||
).setParseAction(lambda t: Property(t.ctype, t.name))
|
|
||||||
|
|
||||||
def __init__(self, ctype: Type, name: str, parent=''):
|
|
||||||
self.ctype = ctype[0] # ParseResult is a list
|
|
||||||
self.name = name
|
|
||||||
self.parent = parent
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return '{} {}'.format(self.ctype.__repr__(), self.name)
|
|
||||||
|
|
||||||
|
|
||||||
class Operator:
|
class Operator:
|
||||||
"""
|
"""
|
||||||
Rule for parsing operator overloads.
|
Rule for parsing operator overloads.
|
||||||
|
@ -256,12 +231,12 @@ class Class:
|
||||||
Rule for all the members within a class.
|
Rule for all the members within a class.
|
||||||
"""
|
"""
|
||||||
rule = ZeroOrMore(Constructor.rule ^ StaticMethod.rule ^ Method.rule
|
rule = ZeroOrMore(Constructor.rule ^ StaticMethod.rule ^ Method.rule
|
||||||
^ Property.rule ^ Operator.rule).setParseAction(
|
^ Variable.rule ^ Operator.rule).setParseAction(
|
||||||
lambda t: Class.Members(t.asList()))
|
lambda t: Class.Members(t.asList()))
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
members: List[Union[Constructor, Method, StaticMethod,
|
members: List[Union[Constructor, Method, StaticMethod,
|
||||||
Property, Operator]]):
|
Variable, Operator]]):
|
||||||
self.ctors = []
|
self.ctors = []
|
||||||
self.methods = []
|
self.methods = []
|
||||||
self.static_methods = []
|
self.static_methods = []
|
||||||
|
@ -274,7 +249,7 @@ class Class:
|
||||||
self.methods.append(m)
|
self.methods.append(m)
|
||||||
elif isinstance(m, StaticMethod):
|
elif isinstance(m, StaticMethod):
|
||||||
self.static_methods.append(m)
|
self.static_methods.append(m)
|
||||||
elif isinstance(m, Property):
|
elif isinstance(m, Variable):
|
||||||
self.properties.append(m)
|
self.properties.append(m)
|
||||||
elif isinstance(m, Operator):
|
elif isinstance(m, Operator):
|
||||||
self.operators.append(m)
|
self.operators.append(m)
|
||||||
|
@ -311,7 +286,7 @@ class Class:
|
||||||
ctors: List[Constructor],
|
ctors: List[Constructor],
|
||||||
methods: List[Method],
|
methods: List[Method],
|
||||||
static_methods: List[StaticMethod],
|
static_methods: List[StaticMethod],
|
||||||
properties: List[Property],
|
properties: List[Variable],
|
||||||
operators: List[Operator],
|
operators: List[Operator],
|
||||||
parent: str = '',
|
parent: str = '',
|
||||||
):
|
):
|
||||||
|
|
|
@ -23,6 +23,7 @@ from .declaration import ForwardDeclaration, Include
|
||||||
from .function import GlobalFunction
|
from .function import GlobalFunction
|
||||||
from .namespace import Namespace
|
from .namespace import Namespace
|
||||||
from .template import TypedefTemplateInstantiation
|
from .template import TypedefTemplateInstantiation
|
||||||
|
from .variable import Variable
|
||||||
|
|
||||||
|
|
||||||
class Module:
|
class Module:
|
||||||
|
@ -43,6 +44,7 @@ class Module:
|
||||||
^ Class.rule #
|
^ Class.rule #
|
||||||
^ TypedefTemplateInstantiation.rule #
|
^ TypedefTemplateInstantiation.rule #
|
||||||
^ GlobalFunction.rule #
|
^ GlobalFunction.rule #
|
||||||
|
^ Variable.rule #
|
||||||
^ Namespace.rule #
|
^ Namespace.rule #
|
||||||
).setParseAction(lambda t: Namespace('', t.asList())) +
|
).setParseAction(lambda t: Namespace('', t.asList())) +
|
||||||
stringEnd)
|
stringEnd)
|
||||||
|
|
|
@ -22,6 +22,7 @@ from .function import GlobalFunction
|
||||||
from .template import TypedefTemplateInstantiation
|
from .template import TypedefTemplateInstantiation
|
||||||
from .tokens import IDENT, LBRACE, NAMESPACE, RBRACE
|
from .tokens import IDENT, LBRACE, NAMESPACE, RBRACE
|
||||||
from .type import Typename
|
from .type import Typename
|
||||||
|
from .variable import Variable
|
||||||
|
|
||||||
|
|
||||||
def find_sub_namespace(namespace: "Namespace",
|
def find_sub_namespace(namespace: "Namespace",
|
||||||
|
@ -67,6 +68,7 @@ class Namespace:
|
||||||
^ Class.rule #
|
^ Class.rule #
|
||||||
^ TypedefTemplateInstantiation.rule #
|
^ TypedefTemplateInstantiation.rule #
|
||||||
^ GlobalFunction.rule #
|
^ GlobalFunction.rule #
|
||||||
|
^ Variable.rule #
|
||||||
^ rule #
|
^ rule #
|
||||||
)("content") # BR
|
)("content") # BR
|
||||||
+ RBRACE #
|
+ RBRACE #
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
"""
|
||||||
|
GTSAM Copyright 2010-2020, Georgia Tech Research Corporation,
|
||||||
|
Atlanta, Georgia 30332-0415
|
||||||
|
All Rights Reserved
|
||||||
|
|
||||||
|
See LICENSE for the license information
|
||||||
|
|
||||||
|
Parser classes and rules for parsing C++ variables.
|
||||||
|
|
||||||
|
Author: Varun Agrawal, Gerry Chen
|
||||||
|
"""
|
||||||
|
|
||||||
|
from pyparsing import Optional, ParseResults
|
||||||
|
|
||||||
|
from .tokens import DEFAULT_ARG, EQUAL, IDENT, SEMI_COLON, STATIC
|
||||||
|
from .type import TemplatedType, Type
|
||||||
|
|
||||||
|
|
||||||
|
class Variable:
|
||||||
|
"""
|
||||||
|
Rule to parse variables.
|
||||||
|
Variables are a combination of Type/TemplatedType and the variable identifier.
|
||||||
|
|
||||||
|
E.g.
|
||||||
|
```
|
||||||
|
class Hello {
|
||||||
|
string name; // This is a property variable.
|
||||||
|
};
|
||||||
|
|
||||||
|
Vector3 kGravity; // This is a global variable.
|
||||||
|
````
|
||||||
|
"""
|
||||||
|
rule = ((Type.rule ^ TemplatedType.rule)("ctype") #
|
||||||
|
+ IDENT("name") #
|
||||||
|
#TODO(Varun) Add support for non-basic types
|
||||||
|
+ Optional(EQUAL + (DEFAULT_ARG))("default") #
|
||||||
|
+ SEMI_COLON #
|
||||||
|
).setParseAction(lambda t: Variable(t.ctype, t.name, t.default))
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
ctype: Type,
|
||||||
|
name: str,
|
||||||
|
default: ParseResults = None,
|
||||||
|
parent=''):
|
||||||
|
self.ctype = ctype[0] # ParseResult is a list
|
||||||
|
self.name = name
|
||||||
|
if default:
|
||||||
|
self.default = default[0]
|
||||||
|
|
||||||
|
self.parent = parent
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return '{} {}'.format(self.ctype.__repr__(), self.name)
|
|
@ -186,6 +186,16 @@ class PybindWrapper:
|
||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
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
|
||||||
|
)
|
||||||
|
|
||||||
def wrap_properties(self, properties, cpp_class, prefix='\n' + ' ' * 8):
|
def wrap_properties(self, properties, cpp_class, prefix='\n' + ' ' * 8):
|
||||||
"""Wrap all the properties in the `cpp_class`."""
|
"""Wrap all the properties in the `cpp_class`."""
|
||||||
res = ""
|
res = ""
|
||||||
|
@ -341,6 +351,13 @@ class PybindWrapper:
|
||||||
includes += includes_namespace
|
includes += includes_namespace
|
||||||
elif isinstance(element, instantiator.InstantiatedClass):
|
elif isinstance(element, instantiator.InstantiatedClass):
|
||||||
wrapped += self.wrap_instantiated_class(element)
|
wrapped += self.wrap_instantiated_class(element)
|
||||||
|
elif isinstance(element, parser.Variable):
|
||||||
|
wrapped += self.wrap_variable(
|
||||||
|
module=self._add_namespaces('', namespaces),
|
||||||
|
module_var=module_var,
|
||||||
|
variable=element,
|
||||||
|
prefix='\n' + ' ' * 4
|
||||||
|
)
|
||||||
|
|
||||||
# Global functions.
|
# Global functions.
|
||||||
all_funcs = [
|
all_funcs = [
|
||||||
|
|
|
@ -50,12 +50,14 @@ PYBIND11_MODULE(namespaces_py, m_) {
|
||||||
py::class_<ns2::ClassC, std::shared_ptr<ns2::ClassC>>(m_ns2, "ClassC")
|
py::class_<ns2::ClassC, std::shared_ptr<ns2::ClassC>>(m_ns2, "ClassC")
|
||||||
.def(py::init<>());
|
.def(py::init<>());
|
||||||
|
|
||||||
|
m_ns2.attr("aNs2Var") = ns2::aNs2Var;
|
||||||
m_ns2.def("aGlobalFunction",[](){return ns2::aGlobalFunction();});
|
m_ns2.def("aGlobalFunction",[](){return ns2::aGlobalFunction();});
|
||||||
m_ns2.def("overloadedGlobalFunction",[](const ns1::ClassA& a){return ns2::overloadedGlobalFunction(a);}, py::arg("a"));
|
m_ns2.def("overloadedGlobalFunction",[](const ns1::ClassA& a){return ns2::overloadedGlobalFunction(a);}, py::arg("a"));
|
||||||
m_ns2.def("overloadedGlobalFunction",[](const ns1::ClassA& a, double b){return ns2::overloadedGlobalFunction(a, b);}, py::arg("a"), py::arg("b"));
|
m_ns2.def("overloadedGlobalFunction",[](const ns1::ClassA& a, double b){return ns2::overloadedGlobalFunction(a, b);}, py::arg("a"), py::arg("b"));
|
||||||
py::class_<ClassD, std::shared_ptr<ClassD>>(m_, "ClassD")
|
py::class_<ClassD, std::shared_ptr<ClassD>>(m_, "ClassD")
|
||||||
.def(py::init<>());
|
.def(py::init<>());
|
||||||
|
|
||||||
|
m_.attr("aGlobalVar") = aGlobalVar;
|
||||||
|
|
||||||
#include "python/specializations.h"
|
#include "python/specializations.h"
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ class ClassB {
|
||||||
// check namespace handling
|
// check namespace handling
|
||||||
Vector aGlobalFunction();
|
Vector aGlobalFunction();
|
||||||
|
|
||||||
}
|
} // namespace ns1
|
||||||
|
|
||||||
#include <path/to/ns2.h>
|
#include <path/to/ns2.h>
|
||||||
namespace ns2 {
|
namespace ns2 {
|
||||||
|
@ -38,7 +38,7 @@ class ClassB {
|
||||||
ClassB();
|
ClassB();
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
} // namespace ns3
|
||||||
|
|
||||||
class ClassC {
|
class ClassC {
|
||||||
ClassC();
|
ClassC();
|
||||||
|
@ -51,10 +51,12 @@ Vector aGlobalFunction();
|
||||||
ns1::ClassA overloadedGlobalFunction(const ns1::ClassA& a);
|
ns1::ClassA overloadedGlobalFunction(const ns1::ClassA& a);
|
||||||
ns1::ClassA overloadedGlobalFunction(const ns1::ClassA& a, double b);
|
ns1::ClassA overloadedGlobalFunction(const ns1::ClassA& a, double b);
|
||||||
|
|
||||||
} //\namespace ns2
|
int aNs2Var;
|
||||||
|
|
||||||
|
} // namespace ns2
|
||||||
|
|
||||||
class ClassD {
|
class ClassD {
|
||||||
ClassD();
|
ClassD();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
int aGlobalVar;
|
||||||
|
|
|
@ -18,12 +18,10 @@ import unittest
|
||||||
|
|
||||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
|
||||||
from gtwrap.interface_parser import (ArgumentList, Class, Constructor,
|
from gtwrap.interface_parser import (
|
||||||
ForwardDeclaration, GlobalFunction,
|
ArgumentList, Class, Constructor, ForwardDeclaration, GlobalFunction,
|
||||||
Include, Method, Module, Namespace,
|
Include, Method, Module, Namespace, Operator, ReturnType, StaticMethod,
|
||||||
Operator, ReturnType, StaticMethod,
|
TemplatedType, Type, TypedefTemplateInstantiation, Typename, Variable)
|
||||||
TemplatedType, Type,
|
|
||||||
TypedefTemplateInstantiation, Typename)
|
|
||||||
|
|
||||||
|
|
||||||
class TestInterfaceParser(unittest.TestCase):
|
class TestInterfaceParser(unittest.TestCase):
|
||||||
|
@ -199,7 +197,8 @@ class TestInterfaceParser(unittest.TestCase):
|
||||||
self.assertEqual(args[3].default, 3.1415)
|
self.assertEqual(args[3].default, 3.1415)
|
||||||
|
|
||||||
# Test non-basic type
|
# Test non-basic type
|
||||||
self.assertEqual(repr(args[4].default.typename), 'gtsam::DefaultKeyFormatter')
|
self.assertEqual(repr(args[4].default.typename),
|
||||||
|
'gtsam::DefaultKeyFormatter')
|
||||||
# Test templated type
|
# Test templated type
|
||||||
self.assertEqual(repr(args[5].default.typename), 'std::vector<size_t>')
|
self.assertEqual(repr(args[5].default.typename), 'std::vector<size_t>')
|
||||||
# Test for allowing list as default argument
|
# Test for allowing list as default argument
|
||||||
|
@ -422,7 +421,8 @@ class TestInterfaceParser(unittest.TestCase):
|
||||||
self.assertEqual("BetweenFactor", ret.parent_class.name)
|
self.assertEqual("BetweenFactor", ret.parent_class.name)
|
||||||
self.assertEqual(["gtsam"], ret.parent_class.namespaces)
|
self.assertEqual(["gtsam"], ret.parent_class.namespaces)
|
||||||
self.assertEqual("Pose3", ret.parent_class.instantiations[0].name)
|
self.assertEqual("Pose3", ret.parent_class.instantiations[0].name)
|
||||||
self.assertEqual(["gtsam"], ret.parent_class.instantiations[0].namespaces)
|
self.assertEqual(["gtsam"],
|
||||||
|
ret.parent_class.instantiations[0].namespaces)
|
||||||
|
|
||||||
def test_include(self):
|
def test_include(self):
|
||||||
"""Test for include statements."""
|
"""Test for include statements."""
|
||||||
|
@ -449,6 +449,23 @@ class TestInterfaceParser(unittest.TestCase):
|
||||||
self.assertEqual("Values", func.return_type.type1.typename.name)
|
self.assertEqual("Values", func.return_type.type1.typename.name)
|
||||||
self.assertEqual(3, len(func.args))
|
self.assertEqual(3, len(func.args))
|
||||||
|
|
||||||
|
def test_global_variable(self):
|
||||||
|
"""Test for global variable."""
|
||||||
|
variable = Variable.rule.parseString("string kGravity;")[0]
|
||||||
|
self.assertEqual(variable.name, "kGravity")
|
||||||
|
self.assertEqual(variable.ctype.typename.name, "string")
|
||||||
|
|
||||||
|
variable = Variable.rule.parseString("string kGravity = 9.81;")[0]
|
||||||
|
self.assertEqual(variable.name, "kGravity")
|
||||||
|
self.assertEqual(variable.ctype.typename.name, "string")
|
||||||
|
self.assertEqual(variable.default, 9.81)
|
||||||
|
|
||||||
|
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_namespace(self):
|
def test_namespace(self):
|
||||||
"""Test for namespace parsing."""
|
"""Test for namespace parsing."""
|
||||||
namespace = Namespace.rule.parseString("""
|
namespace = Namespace.rule.parseString("""
|
||||||
|
@ -505,17 +522,21 @@ class TestInterfaceParser(unittest.TestCase):
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
int oneVar;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Global{
|
class Global{
|
||||||
};
|
};
|
||||||
|
int globalVar;
|
||||||
""")
|
""")
|
||||||
|
|
||||||
# print("module: ", module)
|
# print("module: ", module)
|
||||||
# print(dir(module.content[0].name))
|
# print(dir(module.content[0].name))
|
||||||
self.assertEqual(["one", "Global"], [x.name for x in module.content])
|
self.assertEqual(["one", "Global", "globalVar"],
|
||||||
self.assertEqual(["two", "two_dummy", "two"],
|
[x.name for x in module.content])
|
||||||
|
self.assertEqual(["two", "two_dummy", "two", "oneVar"],
|
||||||
[x.name for x in module.content[0].content])
|
[x.name for x in module.content[0].content])
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
Loading…
Reference in New Issue