QuickNote: Jinja Events (templating)
As we have seen in the recent Workshop blog: QuickNotes a lot of code (to send/handle events) is always the same, but for some details. A template engine, like Jinja, can help in that.
Let’s study how those templates can help us.
See also
The study of generated code in: QuickNote: Sending Events (generated code)
.../SIEVE/1.WorkCopy/CC-event-sieve.Castle-handCompiled.c
(also on OSDN)
Events, Eventshandlers & code
A (Castle) event is always part of a (Castle) protocol. Only the combination of the names of both the Protocol and the Event leads to a uniquely generated name. (for C/rPy/… code). To be used in an EventHandler (within a component, for a port).
Events in Protocols
Besides –and especially for C–, we need a functiontype (aka prototype) and a handler/dispatch-table index (as a constant).
protocol StartSieve : Protocol {
...
runTo(int:max);
...
}
typedef void (*CC_E_StartSieve_runTo_FT)(CC_selfType, CC_ComponentType, int);
#define CC_P_StartSieve_runTo 42 // demo-no
// The index-number is calculates by (e.g.) the number of *inherit* events plus literal index of protocol
Eventshandlers
We also need the event handler implementation, which is component and port specific. Here we focus only of the name of both the Castle and the generated (C-) function.
implement Generator {
...
StartSieve.runTo(max) on self.controll
{...}
void
CC_E_Generator__StartSieve_runTo__controll(CC_C_Generator* self, CC_OutPortType sender, int max)
{ ... }
Jinja macros
Below, we use a 3-step approach:
Combine the protocol and event into an protocol_event; that is the base to
generatedboth the event_FT (function-proto-type) and the event_no index (number constante). And we use it to
generate the name of the event_handler (C-) implementation
{%macro protocol_event(protocol, event) -%}
{{protocol}}_{{event}}
{%- endmacro %}
{%macro event_FT(protocolEvent) -%} CC_E_{{protocolEvent}}_FT {%- endmacro %}
{%macro event_no(protocolEvent) -%} CC_P_{{protocolEvent}} {%- endmacro %}
{%macro event_handler(compName, protocolEvent, portName)
CC_E_{{compName}}__{{protocolEvent}}__{{portName}}
{%- endmacro %}
Caution
The code above is not fully designed. We have to chain the part to make it work. Now the are freewheeling, independer, but readable piece.
We use macro_names and marcoNames. The former is a (jinga) function, the latter act as an argument. When the
are otherwise the same, the results of the function call is stored in the variable, and passed into the next marco as
parameter.
But we assume you understand that, and are able to add the plumbing (or combine/inline them – convenient, but less
readable!
Generating C-Code
Here we use the above marco’s (without plumbing) to generate parts of the code. Other pieces are (for now) hardcoded. That will change later.
Sending
CastleCode
self.generator.controll.runTo(max);
Template
// Input jinja-marco’s:
/// (Aside of the ones given above)
/// All values are valid C-code snippets
//// my_comp: “self”
//// path2port: “generator->control”
/// args: <list of arguments>
void*
CC_E_Main__powerOn__power(CC_Main* self,
CC_OutPortType sender,
int max)
{
...
{
struct CC_B_OutPort outport = {{my_comp}}->{{path2port}};
CC_ComponentType receiver = outport.connection;
CC_B_eDispatchTable handlers = outport.handlers;
{{event_FT(protocolEvent)}} signal = ({{event_FT(protocolEvent)}})handlers[{{event_no(protocolEvent)}}];
signal(receiver, (CC_selfType)self, {{args}}); // args: max
}
...
};
EventHandler
CastleCode
StartSieve.runTo(max) on self.controll // in Generator
Template
// Input jinja-marco’s:
/// (Aside of the ones given above)
/// All values are valid C-code snippets
/// typedParms: <list of parameters with types>
void
{{eventHandler}}(CC_C_{{compName}}* self, CC_OutPortType sender, {{typedParms}})
{...}
Comments
comments powered by Disqus