Calling C functions from Clean

1. INTRODUCTION

This document describes how functions written in the programming language C can be called from Clean using a tool called htoclean. htoclean translates a specification containing both C and Clean definitions of function types, types and constants into Clean functions which directly call the corresponding C function. This specification is stored in a .h file, which can be #included in a C program just like a normal C header file. An example of such a specification is:

int add (int v1,int v2);

This is the prototype of a C function 'add' that has two arguments of type int and a result of type int. The file example_int2.h in the Examples folder that is included with this document (or see the appendix ) contains this function prototype. To be able to call this function from Clean, we use htoclean.

1.1 USING HTOCLEAN

On windows and unix, htoclean is a command line tool and is invoked by cd-ing to the Examples directory and typing (with htoclean in your PATH):
htoclean example_int2.h
or (without .h)
htoclean example_int2
On the PowerMacintosh, the file selecter is used to indicate the .h file that is to be translated.

htoclean reads this .h file and generates the files: example_int2.dcl and example_int2.icl. The generated module contains the definition of the Clean function:

add :: !Int !Int -> Int

that calls the add function written in C.
The file example_int1_c.c contains a definition of add that adds these numbers. A C compiler is used to compile this .c file, and the object file generated by the c compiler is linked with the code generated by Clean to create an executable file. How to do this is explained in chapter 3.

2. HTOCLEAN FEATURES

2.1 COMMENTS

The .h definition file may contains comments. Both '/* comment */' and '// comment' are allowed. '//' comments are not allowed in ansi C, but some compilers support it. '/* */' comments should not be nested.

2.2 INTEGERS

C function types using int arguments and results may be specified in the .h definition file. For an example,.see example_int1.h in the Examples folder.

2.3 POINTERS

Pointers to int, double, void or structs, and pointers to pointers, may be passed to and returned by C functions. In Clean these pointers all have type Int. This is possible because all current implementations of Clean use 32 bit pointers and integers. For example (see example_int2) :

int *store_int (int *p,int offset,int value); /* p [offset] = value */

In Clean this function has type:

store_int :: !Int !Int !Int -> Int

2.4 CLEAN TYPES IN THE .H SPECIFICATION FILE

The type of a function in Clean may be added to the .h file, by writing:

Clean ( clean_function_type )

for example:

Clean (store_int :: Int Int Int -> Int)

To hide these types from the C compiler we use the C preprocessor. We define
#define Clean(a)
in the file Clean.h and include Clean.h.

Annotations (like ! and *) are not allowed in these function types. Since unevaluated objects cannot be passed to or returned by C functions, htoclean automatically adds strictness annotations (!) to all arguments and data structures, so there is no need to specify !'s in types. * is currently not allowed, but objects can be made unique using type synonyms.

2.5 TYPE SYNONYMS

Type synonyms can be defined with:

Clean ( :: TypeSynonym :== type )

The type may be an Int,tuple or type synonym. The type synonym can be made unique by prefixing the name with a '*'. Several function types and type synonyms can be written with one Clean( .. ) definition by separating the definitions by a ';'. For example:

Clean (
    :: *State :== Int ;
    :: IntPointer :== Int
 )

A type synonym can be used before its definition (and after its definition as well of course). In the type of store_int we can now show which parameters are pointers:

Clean (store_int :: IntPointer Int Int -> IntPointer )

2.6 ADDING ADDITIONAL PARAMETERS AND RESULTS IN CLEAN

Functions written in C often have side effects. It is therefore often necessary to execute these functions in a specific order. Because Clean is lazy, and because the order of evaluation is not defined, this is not easy in Clean. For example, in example_int2_main.c we use the guard | s==s to evaluate s before deallocating the allocated memory.
A better way is to specify the order in which to execute C functions by passing a unique state. This is made possible with htoclean by adding additional arguments and return values, that are hidden in C. This is specified by adding extra arguments at the end of the arguments list, and extra return values, also at the end. The number of extra arguments and return values should be the same, and the arguments and results should be passed in the same order, and each extra argument should have the same type as the corresponding return value. For example, we can use:

Clean (store_int :: IntPointer Int Int State -> (IntPointer,State) )

to add a State argument and result. The type of the store_int function in C does not have to be changed. By adding this State to all functions in example_int2.h, we obtain example_int3.h (see the Examples folder or the appendix). Now the guard | s==s is no longer required, because s is always evaluated before my_free is called, because of the (strict) state parameter. Unfortunately, we still need a guard to force the evaluation of my_free, because the Start function does not return the state (see example_int3_main.icl).

We could decide to remove the IntPointer result from store_int, because it has the same value as the first argument. Then the type of store_int changes to:

void store_int (int *p,int offset,int value);

Notice that the type of the result is void. So now we can also call C functions that do not return a value. This is not possible without adding additional parameters, because in Clean all functions yield a result.
Using an infix macro, we can even hide the state parameters and result and write the store_int calls in the right order (see example_int4_main.icl).

2.7 TUPLES

Tuples can be passed to C functions by specifying a tuple with Clean ( ). Such a tuple is unboxed before the C function is called. So the C function is not passed a tuple, but the elements of the tuple. For example:

int f (int tuple_element1,int tuple_element2);
Clean (f :: (Int,Int) -> Int)

A function can also return tuples, as we have already seen in the previous section. In C the elements of the tuple are returned using pointers. For each element in the tuple, an argument is added at the end of the arguments of the C function, that contains the address of the bytes on the stack where the result has to be stored. For example (see example_tuple1.h):

Clean (::ComplexInt:==(Int,Int))
void make_complex_int (int re,int im,int *re_p,int *im_p);
Clean (make_complex_int :: Int Int -> ComplexInt)

The function is implemented as:

void make_complex_int (int re,int im,int *re_p,int *im_p)
{
    *re_p=re;
    *im_p=im;
}

The first element of the tuple may also be returned as the result of the function instead, but not if it is a String.

2.8 STRINGS

Strings can be passed to and returned by C functions. The type of these strings in C is the type CleanString. In Clean ( ) type specifications the predefined synonym type String has to be used, and not {#Char}. {#Char} is used for CleanCharArray (see below). The type CleanString is defined in the file Clean.h, and is a pointer to a 32 bit integer containing the number of characters in the string, followed by the characters (8 bit,unsigned). The string does not end with a '\0' character. So:

struct {
    int    clean_string_length;
    char    clean_string_characters[clean_string_length];
} *CleanString;

The following macros are defined in Clean.h to access CleanStrings:
CleanStringLength (clean_string)
    number of characters in the clean_string.
CleanStringCharacters (clean_string)
    a pointer to the characters of the clean_string.
CleanStringVariable (clean_string,string_length)
    defines variable clean_string with length string_length,
    before using the clean_string variable, cast to CleanString, except for the macros above.
CleanStringSizeInts (string_length)
    size of *CleanString in integers.
CleanStringSizeBytes (string_length)
    size of *CleanString in bytes.

When a String in Clean is passed to C, the String is not copied, but an offset is added to the address of the string in the heap of Clean, and this address is passed to C. Consequently, after returning from C, this address can no longer be used, because the string may be moved or deallocated by the garbage collector. The size of the string should not be modified. If the string is still needed in the C code after returning from C, you should make a copy and use this copy instead.

When a CleanString is returned from C, a copy of the string will be created in the Clean heap, and this copy will be used by Clean. If the string was allocated in C, for example using malloc, it should be deallocated in C when it no longer used, it is not automatically deallocated by Clean.

CleanString can not be the result type of a C function. CleanStrings should always be returned by a *CleanString argument from C. For example (example_string1.h):

void hello_string_from_c (CleanString *);
Clean (hello_string_from_c :: -> String)

The characters of Strings passed to C can be modified, but if the String is not unique, this causes side effects. This problem can be prevented by making sure the string is unique before calling the C code. See string_to_uppercase in example_string1_main.icl for an example.

2.9 ARRAYS

The following array types can be passed to C functions: {#Int}, {#Real} and {#Char}. They cannot be yielded by C functions.
The type of these arrays in C are CleanIntArray, CleanRealArray and CleanCharArray. These types are defined in the file Clean.h as:

typedef int *CleanIntArray;
typedef double *CleanRealArray;
typedef unsigned char *CleanCharArray;

so the elements of these arrays can be accessed directly in C using the [ ] or * operators. The size (number of elements) can be obtained using the following macros (in Clean.h):

CleanIntArraySize(clean_array) returns the size (number of elements) of the clean_int_array
CleanRealArraySize(clean_real_array) returns the size (number of elements) of the clean_real_array
CleanCharArraySize(clean_char_array) returns the size (number of elements) of the clean_char_array

See example_array1_main.icl for an example.

When an array in Clean is passed to C, the array is not copied, but an offset is added to the address of the array in the heap of Clean, and this address is passed to C. Consequently, after returning from C, this address can no longer be used, because the array may be moved or deallocated by the garbage collector. The size of the array should not be modified. If the array is still needed in the C code after returning from C, you should make a copy and use this copy instead.

2.10 STRUCT

struct definitions are not supported by htoclean. Furthermore htoclean cannot interface with C functions that accept struct arguments or return results of type struct.
But pointers to structures are supported. These are represented by integers in Clean.

2.11 TYPEDEFS

typedefs of int, real, pointer and enum types are supported by htoclean. Usually a Clean synonym type should be defined with the same name as the type in C. For example:

typedef int State;
Clean (::*State:==Int)
or:
typedef struct my_struct *MyStruct;
Clean (::MyStruct:==Int)

2.12 ENUMURATION TYPES

C enum types can be used in .h files for htoclean. For example (see example_enum1.h):

enum { ADD,SUB,AND,OR };

In Clean a macro is generated for each constant. So in this example: ADD:==0 and SUB:==1. The enum type can not have a name and = can not be used to define the value of a constant.

2.13 #DEFINES

#defines of decimal and hexadecimal integer constants and character constants may be defined in .h files for htoclean. The htoclean tool will generate a macro in Clean for each #defined definition. \ characters in character constants are not supported. See example_const1.h.

2.14 REALS

The types double (in C) and Real (in Clean) are supported.

2.15 IMPORT AND INCLUDE

To import the Clean module 'clean_module' use:
Clean (import clean_module)

htoclean will read the file clean_module.dcl, so that types defined in this module can be used. The contents of this file are not included in the output of htoclean. Instead 'import clean_module' is added to the output file. An example can be found in example_tuple1.h and example_type1.h.

To include the .h file 'prototypes.h' use:
#include "prototypes.h"
or:
#include <prototypes.h>

htoclean will read the file prototypes.h and use the contents of the file as if they where written in the file that included this .h file. No 'import protypes' is add to the Clean code produced by htoclean.

2.16 #PRAGMA

Lines starting with #pragma are ignored by htoclean.

3. COMPILING AND LINKING

3.1 COMPILING THE C PROGRAM / SUPPORTED C COMPILERS

On unix systems a c program (in this case example_int1_c.c) is compiled with cc, for example:
cc -O -c example_int1_c.c
The C compiler generates an object file: example_int1_c.o.
On windows system a C compiler has to be used that generates ECOFF object files, for example Visual C++ and Metrowerks CodeWarrior. The Watcom C compiler can not be used. The gnu compiler might work. We have tested Visual C versions 4 and 5. The example_int1_c.obj object file can be generated by compiling the .c file using the developer studio, or by invoking the C compiler from the MSDOS prompt with:
cl /c /O2 /Gy example_int1_c.c
On the PowerMacintosh Metrowerks CodeWarrior or MrC can be used. Because CodeWarrior does not generate separate object files, a library has to be build instead. This library should contain only object files, no libraries.

3.2 LINKING C OBJECT FILES WITH CLEAN

The module example_int1_main.icl uses this add function to add two numbers. To compile and link this program with the CleanIDE, create a project with this module as main module, and add the object file or library (for PowerPC CodeWarrior) to the list of Object Modules in the Link Options dialogue.

On unix systems the program can be compiled with clm using:
clm -l example_int1_c.o example_int1_main

3.3 LINKING DLL'S / SHARED LIBRARIES

To link with shared libraries on Unix systems with clm, use -l -lname, where name is the name of the shared library (without the lib prefix and .so suffix). clm will pass -lname to ld (the linker), which reads the file libname.so. clm does not interpret the argument after -l, so other parameters can be passed to the linker in this way as well.
To link with shared libraries using the CleanIDE on Unix systems, add the file libname.so to the Libraries of the Options/Link Options dialogue. Static shared libraries (.a files) should be added to the Object Modules of this dialogue.

The Clean linker for Windows and MacOS does not support importing symbols from DLLs/shared libraries using object files (import libraries), because some relocations are not implemented. Instead symbols from a DLL/shared library are imported using an ascii file that lists the symbols that are exported by a DLL/shared library. The first line of such a file contains the name of the DLL/shared library (with .dll extension on windows, usually no full pathname), the next lines contain the names of the exported symbols (only functions), each symbol on one line, spaces and comments are not permitted. Examples can be found in the 'Clean System Files' folder of the StdEnv. On the PowerMacintosh the files library0, library1 and library2 contain symbols exported by the InterfaceLib, StdCLib and MathLib. On Windows the files kernel_library, gdi_library and user_library contain symbols exported by kernel32.dll, gdi32.dll and user32.dll. These files do not contain all symbols exported by these DLL/libraries, missing symbols can be added with an editor. So to link with another DLL/library, such a text file has to be written, and then added to the Libraries of the Options/Link Options dialogue of the CleanIDE.

4 IMPLEMENTATION OF HTOCLEAN

4.1 CCALL

This section contains some information on the implementation of htoclean. You don't need to know this to use htoclean.

To interface Clean and C, htoclean uses the ABC abstract machine instruction 'ccall'. ABC instructions can be written in Clean using 'code { ABC instructions }'. Clean programmers should not use code{} and ABC instructions. This construct was added to make it easier for us to write the standard library and generate interfaces to the operating system/C code. Incorrect use of code{} and ABC instructions will often result in programs that crash. This construct may change/disappear in future versions of Clean without notice.

Each Clean function generated by htoclean consists of a function type and a function alternative using code { }. All the arguments and tuples of the function type are made strict by htoclean using ! annotations. The function alternative consists of code { ccall c_function_name type_string }. The first characters of the type string specify the types of the input parameters, then a ':' seperator follows, then a character that specifies the result type of the function, then follow (optional) characters that specify the types of the output parameters. I is used to specify an Int, R for Real, S for String and V for void. V is only allowed for the result type. This specification may be followed by a ':' separator and characters describing the additional Clean input/output parameters that are not visible in C. These characters use the same coding of types as above, V is of course not allowed.

For example to call:
void f (int,int *,int *);

with an extra Int parameter, htclean generates:
f :: !Int !*Int -> (!Int,!Int,!*Int);
f a1 a2 = code {
    ccall f "I:VII:I"
};
 

  • 5 FUTURE WORK
  • Support the calling convention used by most DLL's on windows where the callee pops the arguments, instead of the caller. This is already supported by the 'ccall' abc instruction, by adding a 'P' at the beginning of the string with type information.
  • Integrate htoclean in the CleanIDE.
  • Allow output parameters before the last input parameter.
  • Garbage collector support for deallocating memory allocated in C.
  • Call Clean functions from C.
  • APPENDIX

    A. TYPE CONVERSION
     
    C type: Clean type:
    ------------- ------------
    int Int,Bool,Char
    char Char
    double Real
    CleanString String
    CleanIntArray {#Int}
    CleanRealArray {#Real}
    CleanCharArray {#Char}
    AnyType * Int

    Comment starts with // or /*, and ends with a newline (for //) or with */ (for /*).

    B. GRAMMAR OF .H FILE

    interface-file:
            {interface-declaration}

    interface-declaration:
            c-function-declaration
            c-typedef-declaration
            c-define-declaration
            c-enum-declaration
            c-include
            c-pragma
            clean-declarations

    c-define-declaration:
            #define identifier integer_or-char-denotation

    integer_or-char-denotation:
            integer-denotation
            hexadecimal-integer-denotation
            char-denotation

    integer-denotation:
            [+|-] {0-9}+

    hexadecimal-integer-denotation:
            [+|-] 0x{0-9|a-f|A-F}+

    char-denotation:
            ' any-char '

    c-enum-declaration:
            enum { enum-constants } ;

    enum-constants:
            identifier
            identifier , enum-constants

    c-include:
            #include " file-name "
            #include < file-name >

    file-name: {any-char/-("|>)}

    c-pragma:
            #pragma {any-char/-(\n)}

    c-typedef-declaration:
            typedef c-type typedef-name ;
            typedef c-enum-declaration typedef-name ;

    c-type:
            c-basic-type {*}

    c-basic-type
            int
            char
            double
            void
            CleanString
            CleanIntArray
            CleanRealArray
            CleanCharArray
            struct struct-name
            typedef-name

    typedef-name:
            identfier

    c-function-declaration:
            c-type c-function-name ( c-function-argument-list ) ;
            c-type c-function-name ( void ) ;

    c-function-name:
            identifier

    c-function-argument-list:
            c-function-argument
            c-function-argument , c-function-argument-list

    c-function-argument:
            c-type
            c-type c-argument-name.

    c-argument-name:
            identifier.

    clean-declarations:
            Clean ( clean-declaration-list ) [;]

    clean-declaration-list:
            clean-declaration
            clean-declaration ; clean-declaration-list

    clean-declaration:
            clean-function-declaration
            clean-type-synonym-declaration
            clean-import

    clean-function-declaration:
            clean-function-name :: {clean-function-argument} -> clean-type
            clean-function-name :: clean-type

    clean-function-name:
            identifier

    clean-function-argument:
            clean-type

    clean-type:
            Int
            Char
            Bool
            Real
            String
            {#Int}
            {#Real}
            {#Char}
            clean-type-name
            ( clean-type-tuple-arguments )

    clean-type-tuple-arguments:
            clean-type
            clean-type , clean-type-tuple-arguments

    clean-type-synonym-declaration:
            :: [*] clean-type-name :== clean-type

    clean-type-name :: uppercase-identifier

    uppercase-identifier: (A-Z){identifier-char}

    identifier: {identifier-char}+

    identifier-char: (a-z|A-Z|0-9|_)

    clean-import:
            import module-name-list

    module-name-list:
            module-name
            module-name , module-name-list

    module-name: {any-char/-( |,|)|;)}

    C. EXAMPLE CODE

    File: example_const1.h

    #define ADD 10
    #define SUB 20
    #define AND 30
    #define OR  40

    #define ADD_OP '+'
    #define SUB_OP '-'
    #define AND_OP '&'
    #define OR_OP  '|'

    int compute (int operator,int arg1,int arg2);

    File: example_const1_c.c

    #include "Clean.h"
    #include "example_const1.h"

    int compute (int operator,int arg1,int arg2)
    {
     switch (operator){
      case ADD: return arg1+arg2;
      case SUB: return arg1-arg2;
      case AND: return arg1&arg2;
      case OR:  return arg1|arg2;
     }
     return 0;
    }

    File: example_const1_main.icl

    module example_const1_main;

    import example_const1;

    char_to_op ADD_OP = ADD;
    char_to_op SUB_OP = SUB;
    char_to_op AND_OP = AND;
    char_to_op OR_OP  = OR;

    Compute op a1 a2 = compute (char_to_op op) a1 a2;

    Start = (Compute '+' 1 2,Compute '-' 1 2,Compute '&' 1 2,Compute '|' 1 2);

    File: example_enum1.h

    enum {
     ADD,SUB,AND,OR
    };

    int compute (int operator,int arg1,int arg2);

    File: example_enum1_c.c

    #include "Clean.h"
    #include "example_enum1.h"

    int compute (int operator,int arg1,int arg2)
    {
     switch (operator){
      case ADD: return arg1+arg2;
      case SUB: return arg1-arg2;
      case AND: return arg1&arg2;
      case OR:  return arg1|arg2;
     }
     return 0;
    }

    File: example_enum1_main.icl

    module example_enum1_main;

    import example_enum1;

    Start = (compute ADD 1 2,compute SUB 1 2,compute AND 1 2,compute OR 1 2);

    File: example_int1.h

    int add (int v1,int v2); /* returns v1+v2 */

    File: example_int1_c.c

    #include "example_int1.h"

    int add (int v1,int v2)
    {
     return v1+v2;
    }

    File: example_int1_main.icl

    module example_int1_main;

    import example_int1;

    Start = add 1 2;

    File: example_int2.h

    void *my_malloc (int size_in_bytes); /* allocates size_in_bytes bytes */
    int my_free (void*); /* frees memory allocated by my_malloc */

    int *store_int (int *p,int offset,int value); /* p [offset] = value */

    int add_p (int *p1,int *p2); /* returns *p1 + *p2 */

    File: example_int2_c.c

    #include <Memory.h>

    #include "example_int2.h"
    /* or:
    # include "Clean.h"
    # include "example_int3.h"
    */

    void *my_malloc (int size_in_bytes)
    {
     return NewPtr (size_in_bytes);
    }

    int my_free (void *p)
    {
     DisposePtr (p);
     return 0;
    }

    int *store_int (int *p,int offset,int value)
    {
     p[offset]=value;
     return p;
    }

    int add_p (int *p1,int *p2)
    {
     return *p1 + *p2;
    }

    File: example_int2_main.icl

    module example_int2_main;

    import StdEnv;

    import example_int2;

    Start
     # p=my_malloc (2<<2); // allocate memory for 2 integers;
     | p==0
      = abort "Out of memory";
     # p=store_int p 0 10; // p[0]=10
     # p=store_int p 1 20; // p[1]=20
     # s=add_p (p+0) (p+4); // add p[0] and p[1]
     | s==s // force evaluation of add, before deallocating memory !
     # r=my_free p; // release_memory
     | r<>0 // force evaluation of my_free and test for error
      = abort "Function my_free failed";
     = s;

    File: example_int3.h

    Clean (
     :: *State :== Int ;
     :: IntPointer :== Int
     )

    void *my_malloc (int size_in_bytes); /* allocates size_in_bytes bytes */
    Clean (my_malloc :: Int State -> (IntPointer,State) )

    int my_free (void*); /* frees memory allocated by my_malloc */
    Clean (my_free :: IntPointer State -> (Int,State) )

    int *store_int (int *p,int offset,int value); /* p [offset] = value */
    Clean (store_int :: IntPointer Int Int State -> (IntPointer,State) )

    int add_p (int *p1,int *p2); /* returns *p1 + *p2 */
    Clean (add_p :: IntPointer IntPointer State -> (Int,State) )

    File: example_int3_main.icl

    module example_int3_main;

    import StdEnv;

    import example_int3;

    Start
     # state=0;
     # (p,state) = my_malloc (2<<2) state; // allocate memory for 2 integers;
     | p==0
      = abort "Out of memory";
     # (p,state) = store_int p 0 10 state; // p[0]=10
     # (p,state) = store_int p 1 20 state; // p[1]=20
     # (s,state) = add_p (p+0) (p+4) state; // add p[0] and p[1]
     # (r,state) = my_free p state; // release_memory
     | r<>0 // force evaluation of my_free and test for error
      = abort "Function my_free failed";
     = s;

    File: example_int4.h

    Clean (
     :: *State :== Int ;
     :: IntPointer :== Int
     )

    void *my_malloc (int size_in_bytes); /* allocates size_in_bytes bytes */
    int my_free (void*); /* frees memory allocated by my_malloc */
    void store_int (int *p,int offset,int value); /* p [offset] = value */
    int add_p (int *p1,int *p2); /* returns *p1 + *p2 */

    Clean (
     my_malloc :: Int State -> (IntPointer,State);
     my_free :: IntPointer State -> (Int,State) ;
     store_int :: IntPointer Int Int State -> State ;
     add_p :: IntPointer IntPointer State -> (Int,State)
     )

    File: example_int4_c.c

    #include <Memory.h>

    #include "Clean.h"
    #include "example_int4.h"

    void *my_malloc (int size_in_bytes)
    {
     return NewPtr (size_in_bytes);
    }

    int my_free (void *p)
    {
     DisposePtr (p);
     return 0;
    }

    void store_int (int *p,int offset,int value)
    {
     p[offset]=value;
    }

    int add_p (int *p1,int *p2)
    {
     return *p1 + *p2;
    }

    File: example_int4_main.icl

    module example_int4_main;

    import StdEnv;

    import example_int4;

    (>:) infixl;
    (>:) f g:==g f;

    Start
     # state=0;
     # (p,state) = my_malloc (2<<2) state; // allocate memory for 2 integers;
     | p==0
      = abort "Out of memory";
     # (s,state) = state
         >: store_int p 0 10 // p[0]=10
         >: store_int p 1 20  // p[1]=20
         >: add_p (p+0) (p+4); // add p[0] and p[1]
     # (r,state) = my_free p state; // release_memory
     | r<>0 // force evaluation of my_free and test for error
      = abort "Function my_free failed";
     = s;

    File: example_string1.h

    void spaces_string (int n_spaces,CleanString*);
    Clean (spaces_string :: Int -> String)

    void hello_string_from_c (CleanString *);
    Clean (hello_string_from_c :: -> String)

    int string_to_uppercase_with_side_effect (CleanString);
    Clean (string_to_uppercase_with_side_effect :: String -> Int)

    File: example_string1_c.c

    #include "Clean.h"
    #include "example_string1.h"

    #define MAX_SPACES_STRING_SIZE 1024

    static CleanStringVariable(result_string,MAX_SPACES_STRING_SIZE);

    void spaces_string (int n_spaces,CleanString *result_string_p)
    {
     char *result_string_characters;
     int i;

     if (n_spaces>MAX_SPACES_STRING_SIZE)
      n_spaces=MAX_SPACES_STRING_SIZE;

     CleanStringLength(result_string) = n_spaces;

     result_string_characters = CleanStringCharacters(result_string);

     for (i=0; i<n_spaces; ++i)
      result_string_characters[i]=' ';

     *result_string_p = (CleanString)result_string;
    }

    static struct {int length; char chars[5+1]; } hello_string = {5,"hello"};

    void hello_string_from_c (CleanString *hello_string_p)
    {
     *hello_string_p=(CleanString)&hello_string;
    }

    int string_to_uppercase_with_side_effect (CleanString string)
    {
     int i,length,n_lowercase_characters;
     char *characters;

     length=CleanStringLength(string);
     characters=CleanStringCharacters(string);
     n_lowercase_characters=0;

     for (i=0; i<length; ++i){
      char c;

      c=characters[i];
      if ((unsigned)(c-'a')<26u){
       characters[i]=c+('A'-'a');
       ++n_lowercase_characters;
      }
     }

     return n_lowercase_characters;
    }

    File: example_string1_main.icl

    module example_string1_main;

    import StdEnv;
    import example_string1;

    /*
     string_to_uppercase_with_side_effect updates the string. This is only allowed if this
     is the only reference to the string (i.e. the string is unique).
     We cannot make the string unique with the htoclean tool (unless we copy the string). Therefore,
     we define string_to_uppercase to make sure that the string is unique and use this function instead.
     We can return the string as a unique string by cheating using observation typing.
    */

    string_to_uppercase :: !*String -> (!Int,!*String);
    string_to_uppercase string
     #! n=string_to_uppercase_with_side_effect string; // evaluate and pretend we observe the string
     = (n,string);

    Start = (spaces_string 18+++"vwxyz\n"+++
       spaces_string 20+++"xyz\n",
       hello_string_from_c,"\n",
      string_to_uppercase {'A','a','B','b','C','c'});
    // we use {'A','a','B','b','C','c'} because "AaBbCc" is not unique

    File: example_tuple1.h

    Clean (import example_type1)

    void make_complex_int (int re,int im,int *re_p,int *im_p);
    Clean (make_complex_int :: Int Int -> ComplexInt)

    void add_complex_int (int re1,int im1,int re2,int im2,int *re_p,int *im_p);
    Clean (add_complex_int :: ComplexInt ComplexInt -> ComplexInt)

    File: example_tuple1_c.c

    #include "Clean.h"
    #include "example_tuple1.h"

    void make_complex_int (int re,int im,int *re_p,int *im_p)
    {
     *re_p=re;
     *im_p=im;
    }

    void add_complex_int (int re1,int im1,int re2,int im2,int *re_p,int *im_p)
    {
     *re_p=re1+re2;
     *im_p=im1+im2;
    }

    File: example_tuple1_main.icl

    module example_tuple1_main;

    import StdEnv;
    import example_tuple1;

    Start = add_complex_int (make_complex_int 1 2) (make_complex_int 3 4);

    File: example_type1.h

    Clean (::ComplexInt:==(Int,Int))

    File: example_array1.h

    int sum_int_array (CleanIntArray a);
    Clean (sum_int_array :: {#Int} -> Int)

    double sum_real_array (CleanRealArray a);
    Clean (sum_real_array :: {#Real} -> Real)

    int first_different_char_index (CleanCharArray a1,CleanCharArray a2);
    Clean (f :: {#Char} {#Char} -> Int)

    File: example_array1_c.c

    #include "Clean.h"

    int sum_int_array (CleanIntArray a)
    {
            int sum,s,i;
     
            s=CleanIntArraySize (a);
     
            sum=0;
            for (i=0; i<s; ++i)
                    sum+=a[i];
     
            return sum;
    }

    double sum_real_array (CleanRealArray a)
    {
            double sum;
            int s,i;
     
            s=CleanRealArraySize (a);
     
            sum=0;
            for (i=0; i<s; ++i)
                    sum+=a[i];
     
            return sum;
    }

    int first_different_char_index (CleanCharArray a1,CleanCharArray a2)
    {
            int s1,s2,i;
     
            s1=CleanCharArraySize (a1);
            s2=CleanCharArraySize (a2);
     
            for (i=0; i<s1 && i<s2; ++i)
                    if (a1[i]!=a2[i])
                            return i;
     
            if (s1!=s2)
                    return i;
            else
                    return -1;
    }

    File: example_array1_main.icl

    module example_array1_main;

    import StdEnv;
    import example_array1;

    Start = (sum_int_array {1,2,3},
                 sum_real_array {1.5,2.6,3.7},
                 first_different_char_index "abcdef" "abzdef")