General structure

An Active Code Template definition consists of a python or java file found in a variant. It generally follows the text that needs to be generated, involving the navigation through the UML model, extraction of the information from it into the result text, and so on. Active code templates can refer to each other, to allow a better separation of concerns.

Let’s take the "AttributeGen.py" file as an example to show how a python ACT works. It is part of the standard variant, found in Cxx Designer’s resources.

The start of an ACT

Every ACT file starts with a comment zone like this:

#
# Template AttributeGen
# Binded variables
#   CXX      facilities for Cxx specific production and navigation
#   GEN      facilities for code production
#   MDL      facilities for model navigation and condition testing
#   ENG      ACT ENG (used to call another template))
#
##############################################################################
##

# Local utilities functions
#    naming rules:
#       - if the utility function directly prints out the generated code it must be named printXXXXX
#         where XXXX is expected to summarize the function role
#       - if the utility function returns a piece of generated code as a

string it must be named makeXXXXXX
#         where XXXX is expected to summarize the function role
#       - if the utility function is used to get some elements of the model and return a list of model elements (navigation convinience)
#         it must be named modelGetXXXXX where XXXX is expected to summarize the function role
#    other rules:
#       - any utility function takes a model element as first parameter (name it el)
#       - utility function must not define global variables of their own
#

This comment indicates: * the current template’s name, AttributeGen * the available global variables for all python files, corresponding to service classes from Cxx Designer, CXX, GEN, MDL and ENG * several rules about how functions in an ACT are created

The python module’s declaration then begins:

from com.modeliosoft.modelio.cxxdesigner.engine.act import IAct
from com.modeliosoft.modelio.api.model import *
from com.modeliosoft.modelio.api.mda.model import ObUtils
from java.util import ArrayList
import act

INDENT = _"    "_             # four white spaces

class **AttributeGen** (IAct):

A python ACT file must implement the com.modeliosoft.modelio.cxxdesigner.engine.act.IAct interface to become available in the engine. It defines only the main entry point of the ACT, which will be described below.

The run function

When evaluating an ACT file, the Cxx Designer’s engine always launches run:

##############################################################################
#
# Generation code
#

  def run(_self_, ctx, attribute):
    out = ctx.getOutputs()[0]
    self.printAttributeDeclaration(out, attribute)

Three parameters are given to this function:

  • self is the python module, AttributeGen in this example. All local utility functions are called using self, rather than using global ones.

  • ctx is the execution context, containing the output (i.e. the files being written).

  • attribute is the model element being generated.

The first thing to do in the run function is usually to extract the current output from the execution context.

We then have to analyse the given model element to determine which code must be generated. In this example, it is done in a separate utility function.

Utility functions

def **makeMemberDeclaration**(_self_, att):
    # standard case
    # declaration syntax is build as:
    #  decl          = $specifiers $decoratedtype $namespacedname$bindings $init;
    #  decoratedtype = $containerpointers $container($type)
    #  type          = $basetype $pointers
    #
    # example:
    #  static std::vector<int*> C1::att;

    # compute declaration
    decoratedtype=GEN.makeHxxSignature(att)

    # compute bindings
    bindings=""
    if (ObUtils.isTagged(att, "Cxx.Bind")):
      bindings = "<"
      for p in ObUtils.getTagValues(att, "Cxx.Bind"):
        bindings = bindings + p + ", "
      bindings = bindings.rstrip(", ")
      bindings = bindings + ">"

    # final assembly
    decl = decoratedtype + bindings

    return decl

  def printAttributeDeclaration(self, out, el):
    if act.hasDocumentation(el):
      out.println(act.makeDocumentationComment(el))
    out.print(INDENT + _self_.makeMemberDeclaration(el) + ";")

Utility functions are the core of the generation process, as they contain the code generation itself.

The naming rule quoted earlier is applied on those two functions; the first one creates a string from an attribute, whereas the second writes content in the output.

When customizing Cxx generation, you only have to modify those functions and include the behaviour you need.

Calling another ACT

Active code templates are often divided into several sub-templates, to allow a better understanding and separation of concerns. The ACT engine, given as a global variable (ENG), allows another template to be run and its result obtained. Let’s take a part of another python file as an example,

ClassGen:

  def printBodyContent(self, out, el):
    out.println()
    self.printUseIncludes(out, el)
    out.println (_"//class header file"_)
    out.printf("#include \"%s\"\n", [CXX.makeHeaderFilename(el)])
    out.println()
    self.printBodyAutoIncludes(out, el)
    self.printIncludeNotes(out, el)
    self.printBodyTopNotes(out, el)
    self.printOpenNamespace(out, el)
    out.println(ENG.evalAct("ClassDefinitionCxx", el))
    self.printCloseNamespace(out, el)
    self.printBodyBottomNotes(out, el)

The printBodyContent method is the main part of the body file generation. Includes and notes are produced by several utility functions in ClassGen, but the core of the class content is generated by the ClassDefinitionCxx ACT.

The ENG.evalAct method executes this ACT on the given element, and returns its result (i.e. everything that had been printed in the output), which is itself printed in the current body file.