Interfacing with C and C++

Under construction: see definition.

Interfacing with C and C++ are similar enough that both can be covered together. There are two basic mechanisms available. The first and simpler of the two uses an existing routine and is available only to C externals. Using this approach you simply specify the signature of an existing C function along with where the function can be found. The second mechanism allows you to write C or C++ code inline. The second option is always recognizable by the presence of the word "inline" in the external specification. Again, the inline option is the only one allowed for C++ externals.

Using an existing C function

Here is an example of an external targeting an existing C function. In this case it's the floor function from the C library.

floor (v: DOUBLE): DOUBLE -- Floor of `v' external "C signature (double): double use <math.h>" end

The Eiffel external function floor now gives Eiffel code access to the C library function of the same name.

Here external is an Eiffel keyword that introduces the external specification that appears in the quoted string that follows. (The quoted string is actually an Eiffel manifest string, so it could actually be a simple quoted string or a verbatim string.) Within the quoted string we find first the registered language designator, in this case "C", followed by the function's signature, followed by the location in which the C function can be found. The terms "signature" and "use" within the quoted string are technically not Eiffel language keywords, but are keywords used in the specification of externals. There is one other similar keyword "inline" which can be used, but we will address it later.

The "signature" consists of the types of the arguments to C function as well as the function's return type. The argument types are separated by commas in a list enclosed in parentheses. A colon precedes the function type. In cases in which the target function takes no arguments, it is not necessary to include a set of empty parentheses in the signature. However, for compatibility purposes, the empty parentheses are allowed.

The "use" part denotes provides a file name or list of file names. File names can be either "user" file names or "system" file names. In the case of our example, a system file name "<math.h>" is used. System files are those that should be found by the system with no additional information. System file names are enclosed in angle brackets. User file names are included in a quoted string. User file names will be passed as written to the operating system. Remember that when coding a user file name, that because it already occurs within a quoted string, you must use the percent sign to code the quotation marks inside a quoted string, for example:

"C signature (double): double use %"my_c_header.h%""

Alternatively, you could code the external as a verbatim string. This would make the percent signs unnecessary.

There's one more thing to note about using existing C functions. In the floor example above, the Eiffel function has the same name as the C function it targets. But that may not always be the case. It's possible that the C function name might conflict with a name already established in your class, or might conflict with Eiffel naming conventions. Suppose you wanted to call the Eiffel routine my_floor instead of floor. You could use the alias part of the external specification to state that the actual C function name differs from the Eiffel name, as shown below.

my_floor (v: DOUBLE): DOUBLE -- Floor of `v' external "C signature (double): double use <math.h>" alias "floor" end

Inline externals

In addition to using an existing C function, you can, for both C and C++, create an inline external. The idea here is that you write the necessary C or C++ language within the definition of the external. In the case of inline externals the alias part is used to contain the inline code rather than a function name. Your inline C and C++ code can access the arguments of the external feature. Let's look at my_floor written as an inline external:

my_floor (v: DOUBLE): DOUBLE -- Floor of `v' external "C inline use <math.h>" alias "return floor($v)" end

In the alias part you see a line of C code calling the library function floor and returning the result. The argument to the call to floor needs to be the argument to the Eiffel function my_floor. To do this in the inline C code, the dollar sign ('$') precedes the argument name "v". So, the convention in inline externals is to use the dollar sign in C or C++ code to reference an argument of the Eiffel function.

The example below is a C++ example that involves more complex processing in the alias part.

c_height (a_framework: POINTER): INTEGER -- Get ribbon height require a_framework_exists: a_framework /= default_pointer external "C++ inline use <common.h>" alias "{ UINT32 val; HRESULT hr = S_OK; IUIRibbon* pRibbon = NULL; if (SUCCEEDED(((IUIFramework *) $a_framework)->GetView(0, IID_IUIRIBBON, (void **) &pRibbon))) { hr = pRibbon->GetHeight(&val); pRibbon->Release(); } return (EIF_INTEGER) val; }" end

Rules for C and C++ externals

C external validity

A C external is valid if it satisfies the following conditions:

  1. It specifies either an external signature or an inline routine.
  2. If it specifies an inline routine, then
    1. The C text of the routine follows the alias keyword.
    2. For any occurrence, within the C text, of an identifier following a dollar sign ('$'), the identifier must be the name of an argument of the external routine.

C++ external validity

A C++ external is valid if it satisfies the following conditions:

  1. It specifies an inline routine with the C++ text of the routine occurring after the alias keyword.
  2. For any occurrence, within the C++ text, of an identifier following a dollar sign ('$'), the identifier must be the name of an argument of the external routine.

External file name validity

An external file name appearing after the "use" is valid if it satisfies the following conditions:

  1. It denotes a file when interpreted according to the conventions of the underlying operating system.
  2. The file is accessible for reading.
  3. The file contains content valid in the target language.

The first condition means that user file names must conform to the path and file names of the target environment. Such file names will be passed on to the operating system as they appear. And for system file names, those enclosed in angle brackets, this means that they must be found where the operating system would expect such system files to reside.

Some parts of the validity of external file names may not be able to be determined by an Eiffel compiler and thus ultimately depend upon the evaluation of an external compiler. For example, the Eiffel compiler might not be able to determine whether a particular C file contains valid content.

Related pages or articles

See Also:
- https://www.eiffel.org/doc/solutions/Eiffel_%22external%22_mechanism
- https://www.eiffel.org/article/protecting_objects
- https://www.eiffel.org/article/using_externals_in_multithreaded_applications
- https://www.eiffel.org/article/c_c_calls_and_callbacks