QuickNote: Connecting two ports

Like in QN: Sending Events, we collect here some lines/fragments about connecting two ports in the handCompiled code to find similarities, and to design the (Jinja) templates.

See also

  • .../SIEVE/1.WorkCopy/CC-event-sieve.Castle-handCompiled.c (also on OSDN)

Hint

The Castle connect operator (=) is symmetrical. One connects two ports, without any order.
So, a=b and b=a have exactly the same behaviour.

Still, in the code below, we use the out = in order, which is advised. The real compiler will check this, and silently adapt the generated code when the order is reversed.

Collected Code fragments

The Sieve (basic variant) has only a few (kinds of) connections:

  1. Between the Generator and (the first) Sieve-element;

  2. Between two Sieve-elements;

  3. Between (the last) Sieve and the Finder; and

  4. Only at the start, the Generator is connect with the Finder, as there are initially no Sieves (aka primes found).

In this variant of the code, all those connections are made in the Main component. In other variants some code is located in the Finder, but that has no effect on the generated code (except for prefixed and generated function-names).

CC_Mi_Main__init

This connects the output-port outlet of the Generator with the Finder’s input-port newPrime; to start sieving.

.generator.outlet = .finder.newPrime;  // ``.port`` is short for self.port

Generated

{
struct CC_B_OutPort * outlet = &(((CC_C_Generator*)(self->generator))->outlet);      // link to port
outlet->connection           = self->finder;                                         // connection is to comp, not port!
outlet->handlers             = cc_S_Finder_newPrime;                                 // a hardcoded, generated `CC_B_eDispatchTable`
}

CC_E_Main__SimpleSieve_try__generator_found

Connection the new Sieve-element.
Both it’s output-port (coprime) to the Finder’s input (newPrime).

s.coprime = self.finder.newPrime; // s: alias to the newly created Sieve-Element

And it’s input-port (try), typically to the output of the last Sieve, but initially to the output of the Generator.

self.lastSieve.coprime = s.try;
self.generator.outlet = s.try;

Generated

{
struct CC_B_OutPort * coprime        = &(((CC_C_Sieve*)s)->coprime);
coprime->connection                  = self->finder;
coprime->handlers                    = cc_S_Finder_newPrime;
}
{
struct CC_B_OutPort * coprime        = &(((CC_C_Sieve*)(self->lastSieve))->coprime);
coprime->connection                  = s;
coprime->handlers                    = cc_S_Sieve_try;
}
{
struct CC_B_OutPort * outlet        = &(((CC_C_Generator*)(self->generator))->outlet);
outlet->connection                  = s;
outlet->handlers                    = cc_S_Sieve_try;
}

Analyse

The generated code above assumes a cohesive software deployment; where the “out-port” can view the “in port”. This depends on The Machinery (ToDo), like in the (above used) DirectCall, and in the LibDispatch. In others, like DDS, this may not be the case, and the code may need to change.

Still, the generated code hardly uses this cohesive aspect!
It finds the OutPort, which is either ‘in’ self, or in a sub-component (like .generator and .finder in Main__init). And update it’s two fields:

  1. It fills-in the reference (here/in C: a pointer) to the element (not component) that holds the in-port. And,

  2. Links (C: write a pointer) to the dispatchTable of the receiving Component (not element).

Not, it does not need to access (read/write) the in-component!
It only compose the names. For example, the name of the dispatchTable is based on the name of the Component, and the name of the Port. The event-handlers (the functions) itself are not needed! Both names are available in the CastleCode!

There is a bit of magic however.
A components can be an alias –which is generic– like s. Then, we can only generate the (dispatchTable) name when we know the real type!

But as that alias is typically created (or set) a few lines above, we can life with that (for now).

Templating

Below we give in the same style as in “QuickNote: Sending Events (generated code)” some Jinja templates. In the comments we list the input (variables) with some examples as used above.

Jinja macros

We can generate the name of the DispatchTable when we know the names of the component and the port. That is, the name of the Component, as set in the CastleCode (So, the name of the Component, not the instance).

{#
# inComp -- Finder, Sieve,
# inPort -- newPrime, try,
#}
{%macro dispatch_table(inComp, inPort) -%}    cc_S_{{inComp}}_{{inPort}}     {%- endmacro %}

Template

The C-code to make the connection is quite straightforward. We need an alias/pointer to the outgoing port; we like to use the name of the outport for that – although we can use a static name. The value is always (de)referencing the outgoing Port on the outgoing Component; but we need to add a little casting, and (optionally) a few “pointer-steps” to that Comp – that is called the outCompPath. Later, we will use macros to calculate it.

That CC_B_OutPort has two values, that need to be set – that are the last 2 lines. The connection-field is the incoming component (Note: the Component, not the Port), and the handlers is pointer to the corresponding DispatchTable; as calculated above.

// Input jinja-marco’s:
////  outPort (name) -- outlet, coprime
////  outCompType    --  CC_C_Generator, CC_S_Sieve
////  outCompPath    -- self->generator,  self->finder, self->lastSieve
////  inCompPath     -- self->finder, s [as alias]
////  inComp (name)  -- Finder, Sieve
////  inPort (name)  -- newPrime, try

{
struct CC_B_OutPort * {{outPort}}    = &((({{outCompType}}*)({{outCompPath}}))->{{outPort}});
{{outPort}}->connection              = {{inCompPath}};
{{outPort}}->handlers                = {{dispatch_table(inComp, inPort)}};
}

Comments

comments powered by Disqus