5 Advanced IG Topics
This section describes the more advanced features of the Interface Generator and includes the following topics:
- IG generator switches
- Socket example
- Using C macros
- IG files
- Inside IG.
5.1 IG Generator Switches
The generator accepts a few switches which guide the stub generation process.
- cback_mod
- The
cback_mod
switch sets the name of the Erlang module where all callbacks from C will be found. For example:ig:gen(ex1, [{cback_mod, my_cb_mod}]).
The name of the callback module defaults to_cb
appended to the input file name.- nouse_stdio
- IG stubs normally use
stdin
andstdout
(which are file descriptors 0 and 1) for port communication, but this switch assigns file descriptors 2 and 3 instead.5.2 Sockets
IG is capable of running its communication over sockets. Sockets provide the advantage of using a server implemented on a different machine, ports are local, and the socket implementation enforces a more stringent use of the communication protocol. IG allows the C program to behave both as a server using calls from Erlang to C, and as a client using calls from C to Erlang. This could lead to confusion in the communication if both concepts are used at the same time. We could find ourselves in a race condition where both sides simultaneously decide to initiate some function calls. Both sides would then treat calls from the other side as the return value to their call (see figure below). We need not explain why this is unlikely to produce correct behaviour.
Example of Race ConditionThis problem can be solved by using two communication links between Erlang and C. One link is used for messages initiated from Erlang, and the other for messages initiated from C. However, it is difficult to have two ports to the same C program but very easy to have two sockets. Therefore, socket communication is preferred if calls will be initiated from both parties.
Socket communication requires the following additions:
- more standard C libraries are linked into the executable
- some IG socket routines are added to the C executable (the file
igsock.o
)- the main loop is replaced with one that can handle sockets.
The
igsock.o
file is included in the distribution and some examples of main loops are included in the examples directory. The new linker command line must be augmented with-lsocket -lnsl -lresolv
.
There is an
erl_interface_nodns
library for users who have no DNS service running.IG generated socket communication is started differently to ordinary port communication. There are two new start functions,
client/2
andserver/1
. The former is used when the Erlang side is a client and the C side acts as a server. The latter is used when the Erlang side is a server to the C side. Theclient/2
function takes two arguments, the first is the host name where the C program is running, and the second is the port number at which the C program is expecting to communicate. Theserver/1
only takes a port number as an argument and it is up to the C program to find the correct host where the Erlang stub is running. This is all basic socket programming practice.5.3 Socket Example
This example is based on the previous
ex1
example in this section. We will add some libraries at link time and then start the Erlang stub in a different way than port start. We also have to start the C program manually from the Unix prompt. We will use two Unix shells, one for the C program and one for the Erlang program.The first step is to make a new executable with other libraries than port operation, and with a different main loop. The
igsock_server
main loop is taken from theexamples
directory.UNIX 1> gcc -L. -o ex1 ex1.c ex1_stub.c igio.o igsock.o igsock_server.c -lsocket -lnsl -lresolv -lerl_interfaceWe now start the program with a random port number. In our case, we choose 6758 but any free port number will do.
UNIX 1> ./ex1 6758Nothing happens at this point, because the C program waits for Erlang to connect to the port. We start Erlang in the usual way and start the IG stub as a client to the C server. We then make the usual call to
foo
just to make sure that it works. Note the use ofnet_adm:localhost/0
which returns the host name as a string. We are in fact connecting to a socket which can be on any machine in the world.UNIX 2> erl Erlang (JAM) emulator version 4.4.2 Eshell V4.4.2 (abort with ^G) 1> P = ex1:client(net_adm:localhost(), 6758). <0.23.0> 2> ex1:foo(P, "Andrew", 55). {ok,55} 3> ex1:stop(P). okThe call to
foo
also results in a printout from C. In previous examples this has been printed in the Erlang shell, but this time it is printed in the Unix shell used for the C program. A printout of the type shown below appears in the Unix shell where the C program was started.foo: name='Andrew', age=55Two things are worth mentioning about this example:
- We did not have to regenerate any code, we did not even have to recompile the Erlang stub.
- We only had to replace the main loop and add some socket related libraries to compile the C program.
5.4 Using C Macros
IG supports a convenient subset of the standard C macros and pre-processor directives. Macros can be used for declaring constant values, and pre-processor directives can be used for conditional declaration of macros. Macros and conditionals are generated to an Erlang header file and the mapping is straight forward. Therefore, the conditionals do not affect the generation of stubs because they are only generated to the Erlang header file. It is not possible to conditionally declare interface functions in header files intended for IG.
The IG pre-processor directives are only generated to the
.hrl
file, not the.erl
file.
- /*IG*/ #define <name> [<const_value>].
- Puts a
-define( <name>, <const_value> ).
or a-define( <name>, true ).
in the.hrl
file. The second form is used when the constant value is omitted. The constant value can be either an integer, a boolean value, a string, or a previously defined macro name. This makes it easy to use C macro values in Erlang.- /*IG*/ #ifdef <name>.
- This works like ordinary C style pre-processor conditional directives. Puts an
-ifdef( <name> ).
in the.hrl
file. Useful for conditional declarations of macros in the Erlang header file (.hrl
).- /*IG*/ #ifndef <name>
- Puts an
-ifndef( <name> ).
in the.hrl
file.- /*IG*/ #else
- Puts an
-else.
in the.hrl
file.- /*IG*/ #endif
- Puts an
-endif.
in the.hrl
file.In the following example, we have the input file
pre.h
:/*IG*/ #define FWRITE 1 /*IG*/ #define FREAD 2 FILE* open_file(char* fname, int mode); /* where mode is FWRITE or FREAD */This will be translated to the following Erlang header file:
-define(FWRITE, 1). -define(FREAD, 2).This header file enables the Erlang programmer to write Erlang code which looks as follows:
-module(nils). -export([n/0]). -include("pre.hrl"). n() -> P = pre:start(), Fd = pre:file_open(P, "cake", ?FWRITE).
The Erlang code shown above is very similar to an equivalent C code.
5.5 IG Files
IG needs some magic files to work, namely the IO routines, the main loop and the value representation. The IO routines are contained in the file
igio.o
and the value representation is contained in theerl_interface
library. The value representation is a C view of the Erlang values, but it is not the representation used within the emulator.IG also comes with an example main loop in the file
igmain.o
. It is not mandatory because the main loop may need to be replaced when the method of communication changes . The main loop in the distribution supports port communication only but there are some examples of socket communication main loops in the examples directory. We have made a fair attempt to provide IO and main loops that fit large areas of usage, but we recognize that we cannot cover all territory.5.6 Inside IG
This section attempts to explain how IG really works. It contains details about the implementation and process structure. The reader is encouraged to read generated stub code and the
igserver.erl
file for reference. This information is not a requirement for using IG correctly and can be skipped at the discretion of the reader.When an IG generated program is set up, one process is started which is in charge of the port at the Erlang side, and a C program is started which can handle the communication on the port. The Erlang process runs a module called
igserver
which handles marshalling of arguments and all port communication. It is theigserver
process that recognizes the standard Erlang system messages which implement the software manager functions.igserver
also handles communication link details.5.6.1 A Real Function Call
We will now follow a function call from Erlang to C. First, an IG generated interface stub function is called from a user process. Each interface function from the header file is given a number at generate time and this number is used when the stub function communicates with
igserver
, along with any arguments. The stub function passes a message to theigserver
stating the function number and any parameters.The
igserver
then forwards this request for a function call to the C side where it will first be received by the IG IO routines, decoded using theerl_interface
library, and finally presented to the IG generated C stub dispatch function. The dispatcher will then extract the function number and use this number as an index in its array of function pointers. The pointers in the array point to specially generated intermediate functions, one for each stub function. These functions unpack all arguments, make the actual function call to the user function, and then pack the return value and send it back to Erlang.The
igserver
then receives its answer which it sends back to the original user process. All this is pictured in the figure below.
The IG Process