From dfb1c59812cbf32a687ec5db08d87eca54869961 Mon Sep 17 00:00:00 2001 From: "Justin A. Irwin" Date: Mon, 1 Apr 2019 09:17:54 -0400 Subject: [PATCH 1/5] Process nested structs in IDL parsing so they can be looked up by the IDL library --- .../python/ossie/utils/sca/importIDL.py | 2 ++ redhawk/src/testing/tests/idl/TestIdl.idl | 32 +++++++++++++++++++ .../src/testing/tests/test_00_PythonUtils.py | 22 +++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 redhawk/src/testing/tests/idl/TestIdl.idl diff --git a/redhawk/src/base/framework/python/ossie/utils/sca/importIDL.py b/redhawk/src/base/framework/python/ossie/utils/sca/importIDL.py index 056be07b6..c3759e750 100644 --- a/redhawk/src/base/framework/python/ossie/utils/sca/importIDL.py +++ b/redhawk/src/base/framework/python/ossie/utils/sca/importIDL.py @@ -326,6 +326,8 @@ def visitInterface(self, node): node.accept(visitor) interface = visitor.interface _interfaces[node.repoId()] = interface + for decl in node.declarations(): + decl.accept(self) self.myInterfaces.append(interface) def run(tree, args): diff --git a/redhawk/src/testing/tests/idl/TestIdl.idl b/redhawk/src/testing/tests/idl/TestIdl.idl new file mode 100644 index 000000000..d7a7433fa --- /dev/null +++ b/redhawk/src/testing/tests/idl/TestIdl.idl @@ -0,0 +1,32 @@ +/* + * This file is protected by Copyright. Please refer to the COPYRIGHT file + * distributed with this source distribution. + * + * This file is part of REDHAWK core. + * + * REDHAWK core is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * REDHAWK core is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +/** + * This module contains IDL constructs for testing the Python IDL library. + */ +module TestIdl { + interface StructInterface { + struct NestedStruct { + long field; + }; + + NestedStruct func(); + }; +}; diff --git a/redhawk/src/testing/tests/test_00_PythonUtils.py b/redhawk/src/testing/tests/test_00_PythonUtils.py index fcc8a88fe..1106f8014 100644 --- a/redhawk/src/testing/tests/test_00_PythonUtils.py +++ b/redhawk/src/testing/tests/test_00_PythonUtils.py @@ -19,12 +19,15 @@ # along with this program. If not, see http://www.gnu.org/licenses/. # +import os import unittest import weakref from ossie.utils.notify import notification from ossie.utils import weakobj +from ossie.utils import idllib + class TestClass(object): def foo(self): return 'foo' @@ -263,3 +266,22 @@ def test_WeakBoundMethodCallback(self): del obj3 self.assertEqual(len(children), 0) + +class TestIdlLibrary(unittest.TestCase): + def setUp(self): + # Use a local IDL library path to have complete control over tests + idldir = os.path.abspath(os.path.join(os.path.dirname(__file__), 'idl')) + self.lib = idllib.IDLLibrary() + self.lib.addSearchPath(idldir) + + def test_NestedStruct(self): + """ + Tests that the IDL library allows looking up structs nested under + interfaces. + """ + repo_id = 'IDL:TestIdl/StructInterface/NestedStruct:1.0' + try: + structdef = self.lib.getIdlStruct(repo_id) + except idllib.UnknownInterfaceError as exc: + self.fail('Nested IDL struct was not found') + self.assertEqual(structdef.repoId, repo_id) From a746ac2492b251239fa2e64c04c7910f9a7d9116 Mon Sep 17 00:00:00 2001 From: "Justin A. Irwin" Date: Mon, 1 Apr 2019 11:55:42 -0400 Subject: [PATCH 2/5] Improve readability of generic uses port .cpp files by adding a space between methods, clean up excessive calculations in template --- .../cpp/ports/templates/generic.uses.cpp | 20 ++----------------- redhawk-codegen/redhawk/codegen/lang/cpp.py | 11 ++++++---- 2 files changed, 9 insertions(+), 22 deletions(-) diff --git a/redhawk-codegen/redhawk/codegen/jinja/cpp/ports/templates/generic.uses.cpp b/redhawk-codegen/redhawk/codegen/jinja/cpp/ports/templates/generic.uses.cpp index 6c6baf6a9..da48145d2 100644 --- a/redhawk-codegen/redhawk/codegen/jinja/cpp/ports/templates/generic.uses.cpp +++ b/redhawk-codegen/redhawk/codegen/jinja/cpp/ports/templates/generic.uses.cpp @@ -32,24 +32,8 @@ Port_Uses_base_impl(port_name) { } /*{% for operation in portgen.operations() %}*/ + //% set hasreturn = operation.returns != 'void' -/*{% if hasreturn %}*/ -/*{% set returnstate='true' %}*/ -/*{% else %}*/ -/*{% set returnstate='false' %}*/ -/*{% endif %}*/ -//% set hasout = operation.hasout -/*{% if hasout %}*/ -/*{% set _hasout='true' %}*/ -/*{% else %}*/ -/*{% set _hasout='false' %}*/ -/*{% endif %}*/ -//% set hasinout = operation.hasinout -/*{% if hasinout %}*/ -/*{% set _hasinout='true' %}*/ -/*{% else %}*/ -/*{% set _hasinout='false' %}*/ -/*{% endif %}*/ /*{% if operation.readwrite_attr %}*/ ${operation.returns} ${classname}::${operation.name}() { return _get_${operation.name}(""); @@ -71,7 +55,7 @@ Port_Uses_base_impl(port_name) boost::mutex::scoped_lock lock(updatingPortsLock); // don't want to process while command information is coming in - __evaluateRequestBasedOnConnections(__connection_id__, ${returnstate}, ${_hasinout}, ${_hasout}); + __evaluateRequestBasedOnConnections(__connection_id__, ${cpp.boolLiteral(hasreturn)}, ${cpp.boolLiteral(operation.hasinout)}, ${cpp.boolLiteral(operation.hasout)}); if (this->active) { for (i = this->outConnections.begin(); i != this->outConnections.end(); ++i) { if (not __connection_id__.empty() and __connection_id__ != (*i).second) diff --git a/redhawk-codegen/redhawk/codegen/lang/cpp.py b/redhawk-codegen/redhawk/codegen/lang/cpp.py index 80a473ef0..d8ac62cdd 100644 --- a/redhawk-codegen/redhawk/codegen/lang/cpp.py +++ b/redhawk-codegen/redhawk/codegen/lang/cpp.py @@ -79,6 +79,12 @@ def stringLiteral(string): def charLiteral(string): return "'" + string + "'" +def boolLiteral(value): + if value: + return TRUE + else: + return FALSE + def complexType(typename): return 'std::complex<%s>' % (cppType(typename),) @@ -127,10 +133,7 @@ def literal(value, typename, complex=False): else: return stringLiteral(value) elif typename == CorbaTypes.BOOLEAN: - if parseBoolean(value): - return TRUE - else: - return FALSE + return boolLiteral(parseBoolean(value)) elif typename in (CorbaTypes.LONGLONG, CorbaTypes.ULONGLONG): # Explicitly mark the literal as a 'long long' for 32-bit systems if not isinstance(value, list): From 7b76635f9224e6e56845dc765211842d81447c64 Mon Sep 17 00:00:00 2001 From: "Justin A. Irwin" Date: Mon, 1 Apr 2019 16:40:30 -0400 Subject: [PATCH 3/5] Redo C++ generic IDL type mappings for consistency, fixing the determination of variable length structs and removing a lot of special casing that wasn't necessary --- .../codegen/jinja/cpp/ports/generic.py | 236 ++++++++---------- .../python/ossie/utils/sca/importIDL.py | 5 +- 2 files changed, 111 insertions(+), 130 deletions(-) diff --git a/redhawk-codegen/redhawk/codegen/jinja/cpp/ports/generic.py b/redhawk-codegen/redhawk/codegen/jinja/cpp/ports/generic.py index 0d815e71e..13b27b51c 100644 --- a/redhawk-codegen/redhawk/codegen/jinja/cpp/ports/generic.py +++ b/redhawk-codegen/redhawk/codegen/jinja/cpp/ports/generic.py @@ -28,8 +28,6 @@ from generator import CppPortGenerator -from ossie.utils.sca.importIDL import SequenceType, BaseType - if not '__package__' in locals(): # Python 2.4 compatibility __package__ = __name__.rsplit('.', 1)[0] @@ -48,73 +46,55 @@ CORBA.tk_ulonglong: 'CORBA::ULongLong' } -def baseType(typeobj, direction=None): +def cppName(typeobj): + return '::'.join(typeobj.scopedName()) + +def newObject(typeobj): + return 'new %s()' % baseType(typeobj) + +def baseType(typeobj): kind = typeobj.kind() if kind in _baseMap: return _baseMap[kind] elif kind == CORBA.tk_void: return 'void' elif kind == CORBA.tk_string: - if direction == 'out': - return 'CORBA::String_out' - else: - return 'char*' + return 'char*' elif kind == CORBA.tk_any: return 'CORBA::Any' - elif kind == CORBA.tk_alias and \ - typeobj.aliasType().kind() == CORBA.tk_string: - return 'char*' - - name = '::'.join(typeobj.scopedName()) - if kind == CORBA.tk_objref: - if direction == 'out': - return name + '_out' - else: - return name + '_ptr' - elif kind == CORBA.tk_alias and isinstance(typeobj.aliasType(), SequenceType): - if direction == 'out': - return name + '_out' - else: - return name - else: - return name -def doesStructIncludeInterface(scopedName): - repo_id = 'IDL:' - if len(scopedName) == 1: - repo_id += scopedName[0] - else: - for name in scopedName: - repo_id += name + '/' - repo_id = repo_id[:-1] - repo_id += ':1.0' - idl = IDLStruct(repo_id) - _members = idl.members() - for member_key in _members: - if _members[member_key] in ['objref', 'alias', 'struct']: - return True - return False - -def baseReturnType(typeobj): + return cppName(typeobj) + +def isVariableLength(typeobj): + # Remove any aliases + typeobj = unaliasedType(typeobj) kind = typeobj.kind() if kind in _baseMap: - return _baseMap[kind] - elif kind == CORBA.tk_void: - return 'void' - elif kind == CORBA.tk_string: - return 'char*' - elif kind == CORBA.tk_any: - return 'CORBA::Any' - - name = '::'.join(typeobj.scopedName()) + # Basic numeric types are always fixed-length + return False + elif kind == CORBA.tk_struct: + # Structs are variable-length if any members are + repo_id = 'IDL:' + '/'.join(typeobj.scopedName()) + ':1.0' + idl = IDLStruct(repo_id) + for member_name, member_type in idl.idl()._fields: + if isVariableLength(member_type): + return True + return False + else: + # Assume everything else is variable-length + return True + +def returnType(typeobj): + name = baseType(typeobj) + kind = unaliasedType(typeobj).kind() if kind == CORBA.tk_objref: return name + '_ptr' elif kind == CORBA.tk_struct: - if doesStructIncludeInterface(typeobj.scopedName()): + if isVariableLength(typeobj): return name + '*' else: return name - elif kind == CORBA.tk_alias and isinstance(typeobj.aliasType(), SequenceType): + elif kind in (CORBA.tk_any, CORBA.tk_sequence): return name + '*' else: return name @@ -127,20 +107,18 @@ def unaliasedType(typeobj): return unaliasedType(typeobj.aliasType()) def temporaryType(typeobj): - kind = typeobj.kind() - if kind in _baseMap: - return _baseMap[kind] - elif kind == CORBA.tk_void: - return 'void' - elif kind == CORBA.tk_string: - return 'CORBA::String_var' - elif kind == CORBA.tk_any: - return 'CORBA::Any' - - name = '::'.join(typeobj.scopedName()) + name = baseType(typeobj) kind = unaliasedType(typeobj).kind() - if kind in (CORBA.tk_objref, CORBA.tk_sequence) or ((kind == CORBA.tk_struct) and doesStructIncludeInterface(typeobj.scopedName())): + if kind == CORBA.tk_string: + # Use the base type even when aliased + return 'CORBA::String_var' + elif kind in (CORBA.tk_objref, CORBA.tk_sequence, CORBA.tk_any): return name + '_var' + elif kind == CORBA.tk_struct: + if isVariableLength(typeobj): + return name + '_var' + else: + return name else: return name @@ -149,67 +127,77 @@ def temporaryValue(typeobj): if kind in _baseMap: return '0' elif kind == CORBA.tk_enum: - return '::'.join(typeobj.enumValues()[0].scopedName()) + enum_values = unaliasedType(typeobj).enumValues() + return cppName(enum_values[0]) elif kind == CORBA.tk_string: return 'CORBA::string_dup("")' - elif kind == CORBA.tk_sequence or ((kind == CORBA.tk_struct) and doesStructIncludeInterface(typeobj.scopedName())): - return 'new %s()' % '::'.join(typeobj.scopedName()) - elif ((kind == CORBA.tk_struct) and not doesStructIncludeInterface(typeobj.scopedName())): - return '%s()' % '::'.join(typeobj.scopedName()) + elif kind in (CORBA.tk_sequence, CORBA.tk_any): + return newObject(typeobj) + elif kind == CORBA.tk_struct: + if isVariableLength(typeobj): + return newObject(typeobj) + else: + # The default constructor is fine, no need to generate a temporary + # that does the same thing + return None else: return None -def passByValue(argType,direction): - if direction == 'in': - if not passConst(argType,direction): - return True - else: - kind = argType.kind() - if kind == CORBA.tk_string or \ - (kind == CORBA.tk_alias and argType.aliasType().kind() == CORBA.tk_string): - return True - else: - return False +def inType(typeobj): + kind = unaliasedType(typeobj).kind() + if kind == CORBA.tk_string: + # Strings are always "const char*" even when aliased because for a + # typedef the const applies to the full type (i.e., the pointer), but + # the contract is that the contents (char) cannot be modified + return 'const char*' + + name = baseType(typeobj) + if kind in _baseMap or kind == CORBA.tk_enum: + # Basic types are passed by value + return name + elif kind == CORBA.tk_objref: + return name + '_ptr' else: - kind = argType.kind() - if kind == CORBA.tk_alias and isinstance(argType.aliasType(), SequenceType): - if direction == 'out': - return True - elif direction == 'inout': - return False - elif kind == CORBA.tk_string: - if direction == 'out': - return True - elif direction == 'inout': - return False - elif kind == CORBA.tk_objref: - return True - else: - return False + return 'const '+name+'&' -def passConst(argType,direction): - if direction == 'in': - kind = argType.kind() - if kind in _baseMap or \ - (kind == CORBA.tk_alias and isinstance(argType.aliasType(), BaseType) and argType.aliasType().kind() != CORBA.tk_string) or \ - kind == CORBA.tk_objref or kind == CORBA.tk_enum: - return False - else: - return True +def outType(typeobj): + if typeobj.kind() == CORBA.tk_string: + # Strings (just the base type, not aliases) require special handling in + # that the out type is not derived from the normal mapping (char*) + return 'CORBA::String_out' + + name = baseType(typeobj) + kind = unaliasedType(typeobj).kind() + if kind in _baseMap or kind == CORBA.tk_enum: + # CORBA technically has "_out" typedefs for primitive types, but for + # consistency with prior versions just use a reference + return name + '&' + elif kind == CORBA.tk_struct and not isVariableLength(typeobj): + # Since omniORB directly uses the reference type, copy that here + return name + '&' + else: + return name+'_out' + +def inoutType(typeobj): + kind = unaliasedType(typeobj).kind() + if kind == CORBA.tk_string: + # Just for consistency with omniORB, remap all string types to the base + # type + return 'char*&' + + name = baseType(typeobj) + if kind == CORBA.tk_objref: + return name + '_ptr&' else: - return False + return name + '&' def argumentType(argType, direction): - name = baseType(argType,direction) - if not direction == 'in': - # sequence is special case because arg is different in C++ than the IDL - if argType == 'sequence': - return name + '_out' - if passConst(argType,direction): - name = 'const '+name - if not passByValue(argType,direction): - name = name + '&' - return name + if direction == 'in': + return inType(argType) + elif direction == 'out': + return outType(argType) + else: + return inoutType(argType) class GenericPortFactory(PortFactory): def match(self, port): @@ -247,24 +235,14 @@ def hasInOut(self): def operations(self): for op in self.idl.operations(): - _out = False - for p in op.params: - if p.direction == 'out': - _out = True - break - _inout = False - for p in op.params: - if p.direction == 'inout': - _inout = True - break yield {'name': op.name, 'arglist': ', '.join('%s %s' % (argumentType(p.paramType,p.direction), p.name) for p in op.params), 'argnames': ', '.join(p.name for p in op.params), - 'hasout': _out, - 'hasinout': _inout, + 'hasout': any(p.direction == 'out' for p in op.params), + 'hasinout': any(p.direction == 'inout' for p in op.params), 'temporary': temporaryType(op.returnType), 'initializer': temporaryValue(op.returnType), - 'returns': baseReturnType(op.returnType)} + 'returns': returnType(op.returnType)} # # for attributes of an interface...provide manipulator methods # @@ -278,7 +256,7 @@ def operations(self): 'readwrite_attr': readwrite_attr, 'temporary': temporaryType(attr.attrType), 'initializer': temporaryValue(attr.attrType), - 'returns': baseReturnType(attr.attrType)} + 'returns': returnType(attr.attrType)} if not attr.readonly: yield {'name': attr.name, 'arglist': argumentType(attr.attrType,'in') + ' data', diff --git a/redhawk/src/base/framework/python/ossie/utils/sca/importIDL.py b/redhawk/src/base/framework/python/ossie/utils/sca/importIDL.py index c3759e750..3e4c31a79 100644 --- a/redhawk/src/base/framework/python/ossie/utils/sca/importIDL.py +++ b/redhawk/src/base/framework/python/ossie/utils/sca/importIDL.py @@ -293,8 +293,11 @@ def __init__(self, node): self.name = '::'.join(node.scopedName()) self.repoId = node.repoId() self.members = {} + self._fields = [] for member in node.members(): - self.members[member.declarators()[0].scopedName()[-1]] = baseTypes[member.memberType().kind()] + name = member.declarators()[0].scopedName()[-1] + self.members[name] = baseTypes[member.memberType().kind()] + self._fields.append((name, IDLType.instance(member.memberType()))) class ExampleVisitor (idlvisitor.AstVisitor): def __init__(self,*args): From 033ba4a64525eb07da6d8c781d407be0a9c658f8 Mon Sep 17 00:00:00 2001 From: "Justin A. Irwin" Date: Wed, 3 Apr 2019 10:51:27 -0400 Subject: [PATCH 4/5] Process unions in IDL parser to allow proper handling in code generation. Unions are similar to structs in this case. However, while unions are treated more like a proper IDL type, structs cannot change without some ripple effect. Consolidated caching of structs and interfaces (and now, unions) to fix problems with nested types. --- .../codegen/jinja/cpp/ports/generic.py | 26 ++++--- .../framework/python/ossie/utils/idllib.py | 2 +- .../python/ossie/utils/sca/importIDL.py | 76 +++++++++++++++---- redhawk/src/testing/tests/idl/TestIdl.idl | 18 +++++ .../src/testing/tests/test_00_PythonUtils.py | 23 ++++++ 5 files changed, 118 insertions(+), 27 deletions(-) diff --git a/redhawk-codegen/redhawk/codegen/jinja/cpp/ports/generic.py b/redhawk-codegen/redhawk/codegen/jinja/cpp/ports/generic.py index 13b27b51c..af9101c1b 100644 --- a/redhawk-codegen/redhawk/codegen/jinja/cpp/ports/generic.py +++ b/redhawk-codegen/redhawk/codegen/jinja/cpp/ports/generic.py @@ -22,7 +22,7 @@ from omniORB import CORBA from redhawk.codegen.lang import cpp -from redhawk.codegen.lang.idl import IDLInterface, IDLStruct +from redhawk.codegen.lang import idl from redhawk.codegen.jinja.ports import PortFactory from redhawk.codegen.jinja.cpp import CppTemplate @@ -72,12 +72,18 @@ def isVariableLength(typeobj): if kind in _baseMap: # Basic numeric types are always fixed-length return False - elif kind == CORBA.tk_struct: - # Structs are variable-length if any members are + elif kind in (CORBA.tk_struct, CORBA.tk_union): + # Struct and unions are variable-length if any members are repo_id = 'IDL:' + '/'.join(typeobj.scopedName()) + ':1.0' - idl = IDLStruct(repo_id) - for member_name, member_type in idl.idl()._fields: - if isVariableLength(member_type): + idl_type = idl.idlRepo.getIdlStruct(repo_id) + if kind == CORBA.tk_struct: + # Work around insufficent information in public interface by using + # "private" interface that was added for this usage + members = idl_type._fields + else: + members = idl_type.members() + for member in members: + if isVariableLength(member.memberType()): return True return False else: @@ -89,7 +95,7 @@ def returnType(typeobj): kind = unaliasedType(typeobj).kind() if kind == CORBA.tk_objref: return name + '_ptr' - elif kind == CORBA.tk_struct: + elif kind in (CORBA.tk_struct, CORBA.tk_union): if isVariableLength(typeobj): return name + '*' else: @@ -114,7 +120,7 @@ def temporaryType(typeobj): return 'CORBA::String_var' elif kind in (CORBA.tk_objref, CORBA.tk_sequence, CORBA.tk_any): return name + '_var' - elif kind == CORBA.tk_struct: + elif kind in (CORBA.tk_struct, CORBA.tk_union): if isVariableLength(typeobj): return name + '_var' else: @@ -133,7 +139,7 @@ def temporaryValue(typeobj): return 'CORBA::string_dup("")' elif kind in (CORBA.tk_sequence, CORBA.tk_any): return newObject(typeobj) - elif kind == CORBA.tk_struct: + elif kind in (CORBA.tk_struct, CORBA.tk_union): if isVariableLength(typeobj): return newObject(typeobj) else: @@ -172,7 +178,7 @@ def outType(typeobj): # CORBA technically has "_out" typedefs for primitive types, but for # consistency with prior versions just use a reference return name + '&' - elif kind == CORBA.tk_struct and not isVariableLength(typeobj): + elif kind in (CORBA.tk_struct, CORBA.tk_union) and not isVariableLength(typeobj): # Since omniORB directly uses the reference type, copy that here return name + '&' else: diff --git a/redhawk/src/base/framework/python/ossie/utils/idllib.py b/redhawk/src/base/framework/python/ossie/utils/idllib.py index 5931f6bdc..0e0fde31b 100644 --- a/redhawk/src/base/framework/python/ossie/utils/idllib.py +++ b/redhawk/src/base/framework/python/ossie/utils/idllib.py @@ -102,7 +102,7 @@ def _importFile(self, filename): self._interfaces[interface.repoId] = interface # Mark the file that provided this interface as parsed - if not isinstance(interface, importIDL.IdlStruct): + if isinstance(interface, importIDL.Interface): self._parsed.add(interface.fullpath) # Mark the file as parsed in case it didn't contain any interfaces diff --git a/redhawk/src/base/framework/python/ossie/utils/sca/importIDL.py b/redhawk/src/base/framework/python/ossie/utils/sca/importIDL.py index 3e4c31a79..e09c2dde5 100644 --- a/redhawk/src/base/framework/python/ossie/utils/sca/importIDL.py +++ b/redhawk/src/base/framework/python/ossie/utils/sca/importIDL.py @@ -142,12 +142,36 @@ def __init__(self, scopedName, values): def enumValues(self): return self._enumValues +class Member(object): + def __init__(self, name, memberType): + self._name = name + self._memberType = memberType + + def name(self): + return self._name + + def memberType(self): + return self._memberType + +class Union(NamedType): + def __init__(self, scopedName, repoId, discriminant, members): + super(Union,self).__init__(CORBA.tk_union, scopedName) + self.name = scopedName[-1] + self.repoId = repoId + self._discriminant = discriminant + self._members = members + + def discriminant(self): + return self._discriminant + + def members(self): + return self._members + def ExceptionType(type): return NamedType(CORBA.tk_except, type.scopedName()) -# Internal cache of parsed IDL interfaces -_interfaces = {} -_structs = {} +# Internal cache of parsed IDL types +_cache = {} class Interface: def __init__(self,name,nameSpace="",operations=[],filename="",fullpath="",repoId=""): @@ -185,7 +209,6 @@ def __repr__(self): return retstr - class Operation: def __init__(self,name,returnType,params=[]): self.name = name @@ -297,7 +320,7 @@ def __init__(self, node): for member in node.members(): name = member.declarators()[0].scopedName()[-1] self.members[name] = baseTypes[member.memberType().kind()] - self._fields.append((name, IDLType.instance(member.memberType()))) + self._fields.append(Member(name, IDLType.instance(member.memberType()))) class ExampleVisitor (idlvisitor.AstVisitor): def __init__(self,*args): @@ -315,22 +338,44 @@ def visitModule(self, node): n.accept(self) def visitStruct(self, node): - # create the Attribute object - _struct = IdlStruct(node) - _structs[node.repoId()] = _struct - self.myStructs.append(_struct) + # Use cached post-processed struct if available + struct = _cache.get(node.repoId(), None) + if not struct: + struct = IdlStruct(node) + _cache[node.repoId()] = struct + self.myStructs.append(struct) + + def visitUnion(self, node): + # Use cached post-processed union if available + union = _cache.get(node.repoId(), None) + if not union: + members = [] + for case in node.cases(): + name = case.declarator().identifier() + idl_type = IDLType.instance(case.caseType()) + # Ignore case labels here, until there is a need to generate code + # based upon them + members.append(Member(name, idl_type)) + + union = Union(node.scopedName(), node.repoId(), IDLType.instance(node.switchType()), members) + _cache[union.repoId] = union + + self.myStructs.append(union) def visitInterface(self, node): # Use cached post-processed interface if available - global _interfaces - interface = _interfaces.get(node.repoId(), None) + interface = _cache.get(node.repoId(), None) if not interface: visitor = InterfaceVisitor() node.accept(visitor) interface = visitor.interface - _interfaces[node.repoId()] = interface - for decl in node.declarations(): - decl.accept(self) + _cache[node.repoId()] = interface + + # Process nested declarations regardless of cache status in case unions + # and structs were not requested last time + for decl in node.declarations(): + decl.accept(self) + self.myInterfaces.append(interface) def run(tree, args): @@ -382,8 +427,7 @@ def getInterfacesFromFile(filename, includepath=None, getStructs=False): x.filename = x.filename[:-4] #remove the .idl suffix if getStructs: - for _struct in structs: - ints = ints + structs + ints = ints + structs return ints diff --git a/redhawk/src/testing/tests/idl/TestIdl.idl b/redhawk/src/testing/tests/idl/TestIdl.idl index d7a7433fa..81a09b78e 100644 --- a/redhawk/src/testing/tests/idl/TestIdl.idl +++ b/redhawk/src/testing/tests/idl/TestIdl.idl @@ -29,4 +29,22 @@ module TestIdl { NestedStruct func(); }; + + union BasicUnion switch (short) { + case 0: octet octetValue; + case 1: long longValue; + case 2: + case 3: + float floatValue; + default: string stringValue; + }; + + interface UnionInterface { + union NestedUnion switch (long) { + case 0: string stringValue; + default: any anyValue; + }; + + NestedUnion func(); + }; }; diff --git a/redhawk/src/testing/tests/test_00_PythonUtils.py b/redhawk/src/testing/tests/test_00_PythonUtils.py index 1106f8014..cbd334c88 100644 --- a/redhawk/src/testing/tests/test_00_PythonUtils.py +++ b/redhawk/src/testing/tests/test_00_PythonUtils.py @@ -285,3 +285,26 @@ def test_NestedStruct(self): except idllib.UnknownInterfaceError as exc: self.fail('Nested IDL struct was not found') self.assertEqual(structdef.repoId, repo_id) + + def test_Union(self): + repo_id = 'IDL:TestIdl/BasicUnion:1.0' + try: + union = self.lib.getIdlStruct(repo_id) + except idllib.UnknownInterfaceError as exc: + self.fail('IDL union was not found') + self.assertEqual(union.repoId, repo_id) + + # Make sure it has the exactly the expected members + expected = ['octetValue', 'longValue', 'floatValue', 'stringValue'] + expected.sort() + actuals = [m.name() for m in union.members()] + actuals.sort() + self.assertEqual(expected, actuals) + + def test_NestedUnion(self): + repo_id = 'IDL:TestIdl/UnionInterface/NestedUnion:1.0' + try: + union = self.lib.getIdlStruct(repo_id) + except idllib.UnknownInterfaceError as exc: + self.fail('Nested IDL union was not found') + self.assertEqual(union.repoId, repo_id) From fffcba4ee9b36d17fe3c780100c07b9b314421f3 Mon Sep 17 00:00:00 2001 From: "Justin A. Irwin" Date: Wed, 3 Apr 2019 12:44:47 -0400 Subject: [PATCH 5/5] Fix regression with method name change --- redhawk-codegen/redhawk/codegen/jinja/cpp/ports/generic.py | 3 +++ redhawk-codegen/redhawk/codegen/jinja/cpp/service/mapping.py | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/redhawk-codegen/redhawk/codegen/jinja/cpp/ports/generic.py b/redhawk-codegen/redhawk/codegen/jinja/cpp/ports/generic.py index af9101c1b..02a9ba838 100644 --- a/redhawk-codegen/redhawk/codegen/jinja/cpp/ports/generic.py +++ b/redhawk-codegen/redhawk/codegen/jinja/cpp/ports/generic.py @@ -105,6 +105,9 @@ def returnType(typeobj): else: return name +# Preserve old name in case other places still reference it +baseReturnType = returnType + def unaliasedType(typeobj): kind = typeobj.kind() if kind != CORBA.tk_alias: diff --git a/redhawk-codegen/redhawk/codegen/jinja/cpp/service/mapping.py b/redhawk-codegen/redhawk/codegen/jinja/cpp/service/mapping.py index 8f33f62b1..b0592da3a 100644 --- a/redhawk-codegen/redhawk/codegen/jinja/cpp/service/mapping.py +++ b/redhawk-codegen/redhawk/codegen/jinja/cpp/service/mapping.py @@ -70,12 +70,12 @@ def getOperations(self, idl): operations.append({'name': op.name, 'arglist': ', '.join('%s %s' % (generic.argumentType(p.paramType,p.direction), p.name) for p in op.params), 'argnames': ', '.join(p.name for p in op.params), - 'returns': generic.baseReturnType(op.returnType)}) + 'returns': generic.returnType(op.returnType)}) for attr in idl.attributes(): operations.append({'name': attr.name, 'arglist': '', 'argnames': '', - 'returns': generic.baseReturnType(attr.attrType)}) + 'returns': generic.returnType(attr.attrType)}) if not attr.readonly: operations.append({'name': attr.name, 'arglist': generic.baseType(attr.attrType) + ' data',