Chapter 8. Interaction with C

Table of Contents
External Global Names
External Types and Values in ATS
Inclusion of External Code in ATS
Calling External Functions in ATS
Unsafe C-style Programming in ATS
Exporting Types in ATS for Use in C
Example: Constructing a Statically Allocated List

ATS and C share precisely the same native/flat/unboxed data representation. As a consequence, there is no need for wrapping/unwrapping or boxing/unboxing when calling from C a function implemented in ATS or vice versa, and there is also no run-time overhead for doing so. To a large extent, ATS can be considered a front-end to C that is equipped with a highly expressive type system (for specifying program invariants) and a highly adaptable template system (for facilitating code reuse). In particular, ATS can often be effectively employed to turn a large task into subtasks of coherent interfaces, which can be implemented in ATS, C or some other langauges and then assembled together to form a solution to the orginal task.

As can be expected, C code that appears directly in ATS does not go through the kind of rigorous typechecking like ATS code should. So it is recommended that the programmer be extra cautious when making direct use of C code inside ATS code. In practice, my own experience clearly indicates that the portion of C code inside my ATS code is highly likely to be the culprit for most of encountered bugs.

The code employed for illustration in this chapter plus some additional code for testing is available on-line.

External Global Names

A function declared in ATS can be given a global name of C-style so as to allow the function to appear in both ATS code and C code. In particular, the function can be implemented in ATS and called in C or vice versa.

In the following code, we see that two functions are declared:

extern fun fact (n: int): int extern fun fact2 (n: int, res: int): int = "ext#fact2_in_c"

The first function fact does not have a global name while the second function fact2 is assigned a global name fact2_in_c. The symbol ext# indicates that fact2_in_c is treated as a global function in C and its prototype needs to be declared (via the extern keyword) before it can be called. If ext# is written in place of ext#fact2_in_c in the above declaration, then the global name for the function fact2 in ATS is assumed to be same as the name of the function in ATS. In other words, writing ext# in the above declaration is equivalent to writing ext#fact2.

Let us assume that fact can be implemented as follows:

implement fact (n) = fact2 (n, 1)

When compiling this implementation, the ATS compiler needs to form function names in the generated C code to refer to fact and fact2. For the former, the function name in the C code is determined by a set of rules (which take into account the issue of namespace). For the latter, the function name is simply chosen to be the assigned global name fact2_in_c. As is suggested by the name of fact2_in_c, this function can be directly implemented in C as follows:

fact2_in_c (int n, int res)
  while (n > 0) { res *= n ; n -= 1 ; } ; return res ;

It is also allowed to implement fact2 in ATS directly as is shown below:

implement fact2 (n, res) = if n > 0 then fact2 (n-1, n*res) else res

This implementation of fact2 can be called in C through the name fact2_in_c.

If both fact2 and fact2_in_c are implemented (the former in ATS and the latter in C), then a link-time error is to be issued to indicate that fact2_in_c is implemented repeatedly.

One can also declare fact2 as follows:

extern fun fact2 (n: int, res: int): int = "mac#fact2_in_c"

The symbol mac# indicates that fact2_in_c is treated like a macro in C. In particular, fact2_in_c can be called without its prototype being declared first. As a matter of fact, it may not even have a prototype. This style of declaration naturally expects fact2_in_c to be implemented in C directly.

It is also allowed to use sta# in place of mac#:

extern fun fact2 (n: int, res: int): int = "sta#fact2_in_c"

If declared in this style, which only occurs rarely in practice, then fact2_in_c is treated like a static function in C.

For the sake of completeness, I mention as follows another way of declaring a static function:

static fun fact2 (n: int, res: int): int

This style of declaration is automatically translated into the following one:

extern fun fact2 (n: int, res: int): int = "sta#"

where the use of sta# means that the name referring to fact2 in C is simply fact2.