Migrating to Coco Standard 1.2

The Coco Language standard 1.2 includes several features that have required backwards-incompatible changes to be made to the language and also the generated code. These will require users to manually alter their code in order to use their existing models with the new language standard.

Coco

With standard = 1.2 there are several names that are now keywords:

  • monitor;
  • trace.

Further, there are several names that cannot be used as the name of a member of a port:

  • ambiguous
  • drainQueue
  • end
  • start
  • send
  • timer
  • unused

When upgrading, conflicting declarations will need to be renamed. It will be easiest to do this before setting standard = 1.2, as that way Coco’s semantically-aware renaming can be used. One potential workflow would be to briefly set standard = 1.2 and make a note of all files that have errors in them. Then switch back to standard = 1.1 and modify all previously noted errors.

With standard = 1.2, the default value of logValues has changed from Never to WhenAvailable. This means that values will be logged by default. This has several implications:

  • The generated logging data will have a different format, so if you have tools that read the log files these may need to be modified.
  • The generated code will require the runtime logger to support logging values. The loggers that are shipped as part of the Coco runtime will support this out-of-the box (e.g. for C++, :icpp:`coco::StreamMachineLogger`), but if you are using a custom logger implementation you may need to upgrade this to support logging values, or alternatively you can set logValues to Never.

Generated C

The generated C code includes one small change when generator.c.style is set to ObjectOrientated: there is now an explicit destroy function that must be invoked to cleanup any memory allocated by the component.

For example, typically the Coco generated C code will be used as follows:

System component;
System_create(&component);
System_coco_start(&component);

// use component

With standard = 1.2, once the component has been terminated and is no longer required, the corresponding destroy method should be used to cleanup all allocated memory:

System_destroy(&component);

Generated C++

The generated C++ code now uses a slightly different method to generate C++ code for tagged enums (i.e. enums with data). This change was necessary to support using C++’s partial template specialisation feature on tagged enums.

This change only affects users who manually interacted with Coco’s tagged enums from C++, which is only likely if there are external functions or external components in Coco that have parameters that are of tagged enum types. This is not that common.

Previously, the generated C++ code for a tagged enum might look like the following:

struct MyCocoEnum {
  struct EnumCase1_ {
    bool x;
  };
  struct EnumCase2_ {
    bool x;
  };
  using type = coco::variant<EnumCase1_, EnumCase2_>;

  type EnumCase1() { … }
  type EnumCase2() { … }
};

With standard = 1.2, this will be modified so that the MyCocoEnum type is the referred-to type. This is done by adding a member to MyCocoEnum that stores the variant:

struct MyCocoEnum {
  struct EnumCase1_ {
    bool x;
  };
  struct EnumCase2_ {
    bool x;
  };
  using type = coco::variant<EnumCase1_, EnumCase2_>;

  MyCocoEnum EnumCase1() { … }
  MyCocoEnum EnumCase2() { … }

  type storage;
};

Further, any member functions of MyCocoEnum declared in Coco will be member functions on the new MyCocoEnum C++ struct.

The following need to be made to hand-written user code (note that C++ code generated by Coco will be automatically converted - these changes only apply to hand-written code):

  • Any C++ type that refers to MyCocoEnum::type will need to be changed to just MyCocoEnum.
  • Any uses of coco::get will need to be modified to use the inner storage member. For example, coco::get<T>(x) should be modified to coco::get<T>(x.storage).
  • Any uses of the member function index will need to be modified to use the inner storage member. For example, x.index() should be modified to x.storage.index().
  • Any uses of user-defined member functions on the Coco-defined enum will need to be modified to be member function calls instead. For example someFn(x) would become x.someFn().