Datatype Element/en

Aus expecco Wiki (Version 2.x)
(Weitergeleitet von Datatype Element)
Zur Navigation springen Zur Suche springen

Introduction[Bearbeiten]

A datatype element is used to define additional datatypes which are not available in the default set of provided datatypes. They can be modified in the datatype editor. Beside user defined types, a number of builtin (predefined) types are available. Those are not found in the navigation tree, but will be presented in the datatype chooser-menus.

Standard (Predefined) Types[Bearbeiten]

A number of types are already built into and well-known to expecco. These are:

General Types[Bearbeiten]

Any
any object. Useful when the exact type is not known/not relevant. Instances can be of any other type. But please read "Any Type Considered Harmful" before defining an output pin, or freezing an input pin with this type
struct
the "struct" singleton type (i.e. there is only one such type) represents arbitrary compound values, whose field names are defined at runtime. These can be used for compound objects, whose structure is not known in advance (for example, which are returned by an XML-RPC call). Technically, instances are represented as Dictionary (HashTable) instances. In a textual type definition, use the keyword "struct".

Predefined Primary Types[Bearbeiten]

The following well known types are predefined and guaranteed to be present in any expecco, without a need for a special type definition. They all refer to instances of standard classes of the underlying Smalltalk runtime system. More information is found in the "Expecco API" document and the "Smalltalk Class Documentation" of the "Smalltalk/X Online Documentation".

A good entry point to the documentation is the "Smalltalk/X Basic Classes Overview" document, which provides links to the most heavily used classes and more detailed information.

Numeric Types[Bearbeiten]
Integer
an integral number of arbitrary size (i.e. only limited by memory). Operations on integers automatically care for overflow and reserve more memory if required (i.e. there is no 32- or 64-bit overflow as in most other programming languages). If required to reproduce invalid numeric operations of other programming languages (i.e. C, C++, Java or C#), modulu operations which wrap the result within the 32bit range are provided in the standard library and/or the underlying Smalltalk library.
Details in Integer.
Additional info is found in Number API Functions.
Float
an IEEE rational number with double-precision (64bit).
Point right.png Be careful when using floats!Point left.png
Floats sometimes show surprising behavior.
E.g. the sum of 10 floats of 0.1 does not result in an exact 1.0, but in 0.99999999999999989 due to rounding errors.
In general, any number which is not exactly a sum of power-of-two fractions is not representable exactly as float and suffers from this rounding error. This is not a problem or deficiency specific to expecco or its implementation, but a general problem of floating point numbers. Therefore, never use floats when you need exact (e.g. monetary) values.
Read Wikipedia on Accuracy and Wikipedia on Representation for background information.
This also affect comparison of float values, especially compare for equality: it is a bad idea to compare the result of a computation or measurement against a fix value (for example: against zero), because the result might be slightly above or below due to rounding errors in the computation.
In expecco, there are extra blocks for fuzzy comparing float values (almost equal) in the Standard Library.
Class details in Float.
Additional info is found in Number API Functions.
Fraction
an exact fraction. These can result from dividing two integers. Operations on fractions are exact (without rounding errors). When possible, results are reduced and if possible converted to integral results (i.e. (1/3) * (1/3) * 9 -> 1, exact).
Class details in Fraction.
Additional info is found in Number API Functions.
ScaledDecimal (also called FixedPoint)
a decimal number which presents itself rounded to a given scale (= number of post-decimal digits). Although presenting its value rounded to a number of post-decimal digits, the exact value is kept internally for arithmetic operations, to prevent accumulating rounding errors. These are useful when values are to be processed with their exact value, but should print themself rounded.
Class details in ScaledDecimal.
Additional info is found in Number API Functions.
FixedDecimal
a decimal number which always rounds its value to a given scale (= number of post-decimal digits). Similar to ScaledDecimals when printed, these use the rounded value in arithmetic operations.
Details in FixedDecimal.
Additional info in Number API Functions.
Complex
a complex number consisting of a real- and an imaginary part.
Class details in Complex.
Additional info is found in Number API Functions.
Number
any numeric value (integer, float, fraction etc.). Most arithmetic action blocks allow for mixed mode arithmetic; i.e. it is possible to add integer values to fractions, floats and vice versa. The resulting value's representation depends on the combination of input values, and will usually be the one with less exactness. I.e. when adding an exact integer or fraction to an inexact float, the result will be an inexact float.
Class details in Number.
Additional info is found in Number API Functions.
Text Types[Bearbeiten]
String
a collection of characters of the Unicode character set.
Read more in String API Functions
For a full protocol description, see String class documentation.
Point right.png Notice that String inherits a lot of functionality from its superclasses CharacterArray, SequenceableCollection and Collection; most of it is also useful for String instances.
Password
same as String (above), but the string is not shown in the log or in the report.
StringCollection
a collection of lines, typically as read from a file. StringCollections inherit from OrderedCollection, therefore any operation which is possible for an OrderedCollection is also valid for StringCollections.
See StringCollection for more info
Filename
represents a path/file name. Filename instances know how to construct sub- and parent folder names, provide checks for file existence and size queries, etc. If used inside an elementary block, the full protocol of the Filename class can be used on them. In a freeze value of this type, "$(xxx)"and "%(...)" patterns are expanded as described in the "ElementaryBlock Element Documentation".
StringOrFilename
a union type which can be either a Filename object, or a String. This is used as input type of many file-handling actions, which will accept both. If required, those actions convert the string to a Filename instance automatically, before performing their action. In a freeze value of this type, "$(xxx)"and "%(...)" patterns are expanded as described in the "ElementaryBlock Element Documentation".
UnicodeString
now obsolete because the String type has been generalized to include both single-byte and wide characters. The type is still present for backward compatibility, but should not be used explicitly in new projects.
URL
URLs (Universal Resource Locators) are used in web browsers to address web documents and to locate web services. Instances know how to extract host, port, access method and path.
See URL for more info.
Collection Types[Bearbeiten]
Array
an array of any kind of object. The elements can be any object (even mixed).
Arrays have a fixed size; i.e. once allocated, it cannot be resized (as opposed to OrderedCollections).
See Array.
BitString
an array of bits. The elements are integers in the range {0..1}.
See BitArray for more info.
ByteArray
an array of bytes. The elements are integers in the range {0..255}.
See ByteArray.
Collection
a collection of any other object (possibly unordered). Collection is an abstract superclass covering all other container types. This includes ordered and unordered collections, Strings, Sets and Dictionaries. This is an abstract type so no instances of it will be created, but all other collection types inherit its functionality.
See Collection for more info.
Dictionary (HashTable)
a mapped collection, where the key/index may be any arbitrary object. The Java name of this is "Hashtable". Be aware, that dictionaries do not define an ordering of elements. For that, use instances of OrderedDictionary or JSONObject (which are both subclasses of Dictionary and both have ordered elements)
See Dictionary for more info.
DoubleArray
an array of double precision IEEE floating point numbers. The elements are 64bit doubles.
DoubleArrays are used both for more compact storage, and to efficiently pass large collections of numbers to/from C-code (either DLL-calls or CBridge actions).
See DoubleArray.
FloatArray
an array of single precision IEEE floating point numbers. The elements are 32bit floats.
FloatArrays are used both for more compact storage, and to efficiently pass large collections of numbers to/from C-code (either DLL-calls or CBridge actions).
See FloatArray.
HexString
an alias for ByteArray, for TTCN3 compatibility
Int16Array, Int32Array, Int64Array, UInt16Array,...
an array of 16, 32 or 64 bit integer numbers (signed or unsigned). The elements are integers.
These are used both for more compact storage, and to efficiently pass large collections of numbers to/from C-code (either DLL-calls or CBridge actions).
See UInt16Array, Int16Array, UInt32Array, Int32Array, UInt64Array, Int64Array.
OctetString
alias for ByteArray, for TTCN3 compatibility
OrderedCollection
a growable (variable sized) collection of arbitrary objects which is indexed by a numeric index.
See OrderedCollection for more info.
OrderedDictionary
a dictionary with a definite and constant order of its elements.
See OrderedDictionary for more info.
OrderedSet
a set with a definite and constant order of its elements.
See OrderedSet for more info.
SequenceableCollection
any collection, with integer indexed elements (possibly non-growing). This is an abstract type, and concrete subclasses are OrderedCollection, Array, ByteArray and many others.
See SequenceableCollection for more info.
Set
an unordered collection, where every object occurs at most once.
See Set for more info.

The system contains many more collection types, such as Bags, BTrees, LinkedLists, etc.
For more information refer to the ["Smalltalk/X Online Documentation"] or open a class browser and look at the subclasses of the "Collection" class.

Time and Date[Bearbeiten]
Date
represents a date (dd-mm-yy). Instances offer many functions to get the dayOfWeek, to generate various string representations etc. See Date.
DateTime
represents both date and time (aka: Timestamp) with at least millisecond resolution (actually, instances can hold a much better resolution, but not all operating systems will provide such high resolution timestamps). DateTime is an alias for the Timestamp class of Smalltalk. See Timestamp.

Timestamps are not limited to the 1970..2039 range as in many Unix systems, or the 1900.. range, as in MS Windows. However, they do not correctly handle the Julian-Gregorian calendar change (e.g. when asked for weekday, leap year etc., they will simply extrapolate the Gregorian calendar) [1]).

Point right.png Timestamps will be represented either as UTC-timestamps, local timestamps or as TZ-timestamps, depending on the information which was present when it was created. When reading from a string, any timezone information there is preserved and the timestamp will present itself with that information. Thus, if a timestamp was read from an ISO8601 string with a "Z"-timezone, it will be a UTC-timestamp, and present itself as such. If the string ended with a "+/-hh" timezone delta, it will be a TZ-timestamp. Without timezone information, it will be interpreted as a local timestamp. You can always convert timestamps among the above representations, but internally they will all hold the UTC time, so the result of comparisons (before/after) and the sort order is always correct (i.e. based on the UTC time).

Time
a time of the day (hh:mm:ss).
Point right.png Notice, that Time objects represent a time within a single day (in contrast to DateTime ). See the class documentation of Time.
TimeDuration
a time-delta (eg. "100ms", "0.4s", "10m", "1h" or "3d") with at least millisecond resolution. For details, see the class documentation of TimeDuration.
Often Useful Types[Bearbeiten]
Boolean
boolean truth values: true and false. See Boolean.
IPAddress
represents an ipv4 or ipv6 address. See SocketAddress.
PhysicalUnit Types / Physical Values
expecco provides a number of types to represent physical values such as mass, volume, speed, voltage, etc. These are described in detail in "Physical Values".
Socket
represents a socket connection stream. See Socket. Notice that sockets inherit from Stream, so all operations defined there are also useful when writing elementary code for socket connections.
Stream
any stream. See Stream and its subclasses, especially PositionableStream, ReadStream, WriteStream and ExternalStream, which is the superclass of FileStream.
UUID
a globally unique identifier. See UUID.
Special Low Level Types (not normally used)[Bearbeiten]
ExternalAddress
a pointer to external data (C pointer). Instances are typically allocated using malloc / free and are passed to or returned from calls to C library functions (via DLL action blocks).
Handle
a handle as returned by some operating-system calls. For example, window handles and open file handles are represented by them. Also references to remote bridge objects are represented as handles.

GUI Types[Bearbeiten]

These represent objects as needed or returned by GUI actions.

Point
represents a 2D coordinate on the screen or within a widget element. It holds on two values, x and y.
Details are found in the Smalltalk/X documentation on Point.

Rectangle
represents a 2D rectangular area on the screen or within a widget element. It holds on the origin (= top left corner point) and the extent (= width and height).
Details are found in the Smalltalk/X documentation on Rectangle.

Image
a bitmap image. Bitmaps can be captured from the screen, read from a file (eg. a gif, png or jpg file) or be provided by a GUI action. Images may have different color interpretations (black&white, greyscale, colormap or RGB pixels) and different depths (= number of bits per pixel). Various conversions, file save and image manipulating functions are provided by the Image class.
Details are found in the Smalltalk/X documentation on Image.

Expecco Types[Bearbeiten]

These represent objects as used or generated by expecco itself.

ActivityLog
Represents detail information of a test- or block execution. In particular, pin values, log-messages, caller and called actions are recorded in here.
Datatype
a meta type; instances are datatypes themselves. These are most useful as a pin type of an instance creation block. For example, in the collection-instantiation blocks, a type pin defines what type of collection is to be created.

Datatype-types can be constraint (a so called "constraint datatype-type") to a subset of types which match a particular query.
This is e.g. useful to specify that the frozen type must be a subclass of Collection.

Details are found in the datatype editor's documentation.

Error & Exception
An object which provides additional details of an exception (generated at exception output pins)
GUI Aspect
used with GUI block descriptions
Library
Instances represent imported libraries. Pins of this type can only be frozen to a library; these are used to define which concrete library is to be used for a bundle of virtual actions. Similar to performer pins, these define at execution time which concrete actions are to be used for virtual actions (in this case: all virtual actions from a particular library).
Performer
Instances represent action blocks. Their primary use is as input value of a virtual block, in order to define which concrete block is to be executed. Constant values (freeze values) of this type are kept as UUID (Universal Unique Identifier) of the referred-to block. The id of the block is found via the block's selection menu in the project tree, or in the documentation page of the block's editor.

Performer types can also be constrained to only allow for actions with a defined interface to be connected. This type is used with performer input pins, and the constraint is used to ensure that only references to allowed actions (i.e. implementing that interface) are delivered to the pin.

Report Template
Instances represent Report Templates
Resource
Represents a device, a human operator or any other resource.
SUnit TestResult
represents test results as generated by an internal unit test execution
Symbol
unique symbols of the underlying Smalltalk runtime system. These are used as hash key when naming classes, methods, UI aspects and others. Also, all enum values are internally represented as instances of Symbol. Symbols inherit from the String class and can thus be used (readonly) wherever strings can be used; in particular, with the formatting and string concatenation functions.
Verdict
Instances represent a test- or block execution's outcome (PASS, FAIL, ERROR or INCONCLUSIVE)

User Defined Types[Bearbeiten]

New user defined types can be constructed by a number of different mechanisms:

Primary Types[Bearbeiten]

Primary types represent existing classes of the underlying Smalltalk VM (virtual machine). These are the classes as described in the "Smalltalk/X Online Documentation", in addition to any classes which have been loaded dynamically via the plugin mechanism or as extension classes. Notice that all of the above listed builtin types are actually predefined well-known primary types and are placeholders/aliases for the corresponding underlying Smalltalk classes.

You can browse the system for all classes using the system browser (found in the "Tools" menu); however, you must have installed the source code (in the installation procedure) to see the code.

Tuple Types[Bearbeiten]

A tuple type consists of a fixed number of fields which are accessed by a numeric index, where each field is defined by its (individual) type. For example, an object which associates a name with an age and a gender could be represented by a tuple instance as:

   (
       String
       Integer
       enum(M|F)
   )

when instantiated, fixed sizes arrays are created for tuple instances. Given a tuple type T, it can be instantiated with:

   inst = T.new();

and the fields can be set with:

   inst[1] = "Andy";
   inst[2] = 45;
   inst[3] = 'M'.asSymbol();

In most cases, compound types are a better choice to tuples: due to the naming of fields, compounds are less error prone and more self-describing.

Compound Types[Bearbeiten]

A compound type consists of a number of named fields, each defined by its type. For example, a customer record could be represented by a compound type instance as a structure consisting of individual fields:

   {
       firstName: String
       lastName: String
       zip: Integer
       age: Integer
   }

when instantiated, an anonymous class is created for instances of compound types. These objects provide virtual accessor functions (getters and setters), which are named as the field names. Thus, given a compound type T, it can be instantiated with:

   inst = T.new();

and the fields can be set with:

   inst.firstName( "Andy" );
   inst.lastName( "Miller" );
   inst.age ( 41 );

Enumeration Types[Bearbeiten]

An instance of an enumerated type can take a value from a defined list of values. For example, a test-outcome could be defined as an enumerated type:

   (
       pass | fail | inconclusive
   )

Point right.png Notice, that there is already a Verdict type present in the set of standard types, so the above example is somewhat unrealistic.

Internally, enum values are kept as instances of the underlying Smalltalk Symbol class, which itself derives from String. Thus, an enum value can be fed to any input which accepts strings. Be aware of this: if you define an enum type as "( 1 | 2 | 3)", the enum-elements will be the strings "1", "2" and "3" - not integers.

A freeze value editor presents the enum values as a combolist, which presents the values as a list from which the user chooses the desired item. The organization of this list can be specified in the datatype editor to better meet UI requirements (for example, as hierarchical menu, sorted etc.)


Enum values can have an associated integer value, which is useful, if the type is actually a mapping of a corresponding C or Java type.
For this, add an "= <integerValue>" after the element's name to the above definition. I.e.:

   (
       pass = 10 | fail = 20 | inconclusive = 30
   )

Without explicit integer value, elements get auto-incremented values, starting with 0 (zero) assigned implicitly.

Point right.png Notice, that even with values associated, there are still symbols passed along input/output pins. To retrieve an enum's associated value, you have to either use an action ("Convert [Enum to Integer]", in the standard library) or additional elementary code ("theDataType integerValueOf:enumSymbol").

Subrange Types[Bearbeiten]

A subrange of the set of values of either the integer- or the character type. A subrange type defines the minimum and maximum values from the base type's set of values.

For example, a type to describe byte-valued integers could be described as:

range( Integer : 0 .. 255 )

Union Types[Bearbeiten]

A union type represents a number of alternative fields. The selection of which field is actually valid within a union must be done elsewhere (or, via reflection, by asking the object dynamically).

For example, a type to describe an object which is either numeric or a string, could be described as:

   (
       String | Integer
   )

When freezing a pin with union type, you must either specify the concrete type (by using "Freeze as...") or by providing an unambitious freeze value text. In the above example, a freeze value entry of "123" (without double quotes) will make the freeze value an integer, whereas if entered as " '123' " (with single quotes) will make it a string.

Datatype Types[Bearbeiten]

A type representing other types

The type definition:

datatype

defines a type which can represent any other type.

This can be used (especially if constraint as described below) to pass a type to be instantiated in the action. For example, if a collection object is to be instantiated, and you want to provide the type of collection as an input value.

Constraint Datatype Type[Bearbeiten]

Datatype types can be constraint as described in more detail below. For example, a type to represent all types which are CTypes can be described as:

datatype( isCTypeType )

These types are useful as pinType of instance creators, especially because the freeze-value menu provides useful selection lists.

The general form of such a constraint type is:

datatype ( <constraint> )

where <constraint> is either the name of a type:

  • <compatTypeName> - constraint to types which are compatible with a type named <compatTypeName>

or a selector from one of:

  • isApplicationClassType - for defined class types where the defined class inherits from ApplicationModel. An alternative to this is to define the constraint as "datatype(ApplicationModel)".
  • isArrayType - for types which have integer-indexable slots
  • isCTypeType - for C-defined types
  • isCEnumType - for C enum types (2.12)
  • isCStructType - for C-defined struct types (2.12)
  • isCUnionType - for C-defined union types (2.12)
  • isCollectionPrimaryType - for subtypes of Collection (i.e. Dictionary, Set, OrderedCollection etc.)
  • isCompoundType - for compound types
  • isEnumType - for enum types
  • isNumberPrimaryType - for subtypes of Number (i.e. Float, Integer, Fraction, FixedPoint)
  • isPrimaryType - for any primary type
(primary types are those defined as class in the underlying framework. You can see and inspect those with the class browser, which is opened via the "Extras" → "Tools" menu)
  • isStreamPrimaryType - for subtypes of Stream
  • isTupleType - for any tuple type
  • isUnionType - for any union type
  • isUserDefinedDatatype - for all user defined types
  • isWellKnownDatatype - for all builtin (i.e. not user defined) type

or a selector plus string argument from one of:

  • nameMatches: ' globPattern '
  • nameMatchesRegex: ' regexPattern '
  • hasTag: ' tagString '

Constrained datatypes should be used by the StandardLibraries only, and are used to limit the number of possible input values of some instance creation actions (especially to reduce the number of items in the freeze-value-suggestion lists).
For example, the "New Collection" action has an input pin, which defines the type of collection to be created. This pin has the type: "datatype( isCollectionPrimaryType )".

Examples:

datatype ( Integer ) -- a type whose instances are all datatypes which are compatible with the Integer class
datatype ( isCTypeType ) -- a type whose instances are all known C-types
datatype ( isCStructType ) -- a type representing all known C struct types
datatype ( nameMatches: 'DPU*' ) -- a type representing all types which match this name

CTypes[Bearbeiten]

A CType is similar to a compound type; however, a data representation is used, which is compatible to structures of the C programming language. This representation is mapped onto a byte container, such as a ByteArray or a malloc'd chunk of memory, and can be passed to/from external C functions.

Be aware, that by default, C data is allocated on the C heap using malloc() and released by free(). Therefore, the overhead in terms of memory use and processing power is much higher, when compared to normal compound or primary instances (i.e. normal expecco objects). You should therefore only use CType instances when data has to be exchanged with C programs (typically via a DLL-call action block).

A CType's definition is given in C syntax.
For example, the above customer record could be represented as the following C structure:

   /* C: */
   struct {
       char firstName[20];
       char lastName[20];
       int zip;
       short age;
   }

Point right.png Notice the "/* C: */"-comment in the first line, which is obligatory to mark C type definitions. Also notice the explicit dimension of the strings. These are required to define the C structure's exact size. When instantiated, an object is created, which has the underlying C structure layout, and which is not moved in memory by the garbage collector (i.e. its address remains constant). It can therefore be passed to a called C function, for example in a DLL call.

Similar to compound types, instances of CTypes understand accessor functions.
Given a CType in variable T, it can be instantiated with:

   inst = T.new();

a good trick to get hold of the datatype is to fetch it from an output pin, as in:

   inst = aCtypeOutputPin.datatype.new();

Then, the fields can be set with:

   inst.firstName( "Andy" );
   inst.lastName( "Miller" );
   inst.age ( 41 );
Dealing with CPU Differences[Bearbeiten]

The C language does not explicitly specify the size of its data types. Therefore, an "int" can be a 32 bit or a 64 bit entity, depending on the CPU, the compiler and maybe even on compiler flags. The only requirement in C is that sizeo(short) <= sizeof(int) <= sizeof(long) <= sizeof(long long).

The consequence of this is for example noticable with Windows C compilers on x64_64 64bit machines, where "int" and "long" are both 32bit entities and "long long" are 64 bit quantities; in contrast to Linux, where "int"s are 32bit but both "long" and "long long" ints are 64bit integers.

To deal with this situation, expecco provides both types with and without a definite size:

  • int16, int32, int64
    these are integers of that particular size
  • short, int, long
    these are integers of the machine's natural size (actually: as defined by the compiler with which expecco was compiled)

The same goes for unsigned variants.

You may use the natural sizes if you call into dll's or communicate with a program on the local machine via shared memory. But you should use the explicit-sized integers when communicating via a binary protocol with programs on other machines.

Memory Management[Bearbeiten]

By default, a CDatum's underlying memory is allocated on the C-heap by gcMalloc(), which uses the standard malloc() function. It will be automatically freed, when there is no longer any reference to it from expecco.

This scheme works in most situations, except when the datum is to be given to an external C-function which frees the data after use or which holds on the data for an unknown time period. An example is a message-queue framework, where data-buffers are passed to a C-function which frees the data, when done. Another example might be a framework where data is passed to a C-library which frees it later, when a release/cleanup/shutdown function is called.

In this case, expecco should not free the data, because it does not know about any pending references inside the called C code. To prevent this, either send a "protectFromGC()" message to the allocated CDatum:

   inst.protectFromGC();

or allocate it using "malloc()" instead of "gcMalloc()".

Your program (or suite) then has to explicitly free the underlying memory at some time (maybe as a post-action of either the test plan or the unload actions of your project). Explicit freeing is also useful if a big gcMalloc'd memory block should be released as soon as possible:

   inst.free();
Nested Structures and Arrays of Structures[Bearbeiten]

Some care is required when C structures are nested or if arrays of structures/unions are used, as in the following structure:

/* C: */
struct s {
   unsigned long Address;
   unsigned long Length;
   unsigned long PortAddress;
   struct p {
       unsigned char ProtocolID;
       union u {
           uint16 InOutAreaSize;
           uint16 InAreaSize;
       } Parameter;
   } Protocol[4];
};

Assuming, that the above type is referred to as T (acquired usually by getting a pin's datatype), a new instance is created as usual with:

   T = outputPin.datatype;
   inst = T.new();

and gives you a C-datum of size 40 (on a 64 bit machine).

and the fields can be set with:

   inst.Address( 1234 );
   inst.Length( 100 );
   inst.PortAddress ( 40 );

or (in JavaScript) with:

   inst.Address = 1234;
   inst.Length = 100;
   inst.PortAddress = 40;

The accessor functions will always extract (copy) out values from the C datum. Therefore, the inner fields of (say) parameter cannot be accessed directly using code like:

   this does not work!!!

   inst.Protocol[0].ProtocolID = 17;
   inst.Protocol[0].Parameter.InOutAreaSize = 20;

does not work, because it would first extract the Protocol-substructure (Protocol[0]) as a copy of the inner structure, and then access the ProtocolID and Parameter values there, instead of setting fields in the original datum.

To set those inner fields, you need to first get a pointer to the inner structure, and then refer to the fields via the pointer, as in:

   // the following gives us a pointer to the Protocol field...
   pProtocol = _newInst.refMemberAt('Protocol');

   // a pointer to the first element there...
   pProtocol0 = pProtocol.refAt(0);

and then manipulate fields via those pointers:

   pProtocol0.ProtocolID = 17;
   pProtocol0.Parameter.InOutAreaSize = 20;

So here follows example code for an action to instantiate and initialize an instance of the above data structure. To make the example a little shorter, not all fields are initialized; the action block is assumed to have input pins for Address, Length and Port values (all defined as Integer or unsigned long typed pins) and additional pins named pID0, param0 and pid1, param1 to preset two of the inner structures's elements:
New struct s action.png

and its execution code is:

execute() {
   // Generates a new instance of struct s

   var structType;
   var newInst;
   var pProtocol, pProtocol0, pProtocol1;
    
   structType = new_struct_s.datatype;
   newInst = structType.new;

   newInst.Address = Address.value;
   newInst.Length = Length.value;
   newInst.PortAddress = Port.value;

   // the following gives us a pointer to the Protocol field...
   pProtocol = newInst.refMemberAt('Protocol');
   // a pointer to the first element...
   pProtocol0 = pProtocol.refAt(0);

   // set those values from pin values
   pProtocol0.ProtocolID = pID0.value;
   pProtocol0.Parameter.dpInAreaSize = param0.value;
    
   // a pointer to the 2nd element...
   pProtocol1 = pProtocol.refAt(1);

   // set those values from pin values
   pProtocol1.ProtocolID = pID1.value;
   pProtocol1.Parameter.dpInAreaSize = param1.value;

   // write it to the output pin
   new_struct_s.value(newInst);
}
Dealing with Pointers[Bearbeiten]

Warning: the previous expecco releases (pre 18.1) do not handle embedded structure pointers correctly. The example below will only work in 18.1 and later versions.

Consider the following two type definition, which are used in an expecco type definition (of type CType):

/* C: */
struct a {
   int i1;
   float f1;
   double d1;
   char c[20];
};

struct b {
   int i2;
   float f2;
   double d2;
   struct a *aPtr;
};

first notice the comment in the first line, which tells the datatype parser that a C-struct is to be defined. Then, that there are two structures in one type definition.
The expecco type item (in the tree) will always represent the last of the defined types - in this case "struct b".

Let's assume, that we have to allocate an instance of "struct b" in an instance creation block (which has a single pin named "newInst" of type "struct b":

New struct b action.png

and the following code:

execute() {
   var structB_t, newInstOfB;

   structB_t = newInst.datatype();
   newInstOfB = structB_t.gcMalloc();
   
   newInst.value(newInstOfB); // write the pin
}

then, when executed, the output pin "newInst" will generate the following value:


New struct b action output.png

As seen, the "struct a"-pointer in "aPtr" is NULL. An instance needs to be allocated separately and a pointer to it must be placed into the pointer field. To get the pointer-field's type, we can ask the "struct b" instance for its "aPtr" field definition: (newInstOfB type fieldNamed:'aPtr') and then ask this field information about the field's type. Thus, we acquire the aPtr-field's type (i.e. the "struct a"-type) with:

structAPtr_t = (newInstOfB.type.fieldNamed("aPtr").type;

Notice, that this gives us a reference to the "struct a" type - not an instance of a "struct a" type. Thus,

structA_t = structAPtr_t.baseType;

and in the same way as above, we allocate an instance of it with:

newInstOfA = structA_t.gcMalloc();

Finally, the new instance is stored into the pointer field with:

newInstOfB.aPtr( newInstOfA );

So the final code to instantiate the two structs and link them via the pointer will be:

execute() {
   var structB_t, newInstOfA, newInstOfB, structAPtr_t, structA_t;

   // fetch the "struct b"-type from the pin's datatype
   structB_t = newInst.datatype;
   // get a new instance of "struct b"
   newInstOfB = structB_t.gcMalloc();
   newInstOfB.i2(22);
   newInstOfB.f2(22.2);
   newInstOfB.d2(22.22);

   // fetch the "pointer-to-struct a"-type from the field
   structAPtr_t = (newInstOfB.type.fieldNamed("aPtr").type;
   // dereference the type, getting the "struct a"-type
   structA_t = structAPtr_t.baseType;

   // get a new instance of "struct a"
   newInstOfA = structA_t.gcMalloc();
   newInstOfA.i1(11);
   newInstOfA.f1(11.1);
   newInstOfA.d1(11.11);
   newInstOfA.c("hello1");
    
   // store the pointer
   newInstOfB.aPtr(newInstOfA);

   // send it to the pin
   newInst value:newInstOfB.

when this version is executed, the output pin "newInst" will generate the following value:


New struct b action output2.png

Special Types[Bearbeiten]

The following two types are useful as a pin datatype. They should not be declared as elements in the project tree, but instead attached to a pin type (i.e. they are usually anonymous).

Template Types[Bearbeiten]

Template types allow for type save declaration of polymorphic operations.

Template types are a kind of placeholder-type, which will be bound to a real type as soon as a pin is connected. Template types are initially unbound. When a pin with an unbound template type is connected to a real typed pin, the template becomes bound and is treated like a pin of the other type. If a bound template typed pin is connected, the usual type compatibility checks are performed against the underlying real type. Template types have a special name, consisting of a "#"-character followed by a number (i.e. "#1", "#2" etc.). Within the set of pins of a single step, all template types with the same name are unified; i.e. bound together.

For example, assume that we have a block which takes an input value and, depending on a control input, sends its input value to one of two outputs. Let's call this a "multiplexer" block. This block's operation is independent of the input type: it could handle any datum. This might lead us to declare the value input and output pins as "Any":

        +-----+
Any --->| MUX |
Bool -->|     |---> Any
        +-----+

However, having an "Any"-output prevents it from being connected to any non-Any input. You will need a lot of type-cast blocks, to downcast from the very general "Any" to whatever type is actually passed (or disable type checks, or force a connection with the CTRL-key).

What we really want to say is that the output's type should always be the same as the input's type, and vice versa. I.e. if the input is connected to a Number-typed output, the multiplexer's output should also be of Number-type. If connected to a String-pin, the output should have String-type, and so on.

        +-----+
#1  --->| MUX |
Bool -->|     |---> #1
        +-----+

Exactly that is what a template type does: it states, that whatever type is connected to the input pin, will be the type of the output pin.

Template types can be embedded in a user-defined type: for example, a block which takes three inputs of arbitrary type, and generates a tuple of these values on its output, could be defined as:

       +--------+
#1 --->|        |
#2 --->| Tupler |---> (#1 #2 #3)
#3 --->|        |
       +--------+

If the inputs are connected to [Bool, Bool, String], it will only be possible to connect to blocks which accept tuples like (Bool, Bool, Number) or more general tuples, such as (Bool, Bool, Any), but not to more specific typed tuples, such as (Bool, Bool, Integer).

Please also read the motivation for template types in "Any Type Considered Harmful".

Constraint Template Types[Bearbeiten]

Constraint Template Types are similar to template types, in that they are initially unbound and bind to a concrete type when connected. However, they restrict the set of possible types which can be bound. For example, if some pin expects the type to be a kind of collection, the datatype can be defined as "#1(Collection)". Similar to a regular template type, this initially unbound type will be bound whenever a pin gets connected. Also, other "#1(Collection)" types at the same step are unified with this type.

However, when connecting, binding is only allowed to types which are compatible with a Collection type.

Array with Template Typed Elements[Bearbeiten]

The definition "#1[]"defines a (sequenceable) collection of template typed elements. Such a type is useful for pins which expect or generate container objects where the element type has to be unified with another pin's type.

Performer Type[Bearbeiten]

Instances of Performer Type represent a reference to an expecco action. Performer types can also be specific to only allow references to actions which implement a particular interface. Examples:

Performer
Performer ( 'Name of Virtual Action' )
Performer ( interface: UUID-of Virtual Action )

Formal (BNF) Syntax of Type Declarations[Bearbeiten]

Please refer to the "Formal BNF Description of the Expecco Type Syntax" in the datatype editor documentation.

Defining & Loading new Classes (Primary Types)[Bearbeiten]

Knowledgeable users can define their own classes, bundle them in a package, and load them into expecco; both dynamically during a test run or at expecco startup time. The procedure to generate such class libraries is described in the separate document: " Creating new Class Library Packages".

   

See Also[Bearbeiten]

Datatype Editor
A description of the underlying Smalltalk class library is found in "Smalltalk/X Online Documentation"
The full online documentation can be found under: Online Documentation



Copyright © 2014-2024 eXept Software AG