Syntax of CppFBP (C++/Boost Implementation of FBP)
and Component API


Under Construction!

The THREADS package was converted to use Windows "fibres" and C++ a few years ago. However, fibres did not seem sufficiently general, so this package is now in process of being converted to use Boost, to take advantage of Boost multithreading. The earlier, fibres-based, version is still available as a zip file.

Contents

General

CppFBP is an enhanced version of the older C-based THREADS software, and has two different ways of defining networks:

  • "fixed form" - this defines a network as a linked set of C structures. Unlike the JavaFBP and C#FBP conventions, these are not the control blocks used by CppFBP to run, but are used to initialize them at run-time. This separation is intended to allow changes to be made to the CppFBP internals, without requiring all users to change their network definitions.

  • "free form" - this is a text-based notation, originally devised by Wayne Stevens, and now generally referred to as ".fbp notation", or the FBP DSL. The NoFlo project has adopted this notation, but also allows end-of-line to indicate the end of a "clause" (in addition to the comma).

A program is included, named Thxgen, which can be used to convert free form network definitions into (runnable) fixed form definitions. However, the free form notation can also be run directly, if you are willing to pay the price of decoding the free form every time.

Sample Component

Under Visual C++ 2010 Express, for components, you will need to add the CppFBPCore/Headers file to Configuration Properties/C/C++/Additional Include Directories - plus $(LUA_INCLUDE) if you are going to be working with Lua.

For networks, you will need to add the CppFBPCore/lib file to Configuration Properties/C/C++/Additional Include Directories for your component - plus $(BOOST_INCLUDE). In addition, you will need to add the CppFBPCore/lib file and and CppFBPComponents/lib file to Configuration Properties/Linker/Additional Library Directories - plus $(BOOST_LIB).

Here is a simple component written in C++ to copy IPs from IN to OUT. Let's call this rather unoriginally ThCopy.


#include "StdAfx.h" #include "dllheader.h" #include #include "compsvcs.h" // This component simply makes a shallow copy, drops the original, and // sends out the copy THRCOMP ThCopy(_anchor proc_anchor) { void *ptr; void *ptr2; int value; long size; char *type; port_ent port_tab[2]; value = dfsdfpt(proc_anchor, 2, port_tab,"IN","OUT"); value = dfsrecv(proc_anchor, &ptr, &port_tab[0], 0, &size, &type); while (value == 0) { value = dfscrep(proc_anchor, &ptr2, size, type); memcpy(ptr2,ptr,size); dfsdrop(proc_anchor, &ptr); value = dfssend(proc_anchor, &ptr2, &port_tab[1], 0); if (value != 0) { dfsdrop(proc_anchor, &ptr2); return(1); } value = dfsrecv(proc_anchor, &ptr, &port_tab[0], 0, &size, &type); } return(0); }

The first action is always to initialize the port table using a call to dfsdfpt - the 2nd parameter of the call to dfsdfpt must match the dimension declared for the port table. This number must also match the number of port names specified in the parameters to dfsdfpt . Note: dfsdfpt modifies the port table, so this must not be defined as constant.

The return statement causes deactivation (but not necessarily termination, as described above). The return code value on the return statement determines whether the process is willing to be reactivated: a value of 5 or greater forces termination even if data is available.

API Calls

Here are the API calls available to components in CppFBP:

Create a Packet:
value = dfscrep(proc_anchor, &ptr, size, type);

Define ports:
value = dfsdfpt(proc_anchor, port_count, port_tab);

Receive an IP:
value = dfsrecv(proc_anchor, &ptr, &port_tab[port_no],
elem_no, &size, &type);

Send an IP:
value = dfssend(proc_anchor, &ptr, &port_tab[port_no],
elem_no);

Drop an IP:
value = dfsdrop(proc_anchor, &ptr);

Pop an IP off the stack:
value = dfspop(proc_anchor, &ptr, &size, &type);

Push an IP onto the stack:
value = dfspush(proc_anchor, &ptr);

Close a port element:
value = dfsclos(proc_anchor, &port_tab[port_no],
elem_no);
Count of elements in port:
value = dfselct(proc_anchor, &port_tab[port_no]);
Length of IP in bytes:
long = dfsgsize(proc_anchor, &ptr);

All the port-related calls now have a character form, where the port can be referenced using a character string - this entails some extra overhead at run-time, but facilitates interfacing with scripting languages such as Lua. In these cases, the original service name is suffixed with a 'c'.

Parameters:

Note that the ptr, size, type and port table element parameters all have &'s attached, except in the case of dfscrep , where only ptr has it. The &'s are required because these parameters may be modified, and C parameters are all passed by value (except for strings and arrays). In some cases a particular service does not set them, but for consistency &'s are used for all of them (except dfscrep ).

port_count and elem_no are all binary integer variables (int). size is binary long. dfscrep is restricted to a maximum of 64000 bytes.

  • proc_anchor: a variable of type anchor (defined in thxanch.h)

  • ptr: a void pointer used to point at IPs

  • size: a long variable containing the IP's size

  • type: a null-terminated string of up to 32 chars

  • port_count: parameter to dfsdfpt - must match the dimension of the defined port_tab array for this component

  • elem_no: this specifies the element of the appropriate port to be used in a send, receive or close. These are only required for array-type ports, where they number up from 0. For non-array ports, this parameter must be 0.

  • port_tab: this parameter is declared as an array of type port_ent (see below), where each element corresponds to one of the ports known to the component; the whole structure is passed to dfsdfpt, while specific elements of port_tab are passed to send, receive and close using &.

  • port_name_n: port name to be used by dfsdfpt; the number of these must match the port_count parameter

After a call to dfsdfpt, elem_count in each port_ent instance will be set to the number of connected elements for that port, and ret_code will be set to 0 or 2, depending on whether that port was connected or not.

Service return codes:

dfscrep:
0 OK

dfsdfpt:
0 OK - but some elements of port_tab may have their
ret_code fields set to 2, meaning "port name not
found"; a receive or send from or to such a
port_tab element will result in a return code value
of 2.

dfsrecv:
0 OK
1 port element closed (end of data)
2 port element not defined or not connected

dfssend:
0 OK
1 port element closed
2 port element not defined or not connected

dfsdrop:
0 OK

dfspop:
0 OK
2 stack empty

dfspush:
0 OK

dfsclose:
0 OK
1 port element already closed
2 port element not defined or not connected

Limitations:

There is a limit of 4000 elements per array port.

Working storage for a component should not exceed a few thousand bytes (including the working storage of any subroutines it calls). If the component needs more than this, it should use one of C's dynamic allocation functions (e.g. malloc or calloc) to allocate the additional storage.

Network Notation

Fixed-Format Definition

As stated above, CppFBP supports two network definition notations: fixed form, and free form.

Here is an example of a network in fixed form notation. Note: the above structures are not the internal control blocks of CppFBP - they are an encoding of the free-form network specification notation. This separation will allow CppFBP to be extended in the future without requiring network definitions to be reprocessed.

#include "thxdef.h"

#include 
#define FILE struct _iobuf

/* This is not the CppFBP internal structure - this 
is a structured representation of the free-form connection list
*/
void  CppFBP(label_ent* label_blk, bool dynam,  FILE * fp, bool timereq);


THRCOMP ThFileWt(_anchor anch);
THRCOMP ThCopy(_anchor anch);
THRCOMP ThCopyNL(_anchor anch);
THRCOMP ThFileRt(_anchor anch);

#define TRACE true
#define COMPOS true
#define DYNAM true
#define TIME_REQ true

int cap = 2;
int elem0 = 0;


proc_ent P0 = {NULL, "Read", "ThFileRt", ThFileRt, NULL, NULL,  !TRACE, !COMPOS};
proc_ent P1 = {&P0, "Show", "ThFileWt", ThFileWt, NULL, NULL, !TRACE, !COMPOS};
proc_ent P2 = {&P1, "Copy", "ThCopyNL", ThCopyNL, NULL, NULL,  !TRACE, !COMPOS};
//proc_ent P2 = {&P1, "Copy", "ThCopy", ThCopy, NULL, NULL,  !TRACE, !COMPOS;

IIP I0 = {"C:\\Users\\Paul\\Documents\\Business\\THREADN\\POMPIERS.FIL"};
IIP I2 = {"C:\\Users\\Paul\\Documents\\Business\\THREADN\\output.fil"};
cnxt_ent C0 = {NULL, "!", "", 0, "Read", "OPT", elem0, &I0, 0};
cnxt_ent C1 = {&C0, "Read", "OUT", elem0, "Copy", "IN", elem0, NULL, cap};
cnxt_ent C2 = {&C1, "!", "", 0, "Show", "OPT", elem0, &I2, 0};
cnxt_ent C3 = {&C2, "Copy", "OUT", elem0, "Show", "IN", elem0, NULL, cap};


label_ent LABELTAB = {NULL, " ", "", &C3, &P2, 'L'};

void main() {
CppFBP(&LABELTAB, !DYNAM, NULL, TIME_REQ);  // time required
}

An exclamation mark ("!") in the upstream process name of a connection (cnxt_ent) indicates an IIP-type connection.

An asterisk ("*") as the port name of a connection indicates an automatic port (Chapter 13 of the 2nd Edition).

Free-Format Definition

The free-form alternative notation, an early form of which is described briefly in Chapter 22 of the 2nd edition, basically follows a "flow" style, where connections can be chained together until a process has no output ports connected. This constitutes the end of a "clause", and is indicated by a comma or end-of-line (in both NoFlo and CppFBP).

This free form notation can be converted to the fixed form notation, using Thxgen, or can be executed directly by CppFBP (interpretive mode).

Here is an example:

'data.fil'->OPT Reader(THFILERD) OUT -> IN Selector(THSPLIT) MATCH -> ... ,
Selector NOMATCH -> ...

The general syntax for free-form network definitions is quite simple, and can be shown as follows (using a variant of the notation which has started to become popular for defining syntax):


"EOL" ("end of line") indicates the alternative NoFlo convention for "end of clause". The mark above "EOL" is meant to be a comma.

Other symbols:

  • "Proc-name" represents a process name, optionally followed by the component name (in round brackets)
  • "Conn" represent an arrow, with optional capacity (in round brackets), e.g. (30)
  • "IIP" represents a quoted string (using single quotes) - any internal quotes must be escaped
  • "Up-port" and "down-port" are from the point of view of the connection - they could also be called "output port" and "input port", respectively

An asterisk ("*") as the port name of a connection indicates an automatic port (Chapter 13 of the 2nd Edition).

The main network may be followed by one or more subnets, which have basically the same notation (each one starting with a label and finishing with a semi-colon). However, subnets have to have additional notation describing how their external port names relate to their internal ones. Since this association is like an equivalence, we use the symbol => to indicate this relationship. Thus,

, port-name-1 => port-name-2 process-A port-name-3,

indicates that port-name-1 is an external input port of the subnet, while port-name-2 is the corresponding input port of process-A. Similarly,

, port-name-1 process-A port-name-2 => port-name-3,

indicates that port-name-3 is an external output port of the subnet, while port-name-2 is the corresponding output port of process-A.

The component name can be specified on any occurrence of the process name. This can then be followed optionally by a question mark to indicate that tracing is desired.

In Chapter 12 of the 2nd edition we alluded to the fact that we can specify that a process "must run at least once" - this is specified by means of an attribute file for the component, which has the name of the component, and an extension of atr, e.g. thcount.atr. These files must be provided by the supplier of a component, and must have a specific format. So far, only the "must run" attribute has been defined - it is specified by coding one of the character strings must_run, Must_run or MUST_RUN, with optional preceding blanks, in the attribute file for a given component. If no attribute file is found for a component, default values are assumed to apply (the default for the "must run" attribute is "need not run").

The fixed form network definition notation is a data-only C++ program, and specifies a network, together with all referenced subnets. Its importance is that this will be the source form for networks owned by customers, so CppFBP must guarantee that any enhancements to it will be upwards compatible. These guarantees are essentially encoded in the C headers which it uses: thxiip.h, thxanch.h and thxscan.h.