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: 903694b777c4c25bd9cc82f8d3950b3bbc33d8f2
release/4.3a0
Varun Agrawal 2021-04-19 16:09:06 -04:00
parent db373dfa52
commit 56d060b4c9
9 changed files with 125 additions and 44 deletions

View File

@ -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.

View File

@ -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 = '',
):

View File

@ -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)

View File

@ -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 #

View File

@ -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)

View File

@ -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 = [

View File

@ -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"

View File

@ -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;

View File

@ -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()