• No results found

3.3 The Metaclass

3.3.1 Metaclass Ports Overview

A port is a metaclass feature through which a component can read or write data from, respectively to other components. A dataflow application is thus represented as a directed graph whose nodes are metaclass instances and arcs are port-to-port connections. The port-to-port connections are called links

in MC++. A port has three main attributes: type, input/output, and read/write. These attributes deter- mine whether two ports can be connected by a link, and, in the positive case, how data is transferred across the link. Port attributes can vary independently, so one may have input read, output read, input write, and output write ports, of different C++ types.

In most existing dataflow systems, ports have only the type and input/output attributes. However, these systems have difficulties supporting the several data passing mechanisms provided by program- ming languages, such as by-value, by-reference, by-pointer, and reference counted data passing. Small datasets are usually passed by value. In the case of large datasets, the ownership of the data (i.e. which modules create, respectively destroy the datasets) is usually provided by reference counted mechanisms hard-coded in the system kernel. If one desires to support user-defined, kernel-independent data types, this solution is however not possible. The addition of the read/write port attribute solves the above problem, as it will be shown in section 3.3.4. In this section, an overview of the port attributes is given. The type of a port represents the type of values that can be read or written from, respectively to that port. Since metaclasses represent only component interfaces to C++ components, port types are native C++ types, such as integer, float, pointer, or class types, passed by value or by reference. The semantics of data passing is identical to the C++ one. For example, a port that has a reference C++ type such asint*orfloat&is going to read or write just a reference to a value, not the value itself. A port that has a value C++ type such asdoubleorDataSet(whereDataSetis some C++ class type) will read or write a copy of the passed data value, not the passed value itself. If the port is of a class type that has a copy constructor, this will be invoked at the data transfer just as it is done during an usual C++ value transfer.

The input/output port attribute represents the conceptual direction in which data flows related to the port’s metaclass. As previously mentioned, a dataflow application is a directed graph whose nodes are the metaclasses and arcs are port-to-port connections, or links. The port input/output attribute de- termines the direction of links that may end at that port. Links are thought to emerge from output ports (shortly outputs) and enter into input ports (shortly inputs). The sense of the links determines thus the direction in which the dataflow network is traversed and consequently the order in which its compo- nents compute, or update. Figure 3.7 a shows the graphical representation of a metaclass. The upper and lower rectangles represent the input and output ports respectively. This representation is similar to the one used by most dataflow systems that depict components visually.

In most cases, the data transfer that actually takes places follows the links’ directions, i.e. data is read from outputs and written to the inputs. However, as we shall soon see, this is not always the desired situation.

The read/write port attribute represents the actual direction in which data is transferred related to the port’s metaclass. A read port is thus a port from which data can be read. A write port is a port

into which data can be written. We distinguish two kinds of write ports: read-write or write-only. A

read-write port is a write port which can also be read. Conceptually, the value read from a read-write port is the last value written into it. A write-only port is a port which can be only written, but not read. Since read-write ports are used in most concrete cases, we shall call them shortly write ports and use the read-write and write-only terminology only when the context requires this distinction. Ports are usually output and read, respectively input and write. However, input read and output write ports may exist as well, as mentioned above.

Two ports can be connected by a link if all the following hold:

the port C++ types are compatible, in the sense of C++ type compatibility.

one port is read, the other one is write.

Data is transferred across a link by being read from the link’s read port and written into the link’s write port. This is why a read-write port pair is needed to make a link. After a metaclass updates, it will transfer data across all the links connected to its outputs. The metaclasses whose inputs are connected to these outputs will update as well, and so on. This is where the distinction between inputs and outputs comes into play.

Figure 3.7 b shows two connected metaclassesAandB. Data read fromA’s output out flows across the link and is written intoB’s input inp. This is the most common situation, where data ’naturally’ flows along the link’s direction, from outputs to inputs. Most dataflow systems such as AVS, Inventor, Oorange, VTK, Khoros, IRIS Explorer support only this case, where the sense of the traversal of the dataflow network coincides with the sense of the data transfer. This is indeed natural for the case when data is transferred by value.

However, suppose thatAwants to directly modify the contents ofB, e.g. by writingB’s data mem- bers or calling its methods. In this case,Awill hold a reference or pointer toBwhich is set whenAand Bare connected. In order to do this, a reference toBshould be written intoA. This is whyA’s out port is of write type andB’s port inp is of read type. When A updates, it directly modifies B via its stored reference. This is whyA’s out port is of output type, andB’s port inp is of input type. Figure 3.7 c shows the above example.

The separation of the input/output and read/write notions corresponds actually with two different flows in the network, i.e. the data and control flow. The data flow represents the way data is read and written between components, and corresponds to the read/write port attribute. The control flow represents the order in which components are updated during network traversal, and corresponds to the input/output attribute. The two senses coincide when data is transferred by value. However, as we have seen, direct modification of components via references implies a data transfer sense opposite to the network traversal sense. This issue is further detailed in section 3.3.4).

Metaclass A

Metaclass B

output read port out

input write port inp

data transfer across this link involves: write to inp the data read from out

Metaclass A

Metaclass B

output write port out

input read port inp

data transfer across this link involves: write to out the data read from inp a) b) c)

Metaclass A input ports

output ports

Figure 3.7: Metaclass representation (a). Data transfer from a read output to a write input (b) and from a read input to a write output (c)

When a port is read or written, the actual operation of reading or writing data is delegated by the metaclass ports to its C++ class implementation. To illustrate the above, an example follows showing how a metaclass is created out of a C++ class.

Metaclass Example

Suppose one wants to create a metaclass for a C++ classPolyArea(Fig. 3.8).PolyAreacomputes the area of a regular n-sided polygon, being given the polygon’s edge length and the number of edges. For this,PolyArea’s public interface has an integer data memberedgesto specify the number of edges, and a method-pairsetSize,getSizeto set, respectively get the edge size as a float value. The polygon’s area is computed by the methodcomputeArea()and stored in the private member area. To query this computation, a methodgetAreais provided. The class provides also a con- structor to initialise its state, i.e. the data memberssize,area, andedgesto the zero default value.

class PolyArea {public:

PolyArea(): edges(0),size(0),area(0) {} int edges;

void setSize(float s) { size = s; } float getSize() { return size; } double getArea() { return area; }

void computeArea() { compute area as function of size and edges } private:

float size; double area; };

Figure 3.8: Simple C++ class to compute the area of a regular, n-sided polygon

C++ does not specify which of a class’ members are ’inputs’ and which are ’outputs’, as it does not have the dataflow notion. However, in order to make a dataflow component out ofPolyArea, we must identify which are the conceptual inputs and outputs through which this class communicates with the outside world. In the above case, bothedgesand thesetSize,getSizemethod pair control the class’ data inputs, as they provide reading or writing of the parameters on which the polygon’s area depends. PolyAreahas only one output, namelygetAreawhich returns the area of the specified polygon.

Figure 3.9 shows the MC++ code that declares aPolyAreametaclass with two write input ports ”number of edges” and ”edge size” and one read output port ”area”. The MC++ code forPolyArea

module PolyArea {input:

WRPortMem "number of edges" "int" (edges)

WRPortMeth "edge size" "float" (setSize,getSize)

output:

RDPortMeth "area" "double" (getArea) }

Figure 3.9: Metaclass declaration for the C++ classPolyArea

illustrates a couple of important MC++ constructs. First, a metaclass is a declaration similar to a C++ class or structure declaration. It starts with themodulekeyword, followed by the name of its imple- mentation C++ class, and the metaclass body between braces. The metaclass body contains the dec- larations of the metaclass features. In the above example, two input ports and one output port were

declared. Port declarations fall into an inputoroutput access category, specified by the same keywords. All port declarations following aninputspecifier up to the metaclass body end or to a subsequent access specifier are input ports. The same holds for theoutputaccess specifier. Port declarations coming before any specifier are assumed by default to be input declarations.