commit
e9202b836f
|
@ -91,6 +91,13 @@ The python wrapper supports keyword arguments for functions/methods. Hence, the
|
|||
```cpp
|
||||
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
|
||||
- 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,
|
||||
RPAREN, SEMI_COLON, STATIC, VIRTUAL, OPERATOR)
|
||||
from .type import TemplatedType, Type, Typename
|
||||
from .variable import Variable
|
||||
|
||||
|
||||
class Method:
|
||||
|
@ -136,32 +137,6 @@ class Constructor:
|
|||
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:
|
||||
"""
|
||||
Rule for parsing operator overloads.
|
||||
|
@ -256,12 +231,12 @@ class Class:
|
|||
Rule for all the members within a class.
|
||||
"""
|
||||
rule = ZeroOrMore(Constructor.rule ^ StaticMethod.rule ^ Method.rule
|
||||
^ Property.rule ^ Operator.rule).setParseAction(
|
||||
^ Variable.rule ^ Operator.rule).setParseAction(
|
||||
lambda t: Class.Members(t.asList()))
|
||||
|
||||
def __init__(self,
|
||||
members: List[Union[Constructor, Method, StaticMethod,
|
||||
Property, Operator]]):
|
||||
Variable, Operator]]):
|
||||
self.ctors = []
|
||||
self.methods = []
|
||||
self.static_methods = []
|
||||
|
@ -274,7 +249,7 @@ class Class:
|
|||
self.methods.append(m)
|
||||
elif isinstance(m, StaticMethod):
|
||||
self.static_methods.append(m)
|
||||
elif isinstance(m, Property):
|
||||
elif isinstance(m, Variable):
|
||||
self.properties.append(m)
|
||||
elif isinstance(m, Operator):
|
||||
self.operators.append(m)
|
||||
|
@ -311,7 +286,7 @@ class Class:
|
|||
ctors: List[Constructor],
|
||||
methods: List[Method],
|
||||
static_methods: List[StaticMethod],
|
||||
properties: List[Property],
|
||||
properties: List[Variable],
|
||||
operators: List[Operator],
|
||||
parent: str = '',
|
||||
):
|
||||
|
|
|
@ -23,6 +23,7 @@ from .declaration import ForwardDeclaration, Include
|
|||
from .function import GlobalFunction
|
||||
from .namespace import Namespace
|
||||
from .template import TypedefTemplateInstantiation
|
||||
from .variable import Variable
|
||||
|
||||
|
||||
class Module:
|
||||
|
@ -43,6 +44,7 @@ class Module:
|
|||
^ Class.rule #
|
||||
^ TypedefTemplateInstantiation.rule #
|
||||
^ GlobalFunction.rule #
|
||||
^ Variable.rule #
|
||||
^ Namespace.rule #
|
||||
).setParseAction(lambda t: Namespace('', t.asList())) +
|
||||
stringEnd)
|
||||
|
|
|
@ -22,6 +22,7 @@ from .function import GlobalFunction
|
|||
from .template import TypedefTemplateInstantiation
|
||||
from .tokens import IDENT, LBRACE, NAMESPACE, RBRACE
|
||||
from .type import Typename
|
||||
from .variable import Variable
|
||||
|
||||
|
||||
def find_sub_namespace(namespace: "Namespace",
|
||||
|
@ -67,6 +68,7 @@ class Namespace:
|
|||
^ Class.rule #
|
||||
^ TypedefTemplateInstantiation.rule #
|
||||
^ GlobalFunction.rule #
|
||||
^ Variable.rule #
|
||||
^ rule #
|
||||
)("content") # BR
|
||||
+ 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
|
||||
|
||||
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):
|
||||
"""Wrap all the properties in the `cpp_class`."""
|
||||
res = ""
|
||||
|
@ -341,6 +351,13 @@ class PybindWrapper:
|
|||
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_var=module_var,
|
||||
variable=element,
|
||||
prefix='\n' + ' ' * 4
|
||||
)
|
||||
|
||||
# Global functions.
|
||||
all_funcs = [
|
||||
|
|
|
@ -50,12 +50,14 @@ PYBIND11_MODULE(namespaces_py, m_) {
|
|||
py::class_<ns2::ClassC, std::shared_ptr<ns2::ClassC>>(m_ns2, "ClassC")
|
||||
.def(py::init<>());
|
||||
|
||||
m_ns2.attr("aNs2Var") = ns2::aNs2Var;
|
||||
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, double b){return ns2::overloadedGlobalFunction(a, b);}, py::arg("a"), py::arg("b"));
|
||||
py::class_<ClassD, std::shared_ptr<ClassD>>(m_, "ClassD")
|
||||
.def(py::init<>());
|
||||
|
||||
m_.attr("aGlobalVar") = aGlobalVar;
|
||||
|
||||
#include "python/specializations.h"
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ class ClassB {
|
|||
// check namespace handling
|
||||
Vector aGlobalFunction();
|
||||
|
||||
}
|
||||
} // namespace ns1
|
||||
|
||||
#include <path/to/ns2.h>
|
||||
namespace ns2 {
|
||||
|
@ -38,7 +38,7 @@ class ClassB {
|
|||
ClassB();
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace ns3
|
||||
|
||||
class ClassC {
|
||||
ClassC();
|
||||
|
@ -51,10 +51,12 @@ Vector aGlobalFunction();
|
|||
ns1::ClassA overloadedGlobalFunction(const ns1::ClassA& a);
|
||||
ns1::ClassA overloadedGlobalFunction(const ns1::ClassA& a, double b);
|
||||
|
||||
} //\namespace ns2
|
||||
int aNs2Var;
|
||||
|
||||
} // namespace ns2
|
||||
|
||||
class ClassD {
|
||||
ClassD();
|
||||
};
|
||||
|
||||
|
||||
int aGlobalVar;
|
||||
|
|
|
@ -18,12 +18,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)
|
||||
from gtwrap.interface_parser import (
|
||||
ArgumentList, Class, Constructor, ForwardDeclaration, GlobalFunction,
|
||||
Include, Method, Module, Namespace, Operator, ReturnType, StaticMethod,
|
||||
TemplatedType, Type, TypedefTemplateInstantiation, Typename, Variable)
|
||||
|
||||
|
||||
class TestInterfaceParser(unittest.TestCase):
|
||||
|
@ -199,7 +197,8 @@ class TestInterfaceParser(unittest.TestCase):
|
|||
self.assertEqual(args[3].default, 3.1415)
|
||||
|
||||
# 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
|
||||
self.assertEqual(repr(args[5].default.typename), 'std::vector<size_t>')
|
||||
# Test for allowing list as default argument
|
||||
|
@ -422,7 +421,8 @@ class TestInterfaceParser(unittest.TestCase):
|
|||
self.assertEqual("BetweenFactor", ret.parent_class.name)
|
||||
self.assertEqual(["gtsam"], ret.parent_class.namespaces)
|
||||
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):
|
||||
"""Test for include statements."""
|
||||
|
@ -449,6 +449,23 @@ class TestInterfaceParser(unittest.TestCase):
|
|||
self.assertEqual("Values", func.return_type.type1.typename.name)
|
||||
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):
|
||||
"""Test for namespace parsing."""
|
||||
namespace = Namespace.rule.parseString("""
|
||||
|
@ -505,17 +522,21 @@ class TestInterfaceParser(unittest.TestCase):
|
|||
|
||||
};
|
||||
}
|
||||
int oneVar;
|
||||
}
|
||||
|
||||
class Global{
|
||||
};
|
||||
int globalVar;
|
||||
""")
|
||||
|
||||
# print("module: ", module)
|
||||
# print(dir(module.content[0].name))
|
||||
self.assertEqual(["one", "Global"], [x.name for x in module.content])
|
||||
self.assertEqual(["two", "two_dummy", "two"],
|
||||
self.assertEqual(["one", "Global", "globalVar"],
|
||||
[x.name for x in module.content])
|
||||
self.assertEqual(["two", "two_dummy", "two", "oneVar"],
|
||||
[x.name for x in module.content[0].content])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
Loading…
Reference in New Issue