From RTSC-Pedia

Jump to: navigation, search
revision tip
—— LANDSCAPE orientation
[printable version]  offline version generated on 04-Aug-2010 21:08 UTC  

RTSC Module Primer/Lesson 3

Instance objects — using the RandGen module

Another lesson, another module.... Here, we'll focus on bravo.math.RandGen which exemplifies a very common design pattern employed by many RTSC modules—support for instance objects. Suggestive of classes in object-oriented programming languages like C++ or Java, RTSC modules can play a similar role by providing special functions for instantiating objects that clients can then manipulate using other public functions of the same module.

Lesson 8 covers the internal implementation of RandGen, looking at the module from its supplier's perspective. In this lesson, however, all we know about RandGen comes from the module's public specification—which defines the client programmer's perspective seen here.

Contents

Reading the program

We've intentionally kept prog.cfg to a bare minimum for this example, so you focus exclusively on some new RTSC programming idioms illustrated within prog.c.

lesson3/prog.cfg
 
 
var RandGen = xdc.useModule('bravo.math.RandGen');
var System = xdc.useModule('xdc.runtime.System');
 
lesson3/prog.c
 
 
 
 
 
 
 
1
2
 
 
3
 
 
 
4
 
 
5
 
 
 
 
 
#include <bravo/math/RandGen.h>
#include <xdc/runtime/System.h>
 
#define COUNT 10
 
Int main()
{
    RandGen_Handle rgInst;
    RandGen_Params rgParams;
    Int i;
 
    RandGen_Params_init(&rgParams);
 
    rgParams.range = 15;
    rgParams.seed = 3;
    rgInst = RandGen_create(&rgParams, NULL);
 
    for (i = 0; i < COUNT; i++) {
        System_printf("%d ", RandGen_next(rgInst)); 
    }
    System_printf("\n");
 
    return 0;
}

The RandGen module manages instance objects that represent distinct random-numbers sequences generated on-demand, each object created with a potentially unique set of parameters. Working backwards through prog.c, the function RandGen_next called at line 5 returns the next integral value in the random-number generator instance object referenced through rgInst; the function System_printf then outputs the value returned as a decimal number.

The function RandGen_create called earlier at line 4 will internally allocate and initialize an instance object and then return a reference to this newly-created object—here assigned to the local variable rgInst. Note that the latter variable—declared of type RandGen_Handle at line 1—conceptually behaves like a C pointer, efficiently passed to other functions like RandGen_next.

If you're looking for a corresponding RandGen_delete function, we'll get to that in the next lesson.

To shape the characteristics of the underlying instance object, RandGen_create accepts a (pointer-to-a) parameter structure comprising fields that determine the range of values produced by this newly-created generator as well as an initial seed value used by an algorithm internal to RandGen. This parameter structure—declared of type RandGen_Params at line 2—is first initialized to a reasonable set of default values at line 3 and then selectively updated prior to calling RandGen_create.

Though subtle, you may have already noticed the use of Int (in lieu of C's lowercase int type) within our implementation of main beginning at line 1 of prog.c. Rest assured, Int is int; and like over two dozen other RTSC standard types with names like Void, ULong, and String, the special header file <xdc/std.h> contains typedef's that define each of these RTSC types in terms of standard C. Automatically brought in by all RTSC module headers, client programs can nevertheless explicitly #include <xdc/std.h> if appropriate.

More than just "aesthetic uniformity" with our convention of using capitalized CamelCase names for types (RandGen_Handle, RandGen_Params, Int, String), RTSC adds some standard scalar types whose definition will truly vary from one target to the next: for example, the standard type IArg—a signed integral type large enough to hold a data pointer or a function address; or the standard type Bits32—an unsigned integral type occupying exactly 32-bits of space; or the standard type Int16—a signed integral type containing at least 16-bits of arithmetic precision. Though no panacea, using the RTSC standard types dramatically simplifies the task of writing portable C code targeted for processors ranging from 32-bit GPPs or DSPs down to 16-bit MCUs.

Running the program

Before we talk about instance creation in general, go ahead and run the example by invoking gmake test; output should appear as:

 
 
>> gmake test
6 7 10 1 12 8 10 14 1 15

To explore a little more, try playing with the assignments between lines 3 and 4 in prog.c to vary the program's output. You can also try commenting-out both of these lines as well as passing NULL as the first argument to RandGen_create, either of which will yield the same results:

 
 
>> gmake test
908 11517 20227 2263 29335 11254 13253 14839 32425 8173

Creating instance objects

Other than RandGen_next appearing on line 5 above, the remaining RandGen types and functions introduced in prog.c actually track a more generic pattern seen in all RTSC modules that support instance objects—termed RTSC instance modules. As you'll see when we examine RandGen from the supplier's perspective in Lesson 7, a simple declaration of instance object support in the module's spec automatically yields a rich set of client-visible features—the types RandGen_Handle and RandGen_Params, the special functions RandGen_Params_init and RandGen_create, plus numerous other features that address different facets of instance objects from the client's perspective.

Using RandGen as an exemplar, clients of any RTSC instance module will follow a stock recipe when creating new instance objects:

  • declare a (scalar) variable of the module's Mod_Handle type;
  • declare a (structure) variable of the module's Mod_Params type;
  • call the module's Mod_Params_init function to set default values;
  • alter fields in the Mod_Params structure where appropriate; and then
  • call the module's Mod_create function to obtain the new instance's Mod_Handle.

Instance parameters.  While all instance modules will implicitly define a Mod_Params type, the fields of this structure will naturally vary from one instance module to the next; range and seed were explicitly declared by the supplier of RandGen, for example. Conceptually, you can view these fields as playing a role analogous to the module configuration parameters seen in Lesson 2—in fact, module suppliers spec these parameters no differently than configs, declaring a type and default value for each. To avoid confusion, we'll start using the terms module(-wide) config versus (per-)instance config to distinguish configuration parameters that control the module as a whole—and are only assignable in the meta-domain—versus those parameters that shape an individual instance object during its run-time creation in the target-domain.

The Mod_Params_init function associated with a RTSC instance module initializes each field in the Mod_Params structure with the default value for a corresponding per-instance config spec'd by the module's supplier. Clients who plan to alter the values of certain instance configs by selectively updating the Mod_Params structure must call the Mod_Params_init function first.

With Mod_Params structures often allocated as local variables—whose initial values are undefined in C—failure to call Mod_Params_init can lead to erroneous program behavior. Even statically-defined Mod_Params structures—guaranteed by C to have 0 as the value of all scalar fields—require a call to Mod_Params_init prior to usage.

Creation inputs.  The Mod_create function associated with a RTSC instance module accepts as input a reference to a Mod_Params structure—initialized and updated by the client prior to calling create. As a rule, the Mod_create function neither modifies the Mod_Params structure nor retains any pointers to its fields; clients can re-use the same (locally-allocated) structure across multiple Mod_create calls if they so choose. In cases where you want to create an instance object using default values for all instance configs, calling Mod_create with NULL instead of a valid Mod_Params structure reference does the job—and with less clutter in your code.

The last argument to the Mod_create function pertains to underlying error-handling support through the xdc.runtime.Error module. Since Mod_create at some level will dynamically allocate storage for a new instance object using the special xdc.runtime.Memory module—an internal operation that can potentially fail—some sort of prevailing error-handling strategy must be established by the client calling Mod_create. Passing NULL for this argument effectively enables a default strategy—typically, print an appropriate message and then abort execution whenever the program raises an error.

Though we will touch upon both Error and Memory in later lessons, our usage will remain rather elementary. Once you master the material in this Primer, however, we'll direct you to a series of User's Guides that delve into the several dozen concrete modules and abstract interfaces contained within the xdc.runtime package.

While many Mod_create functions will accept only two inputs, suppliers of instance modules can specify additional arguments to Mod_create; we will see an example of this in Lesson 9. Stated another way, the last two arguments to Mod_create will always correspond to the per-instance configs and the prevailing error-handling strategy used when creating this particular instance object—and again, with NULL passed as either argument signifying an appropriate default. Finally, Mod_create returns a (unique) value of type Mod_Handle that references the newly-created instance object or else NULL in the event of failure.

See also

RTSC Coding Conventions Coding conventions used in the XDCtools product

[printable version]  offline version generated on 04-Aug-2010 21:08 UTC  
Copyright © 2008 The Eclipse Foundation. All Rights Reserved
Views
Personal tools
package reference