C externals

    Contents
  1. General consideration
  2. C routines
  3. Macros
  4. Structs
  5. Windows externals
    1. DLLs
    2. Windows API

General consideration

As Eiffel Software's technology relies heavily on the use of a C/C++ ANSI compiler, you have to be sure to always put the correct signature of an external C/C++ routine. If it was not the case, the C compilation of your system could fail. Most of the time a C compiler is more comprehensive than a C++ compiler and most type errors won't cause you any harm, but C++ compilers are not as lax as C compilers and they will mostly generate errors.

The sections concerning Macros and Structs are also available for C++ if the macro or the struct is defined in a C++ header file.

C routines

You can encapsulate routines that are defined in a C header file. We will take some examples and will show you how to write wrappers in Eiffel.

If in a header file called my_header.h, you have the following declaration: /* Routine with no parameter */ extern void no_param(void); /* Routine with one parameter */ extern void one_param(int j); /* Routine returning a value with no parameter */ extern size_t no_param_return(void); /* Routine returning a value with one parameter */ extern size_t one_param_return(FILE *f);

Here is the corresponding Eiffel code: c_no_param -- Encapsulation of a C routine with no parameter. external "C | %"my_header.h%"" alias "no_param" end c_one_param (i: INTEGER) -- Encapsulation of a C routine with one parameter. external "C (int) | %"my_header.h%"" alias "one_param" end c_no_param_return: INTEGER -- Encapsulation of a C routine with no parameter -- returning an INTEGER external "C (): EIF_INTEGER| %"my_header.h%"" alias "no_param_return" end c_one_param_return (p: POINTER): INTEGER -- Encapsulation of a C routine with one parameter -- returning an INTEGER external "C (FILE *): EIF_INTEGER| %"my_header.h%"" alias "one_param_return" end

Macros

If in a header file called my_header.h, you have the following declaration: /* Predefined constants */ #define ID_MENU 128 #define ID_MENU_CHARACTER 'c' /* Access the 'i'-th element of 'a' where 'a' * is an array of EIF_INTEGER */ #define i_th(a,i)((a) + (i)*sizeof(EIF_INTEGER))

Then, the corresponding Eiffel code will look like:

menu_id: INTEGER -- `ID_MENU' C encapsulation. external "C [macro %"my_header.h%"] : EIF_INTEGER" alias "ID_MENU" end menu_id_character: CHARACTER -- `ID_MENU_CHARACTER' C encapsulation. external "C [macro %"my_header.h%"] : EIF_CHARACTER" alias "ID_MENU_CHARACTER" end i_th (p: POINTER; i: INTEGER): INTEGER -- Access the `i'-th element of `p', array of C EIF_INTEGER. external "C [macro %"my_header.h%"] (EIF_INTEGER *, EIF_INTEGER): EIF_INTEGER" alias "i_th" end

Structs

The struct encapsulation enables you to wrap C/C++ structures easily without having to write any additional code in a C header file as it was the case until Eiffel Software introduced this new keyword in the external specification with the 4.5 release of the Eiffel Software environment. With the struct encapsulation you can set and retrieve the value of a certain field of a struct.

If in a header file called my_header.h, you have the following declaration of the Point structure whose x and y fields we want to access and set:

/* Definition of `Point' */ typdef struct point { int x; int y; } Point;

Then, the corresponding Eiffel code will look like: x (p: POINTER): INTEGER -- Access field x of struct pointed by `p'. external "C [struct %"my_header.h%"] (Point): EIF_INTEGER" alias "x" end y (p: POINTER): INTEGER -- Access field y of struct pointed by `p'. external "C [struct %"my_header.h%"] (Point): EIF_INTEGER" alias "y" end set_x (p: POINTER; v: INTEGER) -- Set field x of struct pointed by `p'. external "C [struct %"my_header.h%"] (Point, int)" alias "x" end set_y (p: POINTER: v: INTEGER) -- Set field y of struct pointed by `p' with `v'. external "C [struct %"my_header.h%"] (Point, int)" alias "y" end

Windows externals

DLLs

With EiffelStudio you now have two different ways to call C routines exported in a DLL. Why two, because Windows provides two ways to call a C routine in a DLL:

  • _cdecl: referred to as the standard way
  • __stdcall referred to as the Pascal way

Therefore if you want to call an external routine defined in a DLL supposed to be called using the _cdecl method, you have to use the dll32 sub-language option. For __stdcall you need to use the dllwin32 sub-language option. Here is an example:

my_cdecl_routine (a: INTEGER): POINTER -- Encapsulation of a dll function with the `_cdecl' call mechanism. external "C [dll32 %"my_dll.dll%"] (int): EIF_POINTER" end my_stdcall_routine (a: INTEGER): POINTER -- Encapsulation of a dll function with the `_stdcall' call mechanism. external "C [dllwin32 %"my_dll.dll%"] (int): EIF_POINTER" end

Warning: Using dll32 in cases calling for dllwin32 and vice versa will cause your system to crash, because the C call stack will be corrupted. For more information please read your C compiler documentation.

Windows API

As described in the previous section concerning routines exported in a DLL, the Windows API is using the __stdcall convention. As a consequence, you cannot use the standard external mechanism to wrap them because it is using the _cdecl convention. However, you can easily wrap those function by using the macro sub-language.

Here is an example that has been taken from WEL, the Windows Eiffel Library, to encapsulate the Windows SendMessage function:Windows defined SendMessage as:

LRESULT SendMessage( HWND hWnd, // handle to destination window UINT Msg, // message WPARAM wParam, // first message parameter LPARAM lParam // second message parameter );

In WEL, the encapsulation is written as:

cwin_send_message (hwnd: POINTER; msg, wparam, param: INTEGER) -- SDK SendMessage (without the result) external "C [macro %"wel.h%"] (HWND, UINT, WPARAM, LPARAM)" alias "SendMessage" end

See Also:
C++ externals