Copyright © Quviq AB, 2009-2023
Version: 1.46.3
This module provides functions for testing code written in C.
The purpose of this module is to make it easy to test C programs with QuickCheck. To do this it provides a way to call C functions from Erlang together with a number of functions to manipulate C values.
example.c
containing the following functions:
// Add two integers int plus (int x, int y) { return x + y; } // Sum an array of integers int sum (int *array, int len) { int n; int sum = 0; for (n = 0; n < len; n++) sum += array[n]; return sum; }To make these functions available to call from Erlang, simply call the start function:
1> eqc_c:start(example). okThis creates an Erlang module
example
containing Erlang wrappers for the
functions from the C file example.c
.
2> example:plus(3, 4). 7The
sum
function takes a pointer to an array as an argument, so in order
to call it we need to get that pointer from somewhere. For instance, using
create_array/2
to allocate a new array:
3> P = eqc_c:create_array(int, [1, 2, 3, 4]). {ptr,int,1048864} 4> example:sum(P, 4). 10
start/1
, the C source is preprocessed and analysed and
then three things happen:
start/2
for
information on how to specify for which functions to create wrappers).
These functions will pass their arguments (after type checking them)
to the compiled C program and return the result of calling the
corresponding C function. See the definition of the supported C types for how C values are represented in Erlang.
To access global variables appearing in the C program, the following
functions are provided: address_of/1
, value_of/1
, and set_value/2
. The address_of/1
function gives you a pointer to the global
variable of the specified name, and can also be used to get pointers to
exported functions. value_of/1
and set_value/2
allows you to get and
set the value of a global variable.
int xs[3] = {1, 2, 3}; struct arr { int ys[2]; };can be interacted with as follows
3> eqc_c:value_of(xs). [1,2,3] 4> eqc_c:alloc({struct, arr}, {arr, [4,5]}). {ptr,{struct,arr},4297064576}Functions taking array arguments. If an array appears as an argument to a function, it is treated as a pointer in C. For instance, the following function expects a pointer to an array as its argument:
int sum(int a[2]) { return a[0] + a[1]; }Consequently, the type assigned to this function on the Erlang side is
5> eqc_c:type_of(sum). {func,int,[{ptr,{array,int}}]}and it can be called (assuming it's defined in
example.c
) as
6> P = eqc_c:create_array(int, [7,11]). {ptr,int,4297064592} 7> example:sum(P). 18
Note that a pointer to an integer is happily accepted as a pointer to an array of integers.
Automatic allocation. For convenience, a single value or a list of values is always accepted whenever a pointer is required. An array containing the given elements will then be allocated and the pointer to it passed along. This is particularly handy for functions expecting string arguments. For instance,void hello(char *s) { printf("Hello %s!\n", s); }can be called as
8> example:hello("world"). Hello world! okrather than
9> example:hello(eqc_c:create_string("world")). Hello world! ok
Note that values created in this way will not be freed and can thus cause a memory leak.
5> eqc_c:deref({ptr, int, 0}). ** exception error: bus_error
In the case of a looping C program a timeout exception is raised in the
Erlang process. The timeout value can be set when starting the C program (see
start/2
) or using set_timeout/1
. Note: when a timeout
occurs a new instance of the C program is started and will be used for
subsequent function calls, but the old instance is still running,
possibly consuming a lot of resources.
At the moment there is no support for functions with variable arity. These can appear in your C program, but you will not be able to call functions with variable arity from Erlang.
Many standard C header files declare functions which are not actually defined in the corresponding libraries, which results in link errors if wrappers are generated for them. This can be prevented by using the definitions_only option tostart/2
(which is
the default using start/1
), or by ensuring such header files
are not included in the C file you pass to start/2
, or by
specifying the include_functions or
exclude_functions option to prevent the problematic
wrappers being generated.
ptr() = {ptr, type(), integer()} | 'NULL'
The value of a C pointer. Contains the memory address as well as the type of the value pointed to.
type() = void | bool | char | unsigned_char | short | unsigned_short | int | unsigned_int | long | unsigned_long | long_long | unsigned_long_long | int128 | unsigned_int128 | float | double | long_double | float128 | complex_float | complex_double | complex_long_double | {ptr, type()} | {func, type(), [type()]} | {closure, type(), [type()]} | {array, integer(), type()} | {array, type()} | {struct, atom()} | {union, atom()} | {enum, atom()} | string()
Supported C types.
void
can only appear as the target of a pointer or as a return
type for a function.
bool
is the boolean type from stdbool.h
(C99) and is
represented in Erlang by the atoms true
and false
.
char
, unsigned_char
, short
, unsigned_short
, int
,
unsigned_int
, long
, unsigned_long
, long_long
,
unsigned_long_long
, int128
, and unsigned_int128
are represented in Erlang as integers.
float
, double
, long_double
, and float128
are represented as Erlang
floats. Note: depending on the platform, precision may be lost
for long doubles.
complex_float
, complex_double
, and complex_long_double
are
represented as pairs of Erlang floats, with the first component being
the real part and the second component the imaginary part.
{ptr, T}
is represented as a ptr()
with
the type field set to T
.
{func, Ret, Args}
is the type of a function returning a
value of type Ret
(possibly void
) and whose arguments have
types Args
. Function types can only appear as targets of
pointers.
{closure, Ret, Args}
has the same shape
as a function type. Closures are supported by some C compilers, for
instance gcc on MacOS X 10.6.
{array, T}
or {array, Len, T}
is represented in
Erlang as a list of values of type T
. If a length Len
is specified the list
should have Len
elements.
See working with arrays above for more details.
struct coord { int x, y; };is modelled by the Erlang record
-record(coord, {x, y}).For anonymous structs a name is chosen depending on how the struct is defined. In particular, if it is defined inside a typedef it gets the name of the introduced type. For instance, the definition
typedef struct { int x, y; } coord;will result in the same Erlang record being generated as in the previous example.
union u { int x; bool y; }; union u i = { .x = 0 };we could have the following interaction
5> eqc_c:value_of(i). #u{x = 0,y = false} 6> eqc_c:set_value(i, #u{y = true}). ok 7> eqc_c:value_of(i). #u{x = 1,y = true}For convenience it is also allowed to pass a value matching one of the variants as a union value. So, in place of the set_value call above we could write
6> eqc_c:set_value(i, true). ok
enum colour { red, green, blue };are the atoms
red
, green
, and blue
.
typedef int MyInt;we can call
1> eqc_c:alloc("MyInt", 17). {ptr,int,2097440}Note how the typedef has been expanded in the value of the pointer.
add_to_ptr/2 | Add N to a pointer. |
address_of/1 | Get a pointer to a C function or global variable. |
alloc/1 | Allocate memory for an object of type Type . |
alloc/2 | Allocate memory for an object of type Type and initialize it to
the value X . |
array_index/2 | Return the value at the given index of an array. |
array_index/3 | Set the value at the given index of an array. |
cast_ptr/2 | Change the type of a pointer. |
create_array/2 | Allocate an array of the given type containing the values Xs . |
create_string/1 | Allocate a C string containing the given string. |
deref/1 | Dereference a pointer. |
expand_type/1 | Expand all type definitions in a type. |
free/1 | Free the memory pointed to by a pointer. |
read_array/2 | Read the elements of an array. |
read_string/1 | Read the value of a null terminated C string. |
restart/0 | Restart the C program. |
running/0 | Check if the C program is running. |
set_timeout/1 | Set the timeout for C function calls to N milliseconds. |
set_value/2 | Set the value of a global variable. |
sizeof/1 | The size in bytes required to store an object of the given type. |
start/1 | Equivalent to start(Module, [definitions_only]). |
start/2 | Set up an interface to a given C file. |
start_functions/2 | Equivalent to start(Module, [{include_functions, Functions}]). |
stop/0 | Stop the C program. |
store/2 | Store a value at the location pointed to by a pointer. |
type_info/1 | Get information on a type. |
type_of/1 | Get the type of a C function or global variable. |
type_of/2 | Get the type of a C function or global variable. |
value_of/1 | Get the value of a global variable. |
write_array/2 | Store values in the array pointed to be the first argument. |
Add N to a pointer. Equivalent to Ptr += N
in C. In other words
the value of the resulting pointer depends on the size of the type
pointed to.
address_of(X::atom()) -> ptr()
Get a pointer to a C function or global variable.
Allocate memory for an object of type Type
. The allocated memory
is initialized with zeroes.
Allocate memory for an object of type Type
and initialize it to
the value X
. Type checks the value before allocating the memory.
array_index(Ptr::ptr(), Index::integer()) -> term()
Return the value at the given index of an array. Equivalent to
the C code Ptr[Index]
. Indexing starts at index 0.
array_index(Ptr::ptr(), Index::integer(), Val::term()) -> ok
Set the value at the given index of an array. Equivalent to
the C code Ptr[Index] = Val
. Indexing starts at index 0.
Change the type of a pointer. Equivalent to the C code (Type *)Ptr
.
Note: This is unsafe and should be used with care.
Allocate an array of the given type containing the values Xs
.
The values are type checked before allocation.
create_string(S::string()) -> ptr()
Allocate a C string containing the given string.
deref(Ptr::ptr()) -> term()
Dereference a pointer.
Expand all type definitions in a type.
free(Ptr::ptr()) -> ok
Free the memory pointed to by a pointer. Equivalent to the
C code free(Ptr)
.
read_array(Ptr::ptr(), N::integer()) -> [term()]
Read the elements of an array. Since C arrays do not store their length, the length has to be supplied separately.
read_string(Ptr::ptr()) -> string()
Read the value of a null terminated C string.
restart() -> any()
Restart the C program. Just restarts the executable, no recompilation is performed.
running() -> any()
Check if the C program is running.
set_timeout(N) -> any()
Set the timeout for C function calls to N
milliseconds. Can also be
set from start/2
.
set_value(X::atom(), V::term()) -> ok
Equivalent to store(address_of(X), V).
Set the value of a global variable.
sizeof(Type::type()) -> integer()
The size in bytes required to store an object of the given type. Equivalent
to sizeof(Type)
in C.
start(Module) -> any()
Equivalent to start(Module, [definitions_only]).
start(Module::atom(), Options::proplist()) -> ok | failed | {error, string()}
Set up an interface to a given C file. Generates and loads an Erlang
wrapper Module
for the C file specified by the c_src
option
(atom_to_list(Module) ++ ".c"
by default). For instance, if
foo.c
defines a function int plus(int x, int y)
, calling
eqc_c:start(foo, [])
allows plus
to be called as
foo:plus(1, 2)
.
{c_src, string()}
atom_to_list(Module) ++ ".c"
).definitions_only
{exclude_functions, [atom() | string()]}
{include_functions, [atom() | string()]}
keep_files
{hrl, string() | none}
none
if no file should be
created (default: atom_to_list(Module) ++ ".hrl"
).{rename, [{CName::atom() | string(), ErlName::atom() | string()}]}
{timeout, integer()}
{casts, atom() | [atom()]}
to_c/2
and from_c/2
, which given a C type
and a value performs the desired conversions. The functions should throw an exception
for types that are not to be converted.
A typical use case is when the C program could have used an enum type, but didn't. For instance, if the C code defines
typedef int Answer; #define YES 1 #define NO 0a casts module might define
to_c("Answer", yes) -> 1; to_c("Answer", no) -> 0. from_c("Answer, 1) -> yes; from_c("Answer, 0) -> no.See also the
eqc_c_enum
module for a tool to generate
to_c
and from_c
functions in cases like this one.
verbose
silent
return_error
{cc, string()}
{cpp, string()}
{coutput_flag, string()}
{cmacro_flag, string()}
{cflags, string()}
{cppflag, string()}
{cppflags, string()}
{wrapper_src, string()}
filename:join(code:lib_dir(eqc,include),"eqc_c_lib.c")
.{additional_files, [string()]}
{exec_command_line, fun((string()) -> {string(), [string()]})}
start_functions(Module, Functions) -> any()
Equivalent to start(Module, [{include_functions, Functions}]).
stop() -> any()
Stop the C program.
store(Ptr::ptr(), Val::term()) -> ok
Store a value at the location pointed to by a pointer. Equivalent to
the C code *Ptr = Val
. The value is type checked against the
type of the pointer.
type_info(Type::Defined | Datatype) -> Definition | Fields | Tags
Get information on a type. For defined types,
type_info
returns the definition without expanding nested typedefs
(to get the fully expanded type use expand_type/1
). For struct and
union types, the list of fields is returned, where each field is a pair of a
type and a field name. For enum types, the list of tags is returned.
type_of(X::atom()) -> type()
Equivalent to type_of(X, false).
Get the type of a C function or global variable.
type_of(X::atom(), Expand::bool()) -> type()
Get the type of a C function or global variable. If Expand
is true,
type definitions are unfolded. For instance, given the C program
typedef unsigned int UINT; UINT some_int = 42;we would get the following results from
type_of
:
2> eqc_c:type_of(some_int, true). unsigned_int 3> eqc_c:type_of(some_int, false). "UINT"
value_of(X::atom()) -> term()
Equivalent to deref(address_of(X)).
Get the value of a global variable.
write_array(Ptr::ptr(), List::term()) -> ok
Store values in the array pointed to be the first argument. The values are type checked against the type of the pointer.
Generated by EDoc