You are here: Start » Technical Issues » C++ Code Generator

C++ Code Generator

Introduction

A program accepted by Aurora Vision Studio and Aurora Vision Executor is executed in a virtual machine environment. Such a machine consecutively executes (with preservation of proper rules) filters from the source program. The C++ Code Generator allows to automatically create a C++ program, which is a logical equivalent of the job performed by the virtual machine of Aurora Vision Studio executor. As part of such programs, consecutive filter calls are changed to calls of functions from Aurora Vision Library.

To generate, compile and run programs in this way it is necessary to own also a Aurora Vision Library license.

User interface

The C++ Code Generator functionality is available in the Main Menu in File » Generate C++ Code.... After choosing this command, a window with additional generator options and parameters (divided into tabs) will be opened.

In the Output tab basic generator parameters can be set:

  • Program name - it determines the name of the generated C++ program (it doesn't have to be related to Aurora Vision Studio project name). It is a base name for creating names of source files and (after choosing a proper option) it is also the name of the newly created project.
  • Output directory - it determines the folder in which a generated program and project files will be saved. Such folder has to exist before generation is started. The path can be either absolute or relative (when an Aurora Vision Studio project is already saved) starting from the project directory. All program and project files will be saved in this folder with no additional subfolders. Before overwriting any files in the output directory, a warning message with a list of conflicted files will be displayed.
  • Code namespace - an optional namespace, in which the whole generated C++ code will be contained. A namespace can be nested many times using the "::" symbol as a separator (e.g. "MyNamespace" or "MyProject::MyLibrary::MyNamespace"). Leaving the code namespace field empty will result in generating code to the global namespace.
  • Create sample Microsoft Visual Studio solution - enabling this option will result in generating a new sample tentatively configured solution for compiling generated code in the Microsoft Visual Studio environment. Each time when this option is enabled and code is generated (e.g. when generating code again after making some changes in an Aurora Vision project), a new solution will be created and any potential changes made in an already existing solution with the same path will be overwritten.
    This option is enabled by default when generating code for the first time for the selected folder. It is disabled by default when code is generated again (updated) in the selected output folder.
    Details regarding Microsoft Visual Studio solution configuration required by generated code are described in this document below.

In the Modules tab there is a list of project modules which can participate in code generation. It is possible to choose any subset of modules existing in an Aurora Vision Studio project, as long as this doesn't cause breaking the dependencies existing in them. Selecting a subset of modules will cause that only macrofilters and global parameters present in the selected modules will be generated to C++ code. If disabling a module causes breaking dependencies (e.g. a macrofilter being generated refers to an other macrofilter which exists in a module which is disabled in code generation), it will be reported with an error during code generation.

In the Options tab there are additional and advanced options modifying the behavior of the C++ Code Generator:

  • Include instances in diagnostic mode - diagnostic instances (instances processing data from diagnostic outputs of preceding filter instances) are meant to be used to analyze program execution during program development, that's why they aren't included by default in generated programs. In order to include such instances in a generated program this option has to be enabled (Note: in order to make diagnostic outputs of functions work correctly, enabling diagnostic mode in Aurora Vision Library has to be taken into account in a C++ program).
  • Generate macrofilter inputs range checks - the virtual machine controls data on macrofilter inputs in terms of assigned to them allowed ranges. Generated code reproduces this behavior by default. If such control is not necessary in a final product, it is possible to uncheck this option to remove it from a C++ program.
  • Generate error handlers - when checked the error handling Task macrofilter variants will be generated into the C++ code in a form of try...catch blocks.
  • Enable function block merging - language constructions of conditional blocks and loops, which map virtual machine functions in terms of conditional and array filter execution, are placed in generated code. The C++ Code Generator uses optimization which places, where possible, a couple of filter calls in common blocks (merging their function blocks). Unchecking this option will disable such optimization. This functionality is meant to help solve problems and usually should be kept enabled.
  • Enable filters reordering - allows the C++ code generator to change the order of execution of filters when performing other optimizations. The order of execution will only be changed when it will not affect the program results (e.g. the order of I/O operations will be maintained).
  • Maintain objects with dynamic resources across iterations - causes that the intermediate variables with dynamically allocated memory (like Image, Region or arrays) will be placed outside of the Task iteration loops, maintaining the lifetime of those objects across program iterations. For Step macrofilter functions those variables will be placed in their state object. This function can increase the performance of the program by reducing the memory allocations but might increase the program memory footprint.
  • Keep dynamic resources outside Task macrofilter functions - when set objects with dynamic resources will be kept in a separate state-like objects and their lifetime will be preserved even outside of Task macrofilters. Task macrofilter functions might receive additional state-like object argument named "cache". Step macrofilter functions might receive two state and state-like object arguments. Task macrofilters marked as "Optimize memory" will keep their resources internally.

The Generate button at the bottom of the window starts code generation according to the chosen configuration (in case of a need to overwrite existing files an additional window to confirm such action will be displayed) and when the generation operation is completed successfully it closes the window and saves the parameters. The Close button closes the window and saves the chosen configuration. The Cancel button closes the window and ignores the changes made to the configuration.

All parameters and generation options are saved together with an Aurora Vision Studio project. At the next code generation (when the configuration window is opened again), the parameters chosen previously for the project will be restored.

In order to keep names between Aurora Vision Studio project files and C++ program files synchronized, it is advised to save a project each time before generating C++ code.

Generated Code Organization

A program is generated to C++ code preserving program's split into modules. For each program module, chosen for generation, there are two files (.cpp and .h) created. For the main module (or when there is only one module) these files have names equal to the program name (with appropriate extensions) which was chosen in generation options. For other modules file names are created according to the template program-name.module-name.cpp/h.

Macrofilters contained in modules are generated in form of C++ functions. Each public macrofilter, or a private macrofilter used by an other macrofilter contained in generated code, is included in a .cpp file as a function definition. For each public macrofilter its declaration is included also in an .h file. Analogical rules are applied to global parameters. Macrofilter visibility level (public/private) can be set in its properties.

Other project elements, e.g. an HMI structure, don't have their correspondents in generated code.

Generated function interface corresponds to a set of macrofilter inputs and outputs. In the first place inputs are consecutively mapped to function arguments (sometimes using constant reference type), next arguments of reference types from outputs (function output arguments) are also mapped, through them a function will assign results to objects passed to a function from the outside. E.g. for a macrofilter containing two inputs (inValue1, inValue2) and one output (outResult) a function interface may look like this:

void MyMacro( int inValue1, int inValue2, int& outResult )

When a macrofilter contains facilities requiring preserving their states in consecutive macrofilter iterations (e.g. a Step macrofilter containing registers, loop generators or accumulators), a state argument will be added to the function interface on the first position:

bool MyMacro( MyMacroState& state, int inValue1, int inValue2, int& outResult )

Data types of inputs, outputs and connections will be mapped in code to types, structures and constructions based on the templates (e.g. atl::Array<> or atl::Conditional<>) coming from Aurora Vision Library. Filter calls will be mapped to function calls coming also from Aurora Vision Library. In order to get an access to proper implementations and declarations, there are Aurora Vision Library headers included in generated code. Such includes can appear as well in .cpp files, as in .h files (because references to Aurora Vision Library types appear also in function signatures generated from macrofilters).

In generated program there are also static constants, including constant compound objects, which require initial initialization or loading from a file (e.g. if in the IDE there is a hand-edited region on a filter input, then such region will be saved in an .avdata file together with generated code). A program requires preparing constants before using any of its elements. In order to do this, in the main module in generated code an additional function Init with no arguments is added, as part of this function compound constants are prepared. This function has to be called before using any element from generated code (also before constructing a state object).

Generated Code Usage

The basis of generated code are file pairs emerging from modules with the structure described above. These modules can be used as part of a vision program, e.g. as a set of functions implementing the algorithms designed in the Aurora Vision Studio environment.

Before calling any generated function, constructing a function state object or accessing a global parameter, it is necessary to call the Init() function added to the main module code. The Init function must be called once at the beginning of a program. This function is added to generated code even when a program does not contain any compound constants which require initialization, in order not to have to modify the schema of generated code after modifying and updating the program.

Stateful functions

As described above, sometimes a function may have an additional first argument named state, passed by reference. Such object preserves a function state between consecutive iterations (each call of such function is considered as an iteration). In such objects there are, among others, kept generator positions (e.g. the current position of filters of Enumerate* type), register states of Step macrofilters or internal filter data (e.g. a state of a connection with a device in filters which acquire images from cameras).

A state object type is a simple class with a parameterless constructor (which initializes the state for the first iteration) and with a destructor which frees state resources. It is the task of applications using functions with a state to prepare a state object and to pass it through a parameter to a function. An application may construct a state object only after calling the Init() function. An application should not modify such object by itself. One instance of a state object is intended for a function call as part of one and the same task (it is an equivalent of a single macrofilter instance in an Aurora Vision Studio program). Lifetime of a single object should be sustained for the time when such task is performed (e.g. you should not construct a new state object to perform the same operation on consecutive video frames). A single state object cannot be shared among different tasks (e.g. if as part of one iteration the same function is called twice, then both calls should use different instances of a state object).

State object type name is generated dynamically and it can be found in the declaration of a generated function. The C++ Code Generator, if possible, creates names of state types according to the template Macrofilter-nameState.

Freeing state resources is performed in a state class destructor. If a function established connections with external devices, then a state object destructor is used also to close them. The recommended way of state handling is using a local variable on the stack, in such way that a destructor is called automatically after stepping out of a program block.

Error handling

The functions of Aurora Vision Library and generated code report the same errors which can be observed in the Aurora Vision Studio environment. On the C++ code level error reporting is performed by throwing an exception. To throw an exception, an object of type derived from the atl::Error class is used. Methods of this class can be used to get the description of reported problems.

Note: the Init() function can also throw an exception (e.g. when an external .avdata file containing a static constant could not be loaded).

Global parameters

Simple global parameters (only read by the vision application) are generated as global variables in the C++ program (with the same name as the global parameter). It is possible to modify those variables from the user code in order to change the application configuration, however such modification is not thread safe.

When thread safe modification of the global parameters is needed, global parameters should only be accessed with WriteParameter and ReadParameter filter blocks in the vision application. In order to allow access from user code to such operations, appropriate read/write should be encapsulated in a public macrofilter participating in the code generation. This will give access to the parameters from the user code in form of a function call.

Compiling a program with generated code

Generated C++ code is essentially a project basing on Aurora Vision Library and has to follow its guidelines. Compilation requires this product to be installed in order to get the access to proper headers and .lib files. Aurora Vision Library requires that a project is compiled in the Microsoft Visual Studio environment.

A program being compiled requires the following settings:

  • The header file search path is set to the include folder of the Aurora Vision Library package.
  • The search paths of .lib files for respective platforms are set to the corresponding subfolders in the lib directory of the Aurora Vision Library package (e.g. the Release|Win32 configuration compiled in the Microsoft Visual Studio package has to use the libraries from the lib\Win32\ subfolder).
  • The AVL.lib library is linked to a project (in order to link a dynamic library - AVL.dll).

Running a compiled program

A program created basing on generated code and the Aurora Vision Library product requires the following components to run:

  • All .avdata files generated with C++ code (if any were generated at all), located in the current application folder during the Init() function call.
  • If in the Aurora Vision Studio project (from which code is generated) there are any filters reading external data (e.g LoadImage) or enclosures of data files to filter inputs, then all such files have to be available during program execution. The files will be searched according to the defined path, in case of a relative path the search will begin starting from the current application folder.
  • A redistributable package of Visual C++ (from the version of Microsoft Visual Studio used to compile the program and Aurora Vision Library) installed in the destination system.
  • The AVL.dll file ready to work with the used platform. This module can be found in a subfolder of the bin directory of the Aurora Vision Library package (analogically to the use of the search paths of .lib files).
  • If the generated code uses third party packages (e.g. cameras, I/O cards, serial ports), then additional .dll files (intermediating in the access to the drivers of such devices) are required. Such files are identified with names ending with the "_Kit" suffix, they can be found in the same directory as the used AVL.dll file (also in this case it is necessary that this file is compatible with given platform). In case when a required file is missing, the program being executed will report an error pointing the name of the missing module in a message. Such libraries are loaded dynamically during program execution. In such case, for correct execution it is also necessary to install proper redistributable packages or SDKs for the used devices.
  • A valid license for Aurora Vision Library activated in the destination system.

Generated sample Microsoft Visual Studio solution

After choosing a proper option in the Output tab, the C++ Code Generator, apart from modules' code, will also create a sample tentatively configured Microsoft Visual Studio project together with sample user code using generated code. The configuration of such project follows the compilation requirements described above, including attaching headers and .lib files from the Aurora Vision Library package (it uses the AVL_PATHXX environment path created by the installer of Aurora Vision Library). This means that to compile a sample project, a properly installed package of Aurora Vision Library is required.

To make the generated project's structure clear and distinguish its elements, there are two filters (solution directories) created in such project:

  • Generated Code - here are located files deriving from generation of project modules. Such elements are not supposed to be further manually modified by the user, because in case of project update and code re-generation such modifications will be overwritten.
  • User Code - here are located files from the sample user application. It's assumed that such code should be created by the user and implement a final application which uses generated code.

Project configuration covers:

  • Including paths to the headers of the Aurora Vision Library package.
  • Linking with the AVL.lib library.
  • Copying the AVL.dll file to the destination folder.

The configuration does not cover providing proper _Kit files (if they are required).

As sample user code there is one simple "main.cpp" file created, it performs the following actions:

  • Calling the Init function.
  • Activating the diagnostic mode of Aurora Vision Library (if in project options usage of diagnostic instances is enabled for code generation).
  • Calling the Main macrofilter's function (if it took part in code generation during creation of the main.cpp file).
  • Catching all exceptions thrown by the program and Aurora Vision Library and reporting error messages on the standard output before closing the program.

A project and sample user code are generated once. It means that neither project configuration nor user code which calls generated code will be updated during project update (code re-generation). They can be only completely overwritten in case of choosing again the option to create a sample Microsoft Visual Studio solution. A project and sample code are created basing on names and options from the code generation configuration in Aurora Vision Studio.

In case of developing an application using generated code, the duty to adjust a project to changes in generated code is assigned to the user. To such tasks belong among others:

  • Adding to the project and removing from it files with code of generated modules and adjusting #include directives in case of modifying modules in an Aurora Vision Studio project.
  • Adjusting the code which calls generated code in case of modifications of interfaces of public macrofilters.
  • Providing proper _Kit files in the scope of the resulting program.

Please note that projects generated from Aurora Vision Studio up to version 3.2 and thus prepared for Aurora Vision Library up to version 3.2 required linking with additional library named "AvCodeGenRuntime.lib". This library is not required anymore and can be removed from project configuration when updating to newer version of Aurora Vision Library.

Previous: Project Files Next: .NET Macrofilter Interface Generator