Squashed 'wrap/' changes from b80bc63cf..903694b77
903694b77 Merge pull request #92 from borglab/fix/global-variables abb74dd26 added support for default args, more tests, and docs cfa104257 Merge pull request #83 from borglab/feature/globalVariables fdd7b8cad fixes d4ceb63c6 add correct namespaces to global variable values 925c02c82 global variables works af62fdef7 unit test for global variable 3d3f3f3c9 add "Variable" to the global parsing rule ecfeb2025 rename "Property" to "Variable" and move into separate file git-subtree-dir: wrap git-subtree-split: 903694b777c4c25bd9cc82f8d3950b3bbc33d8f2release/4.3a0
parent
db373dfa52
commit
56d060b4c9
7
DOCS.md
7
DOCS.md
|
@ -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