Skip to content

Host code

Calling C or Java from a chart: text in backticks, extern functions, #hostcode and its relatives, host types, and what host code means for the simulation.

Some things are beyond SCCharts’ own language: a function from a C library such as abs, a 64-bit integer, a helper you have already written in C. For these, a chart can contain host code: text in the target language, the host language, C or Java, that KIELER copies into the program it generates. From chart to C shows what that program looks like.

The bluntest form of host code is text between backticks. KIELER pastes it into the generated code unchanged: as an effect it is a statement, inside an expression it is a value.

BackticksChart

Diagram

Diagram of BackticksStates: Run (initial). Transitions: Run to Run: / d->x = 42;; y = 7 * 6.Backticksoutput intx = 0, y = 0Run/ d->x = 42;;y = 7 * 6

Code

scchart Backticks {
output int x = 0, y = 0
initial state Run
do `d->x = 42;`; y = `7 * 6` go to Run
}
One backtick effect sets x in C; one backtick expression computes y.

KIELER’s diagram prints host code without its backticks: the label reads d->x = 42;; and y = 7 * 6. The first ; belongs to the C statement, the second separates the two effects. In KIELER’s C simulation:

Tick State after x y
1 Run 0 0
2 Run 42 42

In the generated C every variable of the chart is a field of a struct that the code reaches through a pointer d, so the C statement writes d->x (see From chart to C).

The compiler does not look inside backticks. It cannot see that `d->x = 42;` writes x, so the causality checks of chapter 12 do not cover it, and the chart only works with the one language the text is written in. Keep backticks for the edges of a system, if you use them at all.

The better way to call a host function is to declare it:

DistanceChart

Diagram

Diagram of DistanceStates: Track (initial). Transitions: Track to Track: / distance = abs(position).Distanceinput intpositionoutput intdistance = 0externabsabsTrack/ distance = abs(position)

Code

#hostcode "#include <stdlib.h>"
scchart Distance {
input int position
output int distance = 0
extern "abs" abs
initial state Track
do distance = abs(position) go to Track
}
extern "abs" abs makes C’s abs callable as abs(...).
Tick position distance
1 −5 0
2 −6 6
3 7 7
  • extern "abs" abs declares a host function: the string is its name in the host language, the second name is what the chart calls it. Call it with parentheses, abs(position). The compiler sees the call and its arguments, so the arguments take part in the causality analysis.
  • One declaration can name the function for each language: extern @C "abs", @Java "Math.abs" absolute works in both a C and a Java simulation, provided the C include goes into #hostcode-c rather than #hostcode (below says why).
  • extern abs(position) inside an expression calls a host function without declaring it first. The KIELER wiki calls this form deprecated.

Includes, type definitions and helper functions go into pragmas: lines starting with # at the top of the file, before the first scchart. Their strings are pasted at the top of the generated files:

  • #hostcode "...": every generated file, the C header, the C file and the Java file;
  • #hostcode-c "...": the C file only;
  • #hostcode-c-header "...": the C header only;
  • #hostcode-java "...": the Java file only;
  • #resource "helper.c": nothing is pasted; the file is copied next to the generated code, and .c files are compiled with it.

The C file includes the header, so text in #hostcode reaches the C compiler twice. That is harmless for #include lines and type definitions, but a function defined there is defined twice: gcc says redefinition of ‘clamp’. Function bodies go into #hostcode-c:

GaugeChart

Diagram

Diagram of GaugeStates: Show (initial). Transitions: Show to Show: / needle = clamp(reading, 0, 100).Gaugeinput intreadingoutput intneedle = 0externclampclampShow/ needle = clamp(reading, 0, 100)

Code

#hostcode-c "static int clamp(int v, int lo, int hi) { return v < lo ? lo : (v > hi ? hi : v); }"
scchart Gauge {
input int reading
output int needle = 0
extern "clamp" clamp
initial state Show
do needle = clamp(reading, 0, 100) go to Show
}
clamp is defined in #hostcode-c, declared with extern and called like any function.
Tick reading needle
1 42 0
2 250 100
3 −7 0
4 42 42

Since #hostcode text also lands in the Java file, a C #include there breaks the Java version of the chart. If a chart must work in both languages, use #hostcode-c and #hostcode-java.

#resource pulls in whole files. A header used by a resource needs its own #resource line, or gcc will not find it:

#resource "scale.h"
#resource "scale.c"
#hostcode "#include \"scale.h\""

host "int64" big declares a variable whose type is a host-language type, written as a string. The C compiler has to know the type, so it usually comes with a typedef or an #include in #hostcode:

#hostcode "typedef long long int64;"
...
host "int64" big = `1LL << 40`

Without the typedef, gcc reports unknown type name ‘int64’. Host-type variables work in the generated code but are not part of the simulator’s data pool, so you will not see big in a trace.

One more place where C shows through: KIELER’s C code stores a string as a C pointer, and == compares pointers, not text. An input string never equals a literal, whatever it contains. In SCCharts Lab gcc warns about it (official KIELER does not show the warning):

comparison with string literal results in unspecified behavior [-Waddress]

Java simulations compare the text. Use an int code or an enum instead of a string when a chart has to compare values (see chapter 5).

Clamp it yourself

Gauge calls a C function, clamp, so it runs only where KIELER can compile C, and not on this site. Replace the #hostcode-c line and the extern declaration with a method of the chart that does the same, so that Gauge behaves exactly as before and also runs in the playground. Test it with readings above 100 and below 0.

Show the starter chart
#hostcode-c "static int clamp(int v, int lo, int hi) { return v < lo ? lo : (v > hi ? hi : v); }"
scchart Gauge {
input int reading
output int needle = 0
extern "clamp" clamp
initial state Show
do needle = clamp(reading, 0, 100) go to Show
}
Show a solution
scchart Gauge {
input int reading
output int needle = 0
method int clamp(int v, int lo, int hi) {
if (v < lo) {
return lo
}
if (v > hi) {
return hi
}
return v
}
initial state Show
do needle = clamp(reading, 0, 100) go to Show
}
#hostcode "static int twice(int v) { return 2 * v; }" makes gcc report redefinition of ‘twice’. Why?
Show the answer

Answer D: #hostcode text goes into the generated header and the C file, and the C file includes the header

#hostcode is pasted into the header and the C file, and the C file includes the header, so the compiler sees the definition twice. Put function definitions into #hostcode-c, which goes into the C file only.

A chart declares extern "abs" absolute and has the effect distance = absolute. What happens?
Show the answer

Answer B: KIELER warns that the call syntax is missing, and then gcc fails

Without parentheses the name is not a call. KIELER only warns that the external reference is used without call syntax and generates the code anyway, and then gcc fails: ‘absolute’ undeclared (first use in this function). Write the call, absolute(x).

Key ideas

  • Host code is C or Java text that KIELER copies into the generated program. It needs a real compiler, so this site can draw such charts but not run them. Prefer a method when one can do the job.
  • The compiler does not look inside backticks, so it cannot see what they read or write.
  • Declare host functions with extern "name" localName and call them with parentheses.
  • #hostcode text goes into every generated file, function definitions into #hostcode-c, and #resource copies whole files next to the generated code.
  • A host type such as host "int64" needs its definition in #hostcode, and it is not in the data pool.
  • In KIELER’s C code, strings compare as pointers.

Next, From chart to C: what the compiler generates from a chart, and how a main loop on a microcontroller drives it.

Report a mistake on this page