RTSC Module Primer/Lesson 8

From RTSC-Pedia

Jump to: navigation, search
revision tip
—— LANDSCAPE orientation
[printable version]  offline version generated on 18-Aug-2017 00:08 UTC  

RTSC Module Primer/Lesson 8

Instance modules — implementing bravo.math.RandGen

Unlike the other modules we've just examined, the RandGen module supports creation and deletion of instances objects which here represent independently parameterized sequences of random-numbers. First used within client programs back in Lesson 3 and Lesson 4, we'll now revisit RandGen from the perspective of its supplier—all while extending some of the programming idioms already encountered in the last two lessons to now handle instance objects throughout their life-cycle.

You might find it helpful to first review the client programming idioms used with any RTSC instance module by quickly glancing at the prog.c and prog.cfg examples in Lesson 3 and Lesson 4.


Specifying the module

Like the Bench module of Lesson 7, the RandGen.xdc spec uses the internal keyword to delineate private declarations supporting the module's implementation. But unlike Bench, RandGen.xdc also employs the new XDCspec instance keyword to further demarcate (client-visible) public declarations that only pertain to instance objects.

/*! Collection of math modules */
package bravo.math {
    module RandGen;



/*! Random-number sequence generator */
module RandGen {
    /*! Largest possible number in a sequence */
    const Int16 MAXVALUE = (1U << 15) - 1;
    /*! Numbers between (0, `range`] */
    config Int16 range = MAXVALUE;
    /*! Random-number seed value */
    config UInt seed = 1;
    /*! Generate the next number in this sequence */
    Int16 next();
    /*! Generate the next `count` numbers in this sequence
     *  @param(buffer) output buffer
     *  @param(count) number of numbers
    Void nextn(Int16 *buffer, Int count);
    struct Instance_State {
        Int16 range;      /* range parameter value */
        ULong next;       /* next value in sequence */

Recalling our note about RTSC standard types back in Lesson 3, the type Int16 implies at least 16-bits of range and may in fact be larger on certain targets. While we strongly recommend use of RTSC standard types like Void and ULong, XDCspec also recognizes legacy C types such as void and unsigned long.

Working down the page, the XDCspec constant declaration at line 1 of RandGen.xdc—familiar syntax to any C programmer—applies module-wide rather than on a per-instance basis. In object-oriented languages such as C++ or Java, you typically distinguish such declarations through use of the static keyword; but because of the inherently object-disoriented nature of C—the implementation language of choice amongst embedded programmers—XDCspec has chosen the opposite approach by instead introducing its own instance keyword as a marker.

In general, you'll find that all per-instance declarations—those following the instance keyword— feel somewhat "incomplete" when seen against typical usage of RandGen configs and functions in the prog.c target-programs of Lesson 3 or Lesson 4:

  • the special create declaration at line 2 lacks any type information, even though the function takes several stock arguments and returns a RandGen_Handle;

  • the per-instance configs spec'd at lines 3a and 3b should seemingly be declared as corresponding fields inside of a special RandGen_Params structure;

  • the per-instance functions declared at lines 4a and 4b have no explicit declaration of their first argument of type RandGen_Handle; and

  • explict declarations of special functions like RandGen_Params_init, RandGen_construct, RandGen_destruct, and RandGen_delete are missing altogether.

Given the pattern-like regularity of the programming idioms used with any RTSC instance module, XDCspec chooses instead to allow suppliers of such modules to focus on declaring their module's unique per-instance features—its configs and functions—without regard for their exact binding in any particular programming language environment. Axiomatic to RTSC, the same module spec already has a realization in the XDCscript-based meta-domain as well as the C-based target-domain, taking on a somewhat different "look-and-feel" in each domain which ultimately reflects the underlying programming language.

The XDCtools product includes an interactive documentation-generation tool for RTSC packages named xdc.tools.cdoc.sg which you can invoke using the standard xs command. Leveraging XDoc comments embedded in module .xdc files contained in each package found along the RTSC package path—the directories comprising the XDCPATH environment variable set back in Lesson 0 together with the special «xdcroot»/packages directory—the cdoc tool presents distinct target-domain and meta-domain views of an otherwise common module spec file, in terms more meaningful to the client-side programmer. In the case of the C-centric target-domain view, the cdoc.sg tool here fills-in all the seemingly "missing" programmatic information absent from the underlying .xdc file.

Unlike the other per-instance functions specified in RandGen.xdc—which each operate on a previously created instance object, passed through an extra argument of type RandGen_Handle in C—the declaration of create at line 2 seems unnecessary and even somewhat misplaced. As it turns out, all of the special functions used throughout the instance object life-cycle—including RandGen_create, with its stock signature—would still be defined for the RandGen module even with line 2 omitted from the spec. We've elected to explicitly mention create here, in part to remind us of the seminal role this function plays when working with instance objects in RTSC; but also (as we will see in a new module introduced in the next lesson) to suggest that the corresponding Mod_create may require additional arguments at the discretion of the module supplier.

Looking past the internal keyword, the struct declaration of Instance_State at line 5 of RandGen.xdc plays a similar role to the Module_State structure used earlier by Bench in Lesson 7—both specify internal state variables used in this module's implementation by declaring fields of a well-known struct. Unlike the Bench module, however, the implementation of RandGen relies only upon per-instance state—a fresh set of internal variables automatically associated with each instance object upon creation.

Many of the new concepts introduced in XDCspec—such as "internal per-instance state variables"—have clear parallels in the world of object-oriented programming, where languages like C++ or Java have explicit support for controlling visibility of class members on a per-function or per-field basis through keywords like public or private; as noted earlier, these languages already use the static keyword to designate class members that remain common all instances—equivalent in concept to module-wide declarations in RTSC. Over time, perhaps, the same XDCspec source file might yield bindings for other target languages such as C++ (arguably more expressive when working with instance objects in general, though certainly less widespread than C inside the embedded programming community).

Implementing the module (target-domain)

The target-implementation found in RandGen.c reveals many of the "missing" details in the RandGen.xdc spec, such as the extra argument passed to each per-instance function or the Params structure used to collect per-instance config values during creation.

#include "package/internal/RandGen.xdc.h"
Void RandGen_Instance_init(RandGen_Object *obj, const RandGen_Params *params)
    obj->range = params->range;
    obj->next = params->seed;
Int16 RandGen_next(RandGen_Object *obj)
    /* randomize and retain the next sequence value */
    obj->next = obj->next * 1103515245 + 12345;
    /* scale the result to within the sequence's parameterized range */
    return (Int16)((obj->next / (RandGen_MAXVALUE + 1)) % ((ULong)obj->range + 1));
Void RandGen_nextn(RandGen_Object *obj, Int16 *buffer, Int count)
    Int i;
    /* fill the buffer through repeated calls to RandGen_next */
    for (i = 0; i < count; i++) {
        *buffer++ = RandGen_next(obj);

The special target function defined at line 1 effectively serves as a common "back-end" for any client-side calls to RandGen_create or RandGen_construct at runtime. Passed a pointer to a fresh RandGen_Object structure—whose field names match those of Instance_State declared at line 5 of RandGen.xdc—the module's special RandGen_Instance_init function assumes general responsibility for initializing the set of per-instance state variables associated with this newly created object. Once initialized, these very same RandGen_Object structures re-enter the module's target-implementation as first arguments of all per-instance functions (like RandGen_next at line 2 or RandGen_nextn at line 3) where these internal structures become subject to further access and update.

Unlike the Mod_Handle or Mod_Struct types respectively returned by Mod_create and passed to Mod_constructopaque types from the client's perspective whose internal representation remains hidden—everything becomes transparent with the concrete Mod_Object structures manipulated directly inside the module's implementation. In reality, Mod_Handle is no more than a typedef for Mod_Object* (and Mod_Struct is just an amorphous block of memory at least as large as a concrete Mod_Object); and yet, to emphasize the all-important client-supplier dichotomy within RTSC, we will avoid direct use of a module's (internal) Mod_Object type outside of its implementation.

Continuing with the instance creation pattern from the supplier's perspective, any Mod_Instance_init function will also accept a reference to a (readonly) Mod_Params structure whose client-assignable fields—the module's per-instance configs—often play an important role in shaping the newly created instance object. Since the client will often declare local variables of type Mod_Params before calling Mod_create or Mod_construct, the body of Mod_Instance_init must often retain certain of its per-instance configs in corresponding state variables for later access in the instance life-cycle—as is the case at line 1 of RandGen.c. Note also that Mod_Instance_init receives a special Mod_Params structure initialized with the spec'd defaults for each per-instance config whenever client passes NULL as the corresponding argument to Mod_create or Mod_construct.

Working in C++ or Java, functions like RandGen_Instance_init would become constructors for a corresponding class—responsible for initializing the state of a newly-allocated instance object belonging to this class. Following this pattern, the built-in new operator would supplant client calls to functions like RandGen_create.

Implementing the module (meta-domain)

Turning now to the meta-implementation of RandGen, we once again introduce another special XDCscript function used here to initialize the state of any RandGen instance objects created statically during program configuration.

function instance$static$init(obj, params)
    obj.range = params.range;
    obj.next = params.seed;

Seeing past some obvious syntactic differences between XDCscript and C, the instance$static$init function defined at line 4 of RandGen.xs becomes a configuration-time clone of the runtime RandGen_Instance_init function defined at line 1 of RandGen.c. Similar to how Bench used module$static$init in Lesson 7 to statically initialize its module-wide state, the instance$static$init function at line 4 would here yield a statically-initialized RandGen_Object structure in the target-domain.

Unlike initialization of module-wide state—which occurs just once in the meta-domain, barring any supplemental processing required at program startup—initialization of per-instance state objects can occur multiple times and in either domain. With clients of the RandGen module able to repeatedly call RandGen.create in the meta-domain as well as RandGen_create (or RandGen_construct) in the target-domain, the supplier of RandGen must effectively provide two implementations of the same back-end functionality to support instance creation in general.

Building the package

Like the acme.utils package examined in Lesson 6, the bravo.math package only builds libraries; you'll find sample client programs that consume this package back in Lesson 3 and Lesson 4. The build script for this package contains one minor twist, however.

var Build = xdc.useModule('xdc.bld.BuildEnvironment');
var Pkg = xdc.useModule('xdc.bld.PackageContents');
var LIB_NAME = "lib/" + Pkg.name;
var LIB_SRCS = ["RandGen.c"];
for each (var targ in Build.targets) {
    Pkg.addLibrary(LIB_NAME, targ).addObjects(LIB_SRCS);

As programmers, we're continually reminded to avoid placing arbitrary constants in the middle of executable code and instead to define meaningful symbolic names at the top of our source files; we've done just that at lines 1 and 2 of package.bld. Besides improving the readability of line 3, this technique simplifies the maintenance of more complex build scripts that contain multiple module source files or perhaps more that one named library for each target.

Lest we forget, package.bld is a program (albeit one that executes in the meta-domain) and XDCscript is a general-purpose programming language. But that's a good thing, since we can now apply the same sorts of techniques we already know from working with C to keep our source code simple yet flexible.

See also

C - Instance_init Module's instance initialization function
Command - xdc.tools.cdoc.sg Interactive package documentation tool
XDCscript - Instance-Body.instance$static$init Statically initialize a target module's instance object

[printable version]  offline version generated on 18-Aug-2017 00:08 UTC  
Copyright © 2008 The Eclipse Foundation. All Rights Reserved
Personal tools
package reference