diff options
author | Terry May <terrydmay@gmail.com> | 2019-09-09 19:45:41 -0400 |
---|---|---|
committer | Martin Braun <martin@gnuradio.org> | 2020-02-18 22:04:15 -0800 |
commit | 64ee803068269fe3b9563fd415c420fb513a1c9b (patch) | |
tree | d27e9b5560b5bb4377f964e39772cd1395a7bff0 | |
parent | 012870af22ae873b3b998691de8e81752179d266 (diff) |
grc: Fix C++ code generation
- Added support for C++ std::map<> from python dict
- Fixed default initialization of parameters in main()
- Added missing include for realtime scheduling
- Added default CMake option for std=c++11 as this is required for
gnuradio >= 3.8
- Fixed mako template to add initialization to parameter declarations in
main()
- Fixed C++ constructor initialization list generation
- Fixed parameter type determination when an lvalue is used in an rvalue
expression
- Improved determining type of C++ variables
- Added C++ support for gr_complex parameters
- Fixed C++ generation of nested container types
- Added C++ support for virtual source/sink
- Moved cmake CMAKE_CXX_STANDARD 11 option from options.yml to
CMakeLists.txt.mako
- Fixed qtqui_sink to allow multiple instances
- Revised virtual connection processing in cpp_top_block.py
Reviewed-By: HÃ¥kon Vagsether <hauk142@gmail.com>
-rw-r--r-- | gr-blocks/grc/blocks_socket_pdu.block.yml | 2 | ||||
-rw-r--r-- | gr-blocks/grc/blocks_wavfile_sink.block.yml | 6 | ||||
-rw-r--r-- | gr-blocks/grc/blocks_wavfile_source.block.yml | 3 | ||||
-rw-r--r-- | gr-qtgui/grc/qtgui_sink_x.block.yml | 28 | ||||
-rw-r--r-- | grc/blocks/message.domain.yml | 2 | ||||
-rw-r--r-- | grc/blocks/parameter.block.yml | 2 | ||||
-rw-r--r-- | grc/core/blocks/_build.py | 2 | ||||
-rw-r--r-- | grc/core/blocks/block.py | 130 | ||||
-rw-r--r-- | grc/core/blocks/virtual.py | 8 | ||||
-rw-r--r-- | grc/core/generator/FlowGraphProxy.py | 4 | ||||
-rw-r--r-- | grc/core/generator/cpp_templates/CMakeLists.txt.mako | 5 | ||||
-rw-r--r-- | grc/core/generator/cpp_templates/flow_graph.cpp.mako | 26 | ||||
-rw-r--r-- | grc/core/generator/cpp_templates/flow_graph.hpp.mako | 2 | ||||
-rw-r--r-- | grc/core/generator/cpp_top_block.py | 69 |
14 files changed, 219 insertions, 70 deletions
diff --git a/gr-blocks/grc/blocks_socket_pdu.block.yml b/gr-blocks/grc/blocks_socket_pdu.block.yml index 13d4d3b7a4..74108accb5 100644 --- a/gr-blocks/grc/blocks_socket_pdu.block.yml +++ b/gr-blocks/grc/blocks_socket_pdu.block.yml @@ -45,7 +45,7 @@ templates: cpp_templates: includes: ['#include <gnuradio/blocks/socket_pdu.h>'] declarations: 'blocks::socket_pdu::sptr ${id};' - make: 'this->${id} = blocks::socket_pdu::make(${type}, ${host}, ${port}, ${mtu}, ${tcp_no_delay});' + make: 'this->${id} = blocks::socket_pdu::make("${type}", ${host}, ${port}, ${mtu}, ${tcp_no_delay});' translations: 'True': 'true' 'False': 'false' diff --git a/gr-blocks/grc/blocks_wavfile_sink.block.yml b/gr-blocks/grc/blocks_wavfile_sink.block.yml index 20d956faaf..5139a17244 100644 --- a/gr-blocks/grc/blocks_wavfile_sink.block.yml +++ b/gr-blocks/grc/blocks_wavfile_sink.block.yml @@ -37,8 +37,10 @@ templates: cpp_templates: includes: ['#include <gnuradio/blocks/wavfile_sink.h>'] declarations: 'blocks::wavfile_sink::sptr ${id};' - make: 'this->${id} = blocks::wavfile_sink::make(${file}, ${nchan}, ${samp_rate}, ${bits_per_sample});' + make: |- + this->${id} = blocks::wavfile_sink::make(${file}${'.c_str()' if str(file)[0] != "'" and str(file)[0] != "\"" else ''}, ${nchan}, ${samp_rate}, ${bits_per_sample}); callbacks: - - open(${file}) + ## TODO Handle std::string type when const char* argument is needed + - this->${id}->open(${file}) file_format: 1 diff --git a/gr-blocks/grc/blocks_wavfile_source.block.yml b/gr-blocks/grc/blocks_wavfile_source.block.yml index 387bef3afb..04db7b87db 100644 --- a/gr-blocks/grc/blocks_wavfile_source.block.yml +++ b/gr-blocks/grc/blocks_wavfile_source.block.yml @@ -33,7 +33,8 @@ templates: cpp_templates: includes: ['#include <gnuradio/blocks/wavfile_source.h>'] declarations: 'blocks::wavfile_source::sptr ${id};' - make: 'this->${id} = blocks::wavfile_source::make(${file}, ${repeat});' + make: |- + this->${id} = blocks::wavfile_source::make(${file}${'.c_str()' if str(file)[0] != "'" and str(file)[0] != "\"" else ''}, ${repeat}); translations: 'True': 'true' 'False': 'false' diff --git a/gr-qtgui/grc/qtgui_sink_x.block.yml b/gr-qtgui/grc/qtgui_sink_x.block.yml index af3fefb3ca..0424769169 100644 --- a/gr-qtgui/grc/qtgui_sink_x.block.yml +++ b/gr-qtgui/grc/qtgui_sink_x.block.yml @@ -1,6 +1,6 @@ id: qtgui_sink_x label: QT GUI Sink -flags: [ python ] +flags: [ python, cpp ] parameters: - id: type @@ -129,6 +129,32 @@ templates: ${gui_hint() % win} + +cpp_templates: + includes: ['#include <gnuradio/qtgui/${type.fcn}.h>', '#include <gnuradio/filter/firdes.h>'] + declarations: gr::qtgui::${type.fcn}::sptr ${id}; + make: |- + ${id} = gr::qtgui::${type.fcn}::make( + ${fftsize}, //fftsize + gr::filter::${wintype.replace('.', '::win_type::')}, //wintype + ${fc}, //fc + ${bw}, //bw + ${name}, //name + ${plotfreq}, //plotfreq + ${plotwaterfall}, //plotwaterfall + ${plottime}, //plottime + ${plotconst} //plotconst + ); + ${id}->set_update_time(1.0/${rate}); + ${id}->enable_rf_freq(${showrf}); + ## Attempt to replicate logic of Python gui_hint() method + this->top_grid_layout->addWidget(${id + '->qwidget()' + (', ' + str(gui_hint)[1:-1] if len(gui_hint) > 2 else '')}); + + link: ['gnuradio-qtgui', 'Qt5Widgets', 'Qt5Core'] + translations: + 'True': 'true' + 'False': 'false' + documentation: |- The GUI hint can be used to position the widget within the application. The hint is of the form [tab_id@tab_index]: [row, col, row_span, col_span]. Both the tab specification and the grid position are optional. diff --git a/grc/blocks/message.domain.yml b/grc/blocks/message.domain.yml index 7e6cc529d9..d4b79d0200 100644 --- a/grc/blocks/message.domain.yml +++ b/grc/blocks/message.domain.yml @@ -8,3 +8,5 @@ multiple_connections_per_output: true templates: - type: [message, message] connect: self.msg_connect(${ make_port_sig(source) }, ${ make_port_sig(sink) }) + cpp_connect: hier_block2::msg_connect(${ make_port_sig(source) }, ${ make_port_sig(sink) }) + diff --git a/grc/blocks/parameter.block.yml b/grc/blocks/parameter.block.yml index f37ca1923f..d7fa926aac 100644 --- a/grc/blocks/parameter.block.yml +++ b/grc/blocks/parameter.block.yml @@ -39,7 +39,7 @@ templates: make: ${value} cpp_templates: - var_make: ${type.type} ${id} = ${id}; + var_make: ${id} = ${value}; make: ${value} documentation: |- diff --git a/grc/core/blocks/_build.py b/grc/core/blocks/_build.py index f8406f9d6b..151fa3274f 100644 --- a/grc/core/blocks/_build.py +++ b/grc/core/blocks/_build.py @@ -53,7 +53,7 @@ def build(id, label='', category='', flags='', documentation='', cpp_templates = cpp_templates or {} cls.cpp_templates = MakoTemplates( - includes=cpp_templates.get('includes', ''), + includes=cpp_templates.get('includes', []), make=cpp_templates.get('make', ''), callbacks=cpp_templates.get('callbacks', []), var_make=cpp_templates.get('var_make', ''), diff --git a/grc/core/blocks/block.py b/grc/core/blocks/block.py index e51b730cdd..fe16f710cc 100644 --- a/grc/core/blocks/block.py +++ b/grc/core/blocks/block.py @@ -77,7 +77,7 @@ class Block(Element): self.states = {'state': True, 'bus_source': False, 'bus_sink': False, 'bus_structure': None} - if 'cpp' in self.flags: + if 'cpp' in self.flags and not (self.is_virtual_source() or self.is_virtual_sink()): self.orig_cpp_templates = self.cpp_templates # The original template, in case we have to edit it when transpiling to C++ self.current_bus_structure = {'source': None, 'sink': None} @@ -400,7 +400,7 @@ class Block(Element): return [make_callback(c) for c in self.cpp_templates.render('callbacks')] - def decide_type(self): + def format_expr(self, py_type): """ Evaluate the value of the variable block and decide its type. @@ -410,60 +410,112 @@ class Block(Element): value = self.params['value'].value self.cpp_templates = copy.copy(self.orig_cpp_templates) - def get_type(element): + # Determine the lvalue type + def get_type(element, _vtype): + evaluated = None try: evaluated = ast.literal_eval(element) + if _vtype == None: + _vtype = type(evaluated) + except ValueError or SyntaxError as excp: + if _vtype == None: + print(excp) + + if _vtype in [int, float, bool, list, dict, str, complex]: + if _vtype == (int or long): + return 'int' + + if _vtype == float: + return 'double' + + if _vtype == bool: + return 'bool' + + if _vtype == complex: + return 'gr_complex' + + if _vtype == list: + try: + # For container types we must also determine the type of the template parameter(s) + return 'std::vector<' + get_type(str(evaluated[0]), type(evaluated[0])) + '>' + + except IndexError: # empty list + return 'std::vector<std::string>' + + if _vtype == dict: + try: + # For container types we must also determine the type of the template parameter(s) + key = list(evaluated)[0] + val = list(evaluated.values())[0] + return 'std::map<' + get_type(str(key), type(key)) + ', ' + get_type(str(val), type(val)) +'>' + + except IndexError: # empty dict + return 'std::map<std::string, std::string>' - except ValueError or SyntaxError: - if re.match(r'^(numpy|np|scipy|sp)\.pi$', value): - return 'pi' else: return 'std::string' - else: - _vtype = type(evaluated) - if _vtype in [int, float, bool, list]: - if _vtype == (int or long): - return 'int' - - if _vtype == float: - return 'double' + # Get the lvalue type + self.vtype = get_type(value, py_type) + + # The r-value for these types must be transformed to create legal C++ syntax. + if self.vtype in ['bool', 'gr_complex'] or 'std::map' in self.vtype or 'std::vector' in self.vtype: + evaluated = ast.literal_eval(value) + self.cpp_templates['var_make'] = self.cpp_templates['var_make'].replace('${value}', self.get_cpp_value(evaluated)) - if _vtype == bool: - return 'bool' + if 'string' in self.vtype: + self.cpp_templates['includes'].append('#include <string>') - if _vtype == list: - try: - first_element_type = type(evaluated[0]) - if first_element_type != str: - list_type = get_type(str(evaluated[0])) - else: - list_type = get_type(evaluated[0]) - except IndexError: # empty list - return 'std::vector<std::string>' + def get_cpp_value(self, pyval): - else: - return 'std::vector<' + list_type + '>' + if type(pyval) == int or type(pyval) == float: + val_str = str(pyval) - else: - return 'std::string' + # Check for PI and replace with C++ constant + pi_re = r'^(math|numpy|np|scipy|sp)\.pi$' + if re.match(pi_re, str(pyval)): + val_str = re.sub(pi_re, 'boost::math::constants::pi<double>()', val_str) + self.cpp_templates['includes'].append('#include <boost/math/constants/constants.hpp>') + + return str(pyval) - self.vtype = get_type(value) - if self.vtype == 'bool': - self.cpp_templates['var_make'] = self.cpp_templates['var_make'].replace('${value}', (value[0].lower() + value[1:])) + elif type(pyval) == bool: + return str(pyval)[0].lower() + str(pyval)[1:] - elif self.vtype == 'pi': - self.vtype = 'double' - self.cpp_templates['var_make'] = self.cpp_templates['var_make'].replace('${value}', 'boost::math::constants::pi<double>()') - self.cpp_templates['includes'].append('#include <boost/math/constants/constants.hpp>') + elif type(pyval) == complex: + self.cpp_templates['includes'].append('#include <gnuradio/gr_complex.h>') + evaluated = ast.literal_eval(str(pyval).strip()) + return '{' + str(evaluated.real) + ', ' + str(evaluated.imag) + '}' - elif 'std::vector' in self.vtype: + elif type(pyval) == list: self.cpp_templates['includes'].append('#include <vector>') - self.cpp_templates['var_make'] = self.cpp_templates['var_make'].replace('${value}', '{' + value[1:-1] + '}') + val_str = '{' + for element in pyval: + val_str += self.get_cpp_value(element) + ', ' - if 'string' in self.vtype: + if len(val_str) > 1: + # truncate to trim superfluous ', ' from the end + val_str = val_str[0:-2] + + return val_str + '}' + + elif type(pyval) == dict: + self.cpp_templates['includes'].append('#include <map>') + val_str = '{' + for key in pyval: + val_str += '{' + self.get_cpp_value(key) + ', ' + self.get_cpp_value(pyval[key]) + '}, ' + + if len(val_str) > 1: + # truncate to trim superfluous ', ' from the end + val_str = val_str[0:-2] + + return val_str + '}' + + if type(self.vtype) == str: self.cpp_templates['includes'].append('#include <string>') + return '"' + pyval + '"' + def is_virtual_sink(self): return self.key == 'virtual_sink' diff --git a/grc/core/blocks/virtual.py b/grc/core/blocks/virtual.py index a742419d27..598e8371d5 100644 --- a/grc/core/blocks/virtual.py +++ b/grc/core/blocks/virtual.py @@ -18,10 +18,12 @@ class VirtualSink(Block): key = 'virtual_sink' label = 'Virtual Sink' + flags = Block.flags + flags.set('cpp') parameters_data = build_params( params_raw=[dict(label='Stream ID', id='stream_id', dtype='stream_id')], - have_inputs=False, have_outputs=False, flags=Block.flags, block_id=key + have_inputs=False, have_outputs=False, flags=flags, block_id=key ) inputs_data = [dict(domain='stream', dtype='', direction='sink', id="0")] @@ -40,10 +42,12 @@ class VirtualSource(Block): key = 'virtual_source' label = 'Virtual Source' + flags = Block.flags + flags.set('cpp') parameters_data = build_params( params_raw=[dict(label='Stream ID', id='stream_id', dtype='stream_id')], - have_inputs=False, have_outputs=False, flags=Block.flags, block_id=key + have_inputs=False, have_outputs=False, flags=flags, block_id=key ) outputs_data = [dict(domain='stream', dtype='', direction='source', id="0")] diff --git a/grc/core/generator/FlowGraphProxy.py b/grc/core/generator/FlowGraphProxy.py index da95417b1b..0003cb5012 100644 --- a/grc/core/generator/FlowGraphProxy.py +++ b/grc/core/generator/FlowGraphProxy.py @@ -140,7 +140,7 @@ class FlowGraphProxy(object): # TODO: move this in a refactored Generator Returns: a list of #include statements """ - return [block.cpp_templates.render('includes') for block in self.iter_enabled_blocks()] + return [block.cpp_templates.render('includes') for block in self.iter_enabled_blocks() if not (block.is_virtual_sink() or block.is_virtual_source())] def links(self): """ @@ -149,7 +149,7 @@ class FlowGraphProxy(object): # TODO: move this in a refactored Generator Returns: a list of GNU Radio modules """ - return [block.cpp_templates.render('link') for block in self.iter_enabled_blocks()] + return [block.cpp_templates.render('link') for block in self.iter_enabled_blocks() if not (block.is_virtual_sink() or block.is_virtual_source())] def get_hier_block_io(flow_graph, direction, domain=None): """ diff --git a/grc/core/generator/cpp_templates/CMakeLists.txt.mako b/grc/core/generator/cpp_templates/CMakeLists.txt.mako index 1b212e7bcc..a1f1b3f822 100644 --- a/grc/core/generator/cpp_templates/CMakeLists.txt.mako +++ b/grc/core/generator/cpp_templates/CMakeLists.txt.mako @@ -16,6 +16,7 @@ class_name = flow_graph.get_option('id') %>\ cmake_minimum_required(VERSION 3.8) +set(CMAKE_CXX_STANDARD 11) % if generate_options == 'qt_gui': find_package(Qt5Widgets REQUIRED) @@ -25,14 +26,14 @@ include_directories( ${'$'}{GNURADIO_ALL_INCLUDE_DIRS} ${'$'}{Boost_INCLUDE_DIRS} % if generate_options == 'qt_gui': - ${'$'}{Qt5Widgets_INCLUDES} + ${'$'}{Qt5Widgets_INCLUDE_DIRS} % endif $ENV{HOME}/.grc_gnuradio ) % if generate_options == 'qt_gui': add_definitions(${'$'}{Qt5Widgets_DEFINITIONS}) - +set(CMAKE_CXX_FLAGS "${'$'}{CMAKE_CXX_FLAGS} -fPIC") set(CMAKE_AUTOMOC TRUE) % endif diff --git a/grc/core/generator/cpp_templates/flow_graph.cpp.mako b/grc/core/generator/cpp_templates/flow_graph.cpp.mako index 208ec97bbe..7dabf7b0ed 100644 --- a/grc/core/generator/cpp_templates/flow_graph.cpp.mako +++ b/grc/core/generator/cpp_templates/flow_graph.cpp.mako @@ -13,6 +13,10 @@ GNU Radio version: ${version} ********************/ #include "${flow_graph.get_option('id')}.hpp" +% if flow_graph.get_option('realtime_scheduling'): +#include <gnuradio/realtime.h> +% endif + % if parameters: namespace po = boost::program_options; @@ -25,12 +29,18 @@ class_name = flow_graph.get_option('id') + ('_' if flow_graph.get_option('id') = param_str = ", ".join((param.vtype + " " + param.name) for param in parameters) param_str_without_types = ", ".join(param.name for param in parameters) +initializer_str = ",\n ".join((param.name + "(" + param.name + ")") for param in parameters) + +if generate_options == 'qt_gui': + initializer_str = 'QWidget()' + (',\n ' if len(parameters) > 0 else '') + initializer_str + +if len(initializer_str) > 0: + initializer_str = '\n: ' + initializer_str %>\ -% if generate_options == 'no_gui': -${class_name}::${class_name} (${param_str}) { -% elif generate_options == 'qt_gui': -${class_name}::${class_name} (${param_str}) : QWidget() { +${class_name}::${class_name} (${param_str}) ${initializer_str} { + +% if generate_options == 'qt_gui': this->setWindowTitle("${title}"); // check_set_qss // set icon @@ -47,17 +57,13 @@ ${class_name}::${class_name} (${param_str}) : QWidget() { this->top_layout->addLayout(this->top_grid_layout); this->settings = new QSettings("GNU Radio", "${class_name}"); - % endif % if flow_graph.get_option('thread_safe_setters'): ## self._lock = threading.RLock() % endif - - this->tb = gr::make_top_block("${title}"); - % if blocks: // Blocks: % for blk, blk_make, declarations in blocks: @@ -113,7 +119,7 @@ void ${class_name}::set_${var.name} (${var.vtype} ${var.name}) { int main (int argc, char **argv) { % if parameters: % for parameter in parameters: - ${parameter.vtype} ${parameter.name}; + ${parameter.vtype} ${parameter.name} = ${parameter.cpp_templates.render('make')}; % endfor po::options_description desc("Options"); @@ -140,7 +146,7 @@ int main (int argc, char **argv) { % endif % if generate_options == 'no_gui': - ${class_name}* top_block = new ${class_name}(); + ${class_name}* top_block = new ${class_name}(${param_str_without_types}); ## TODO: params % if flow_graph.get_option('run_options') == 'prompt': top_block->tb->start(); diff --git a/grc/core/generator/cpp_templates/flow_graph.hpp.mako b/grc/core/generator/cpp_templates/flow_graph.hpp.mako index b9f3ce60be..b5b9d51b00 100644 --- a/grc/core/generator/cpp_templates/flow_graph.hpp.mako +++ b/grc/core/generator/cpp_templates/flow_graph.hpp.mako @@ -77,7 +77,7 @@ ${indent(declarations)} % if parameters: // Parameters: % for param in parameters: - ${param.get_cpp_var_make()} + ${param.vtype} ${param.cpp_templates.render('var_make')} % endfor % endif diff --git a/grc/core/generator/cpp_top_block.py b/grc/core/generator/cpp_top_block.py index c24e855d48..9b5a8ecc1b 100644 --- a/grc/core/generator/cpp_top_block.py +++ b/grc/core/generator/cpp_top_block.py @@ -5,6 +5,7 @@ import os import tempfile import textwrap import re +import ast from mako.template import Template @@ -232,7 +233,7 @@ class CppTopBlockGenerator(TopBlockGenerator): blocks = [ b for b in fg.blocks - if b.enabled and not (b.get_bypassed() or b.is_import or b in parameters or b.key == 'options') + if b.enabled and not (b.get_bypassed() or b.is_import or b in parameters or b.key == 'options' or b.is_virtual_source() or b.is_virtual_sink()) ] blocks = expr_utils.sort_objects(blocks, operator.attrgetter('name'), _get_block_sort_text) @@ -263,17 +264,61 @@ class CppTopBlockGenerator(TopBlockGenerator): fg = self._flow_graph variables = fg.get_cpp_variables() + type_translation = {'complex': 'gr_complex', 'real': 'double', 'float': 'float', 'int': 'int', 'complex_vector': 'std::vector<gr_complex>', 'real_vector': 'std::vector<double>', 'float_vector': 'std::vector<float>', 'int_vector': 'std::vector<int>', 'string': 'std::string', 'bool': 'bool'} + # If the type is explcitly specified, translate to the corresponding C++ type + for var in list(variables): + if var.params['value'].dtype != 'raw': + var.vtype = type_translation[var.params['value'].dtype] + variables.remove(var) + + # If the type is 'raw', we'll need to evaluate the variable to infer the type. + # Create an executable fragment of code containing all 'raw' variables in + # order to infer the lvalue types. + # + # Note that this differs from using ast.literal_eval() as literal_eval evaluates one + # variable at a time. The code fragment below evaluates all varaibles together which + # allows the variables to reference each other (i.e. a = b * c). + prog = 'def get_decl_types():\n' + prog += '\tvar_types = {}\n' for var in variables: - var.decide_type() + prog += '\t' + str(var.params['id'].value) + '=' + str(var.params['value'].value) + '\n' + prog += '\tvar_types = {}\n'; + for var in variables: + prog += '\tvar_types[\'' + str(var.params['id'].value) + '\'] = type(' + str(var.params['id'].value) + ')\n' + prog += '\treturn var_types' + + # Execute the code fragment in a separate namespace and retrieve the lvalue types + var_types = {} + namespace = {} + try: + exec(prog, namespace) + var_types = namespace['get_decl_types']() + except Exception as excp: + print('Failed to get parameter lvalue types: %s' %(excp)) + + # Format the rvalue of each variable expression + for var in variables: + var.format_expr(var_types[str(var.params['id'].value)]) def _parameter_types(self): fg = self._flow_graph parameters = fg.get_parameters() for param in parameters: - type_translation = {'eng_float' : 'double', 'intx' : 'int', 'std' : 'std::string'}; + type_translation = {'eng_float' : 'double', 'intx' : 'int', 'str' : 'std::string', 'complex': 'gr_complex'}; param.vtype = type_translation[param.params['type'].value] + if param.vtype == 'gr_complex': + evaluated = ast.literal_eval(param.params['value'].value.strip()) + cpp_cmplx = '{' + str(evaluated.real) + ', ' + str(evaluated.imag) + '}' + + # Update the 'var_make' entry in the cpp_templates dictionary + d = param.cpp_templates + cpp_expr = d['var_make'].replace('${value}', cpp_cmplx) + d.update({'var_make':cpp_expr}) + param.cpp_templates = d + + def _callbacks(self): fg = self._flow_graph variables = fg.get_cpp_variables() @@ -286,7 +331,8 @@ class CppTopBlockGenerator(TopBlockGenerator): callbacks_all = [] for block in fg.iter_enabled_blocks(): - callbacks_all.extend(expr_utils.expr_replace(cb, replace_dict) for cb in block.get_cpp_callbacks()) + if not (block.is_virtual_sink() or block.is_virtual_source()): + callbacks_all.extend(expr_utils.expr_replace(cb, replace_dict) for cb in block.get_cpp_callbacks()) # Map var id to callbacks def uses_var_id(callback): @@ -313,7 +359,13 @@ class CppTopBlockGenerator(TopBlockGenerator): key = port.key if not key.isdigit(): - key = re.findall(r'\d+', key)[0] + # TODO What use case is this supporting? + toks = re.findall(r'\d+', key) + if len(toks) > 0: + key = toks[0] + else: + # Assume key is a string + key = '"' + key + '"' return '{block}, {key}'.format(block=block, key=key) @@ -321,12 +373,15 @@ class CppTopBlockGenerator(TopBlockGenerator): # Get the virtual blocks and resolve their connections connection_factory = fg.parent_platform.Connection - virtual = [c for c in connections if isinstance(c.source_block, blocks.VirtualSource)] - for connection in virtual: + virtual_source_connections = [c for c in connections if isinstance(c.source_block, blocks.VirtualSource)] + for connection in virtual_source_connections: sink = connection.sink_port for source in connection.source_port.resolve_virtual_source(): resolved = connection_factory(fg.orignal_flowgraph, source, sink) connections.append(resolved) + + virtual_connections = [c for c in connections if (isinstance(c.source_block, blocks.VirtualSource) or isinstance(c.sink_block, blocks.VirtualSink))] + for connection in virtual_connections: # Remove the virtual connection connections.remove(connection) |