Previous Section Next Section Table of Contents Glossary Index

Chapter 12. The Foreign-Function Interface

12.10. Tutorial: Using Basic Calls and Types

This tutorial is meant to cover the basics of CCL for calling external C functions and passing data back and forth. These basics will provide the foundation for more advanced techniques which will allow access to the various external libraries and toolkits.

The first step is to start with a simple C dynamic library in order to actually observe what is actually passing between CCL and C. So, some C code is in order:

Create the file typetest.c, and put the following code into it:

#include <stdio.h>

void
void_void_test(void)
{
    printf("Entered %s:\n", __FUNCTION__);
    printf("Exited  %s:\n", __FUNCTION__);
    fflush(stdout);
}

signed char
sc_sc_test(signed char data)
{
    printf("Entered %s:\n", __FUNCTION__);
    printf("Data In: %d\n", (signed int)data);
    printf("Exited  %s:\n", __FUNCTION__);
    fflush(stdout);
    return data;
}

unsigned char
uc_uc_test(unsigned char data)
{
    printf("Entered %s:\n", __FUNCTION__);
    printf("Data In: %d\n", (signed int)data);
    printf("Exited  %s:\n", __FUNCTION__);
    fflush(stdout);
    return data;
}
    

This defines three functions. If you're familiar with C, notice that there's no main(), because we're just building a library, not an executable.

The function void_void_test() doesn't take any parameters, and doesn't return anything, but it prints two lines to let us know it was called. sc_sc_test() takes a signed char as a parameter, prints it, and returns it. uc_uc_test() does the same thing, but with an unsigned char. Their purpose is just to prove to us that we really can call C functions, pass them values, and get values back from them.

This code is compiled into a dynamic library on OS X 10.3.4 with the command:


      gcc -dynamiclib -Wall -o libtypetest.dylib typetest.c \
      -install_name ./libtypetest.dylib
    

Tip

Users of 64-bit platforms may need to pass options such as "-m64" to gcc, may need to give the output library a different extension (such as ".so"), and may need to user slightly different values for other options in order to create an equivalent test library.

The -dynamiclib tells gcc that we will be compiling this into a dynamic library and not an executable binary program. The output filename is "libtypetest.dylib". Notice that we chose a name which follows the normal OS X convention, being in the form "libXXXXX.dylib", so that other programs can link to the library. CCL doesn't need it to be this way, but it is a good idea to adhere to existing conventions.

The -install_name flag is primarily used when building OS X "bundles". In this case, we are not using it, so we put a placeholder into it, "./libtypetest.dylib". If we wanted to use typetest in a bundle, the -install_name argument would be a relative path from some "current" directory.

After creating this library, the first step is to tell CCL to open the dynamic library. This is done by calling .


      Welcome to CCL Version (Beta: Darwin) 0.14.2-040506!

      ? (open-shared-library "/Users/andewl/openmcl/libtypetest.dylib")
      #<SHLIB /Users/andewl/openmcl/libtypetest.dylib #x638EF3E>
    

You should use an absolute path here; using a relative one, such as just "libtypetest.dylib", would appear to work, but there are subtle problems which occur after reloading it. See the Darwin notes on for details. It would be a bad idea anyway, because software should never rely on its starting directory being anything in particular.

This command returns a reference to the opened shared library, and CCL also adds one to the global variable ccl::*shared-libraries*:


      ? ccl::*shared-libraries*
      (#<SHLIB /Users/andewl/openmcl/libtypetest.dylib #x638EF3E>
       #<SHLIB /usr/lib/libSystem.B.dylib #x606179E>)
    

Before we call anything, let's check that the individual functions can actually be found by the system. We don't have to do this, but it helps to know how to find out whether this is the problem, when something goes wrong. We use external-call:


      ? (external "_void_void_test")
      #<EXTERNAL-ENTRY-POINT "_void_void_test" (#x000CFDF8) /Users/andewl/openmcl/libtypetest.dylib #x638EDF6>

      ? (external "_sc_sc_test")
      #<EXTERNAL-ENTRY-POINT "_sc_sc_test" (#x000CFE50) /Users/andewl/openmcl/libtypetest.dylib #x638EB3E>

      ? (external "_uc_uc_test")
      #<EXTERNAL-ENTRY-POINT "_uc_uc_test" (#x000CFED4) /Users/andewl/openmcl/libtypetest.dylib #x638E626>
    

Notice that the actual function names have been "mangled" by the C linker. The first function was named "void_void_test" in typetest.c, but in libtypetest.dylib, it has an underscore (a "_" symbol) before it: "_void_void_test". So, this is the name which you have to use. The mangling - the way the name is changed - may be different for other operating systems or other versions, so you need to "just know" how it's done...

Also, pay particular attention to the fact that a hexadecimal value appears in the EXTERNAL-ENTRY-POINT. (#x000CFDF8, for example - but what it is doesn't matter.) These hex numbers mean that the function can be dereferenced. Functions which aren't found will not have a hex number. For example:


      ? (external "functiondoesnotexist")
      #<EXTERNAL-ENTRY-POINT "functiondoesnotexist" {unresolved}  #x638E3F6>
    

The "unresolved" tells us that CCL wasn't able to find this function, which means you would get an error, "Can't resolve foreign symbol," if you tried to call it.

These external function references also are stored in a hash table which is accessible through a global variable, ccl::*eeps*.

At this point, we are ready to try our first external function call:


      ? (external-call "_void_void_test" :void)
      Entered void_void_test:
      Exited  void_void_test:
      NIL
    

We used , which is is the normal mechanism for accessing externally linked code. The "_void_void_test" is the mangled name of the external function. The :void refers to the return type of the function.

The next step is to try passing a value to C, and getting one back:


      ? (external-call "_sc_sc_test" :signed-byte -128 :signed-byte)
      Entered sc_sc_test:
      Data In: -128
      Exited  sc_sc_test:
      -128
    

The first :signed-byte gives the type of the first argument, and then -128 gives the value to pass for it. The second :signed-byte gives the return type. The return type is always given by the last argument to .

Everything looks good. Now, let's try a number outside the range which fits in one byte:


      ? (external-call "_sc_sc_test" :signed-byte -567 :signed-byte)
      Entered sc_sc_test:
      Data In: -55
      Exited  sc_sc_test:
      -55
    

Hmmmm. A little odd. Let's look at the unsigned stuff to see how it reacts:


      ? (external-call "_uc_uc_test" :unsigned-byte 255 :unsigned-byte)
      Entered uc_uc_test:
      Data In: 255
      Exited  uc_uc_test:
      255
    

That looks okay. Now, let's go outside the valid range again:


      ? (external-call "_uc_uc_test" :unsigned-byte 567 :unsigned-byte)
      Entered uc_uc_test:
      Data In: 55
      Exited  uc_uc_test:
      55

      ? (external-call "_uc_uc_test" :unsigned-byte -567 :unsigned-byte)
      Entered uc_uc_test:
      Data In: 201
      Exited  uc_uc_test:
      201
    

Since a signed byte can only hold values from -128 through 127, and an unsigned one can only hold values from 0 through 255, any number outside that range gets "clipped": only the low eight bits of it are used.

What is important to remember is that external function calls have very few safety checks. Data outside the valid range for its type will silently do very strange things; pointers outside the valid range can very well crash the system.

That's it for our first example library. If you're still following along, let's add some more C code to look at the rest of the primitive types. Then we'll need to recompile the dynamic library, load it again, and then we can see what happens.

Add the following code to typetest.c:

int
si_si_test(int data)
{
    printf("Entered %s:\n", __FUNCTION__);
    printf("Data In: %d\n", data);
    printf("Exited  %s:\n", __FUNCTION__);
    fflush(stdout);
    return data;
}

long
sl_sl_test(long data)
{
    printf("Entered %s:\n", __FUNCTION__);
    printf("Data In: %ld\n", data);
    printf("Exited  %s:\n", __FUNCTION__);
    fflush(stdout);
    return data;
}

long long
sll_sll_test(long long data)
{
    printf("Entered %s:\n", __FUNCTION__);
    printf("Data In: %lld\n", data);
    printf("Exited  %s:\n", __FUNCTION__);
    fflush(stdout);
    return data;
}

float
f_f_test(float data)
{
    printf("Entered %s:\n", __FUNCTION__);
    printf("Data In: %e\n", data);
    printf("Exited  %s:\n", __FUNCTION__);
    fflush(stdout);
    return data;
}

double
d_d_test(double data)
{
    printf("Entered %s:\n", __FUNCTION__);
    printf("Data In: %e\n", data);
    printf("Exited  %s:\n", __FUNCTION__);
    fflush(stdout);
    return data;
}
    

The command line to compile the dynamic library is the same as before:


      gcc -dynamiclib -Wall -o libtypetest.dylib typetest.c \
      -install_name ./libtypetest.dylib
    

Now, restart CCL. This step is required because CCL cannot close and reload a dynamic library on OS X.

Have you restarted? Okay, try out the new code:


      Welcome to CCL Version (Beta: Darwin) 0.14.2-040506!

      ? (open-shared-library "/Users/andewl/openmcl/libtypetest.dylib")
      #<SHLIB /Users/andewl/openmcl/libtypetest.dylib #x638EF3E>

      ? (external-call "_si_si_test" :signed-fullword -178965 :signed-fullword)
      Entered si_si_test:
      Data In: -178965
      Exited  si_si_test:
      -178965

      ? ;; long is the same size as int on 32-bit machines.
      (external-call "_sl_sl_test" :signed-fullword -178965 :signed-fullword)
      Entered sl_sl_test:
      Data In: -178965
      Exited  sl_sl_test:
      -178965

      ? (external-call "_sll_sll_test"
      :signed-doubleword -973891578912 :signed-doubleword)
      Entered sll_sll_test:
      Data In: -973891578912
      Exited  sll_sll_test:
      -973891578912
    

Okay, everything seems to be acting as expected. However, just to remind you that most of this stuff has no safety net, here's what happens if somebody mistakes sl_sl_test() for sll_sll_test(), thinking that a long is actually a doubleword:


      ? (external-call "_sl_sl_test"
      :signed-doubleword -973891578912 :signed-doubleword)
      Entered sl_sl_test:
      Data In: -227
      Exited  sl_sl_test:
      -974957576192
    

Ouch. The C function changes the value with no warning that something is wrong. Even worse, it manages to pass the original value back to CCL, which hides the fact that something is wrong.

Finally, let's take a look at doing this with floating-point numbers.


      Welcome to CCL Version (Beta: Darwin) 0.14.2-040506!

      ? (open-shared-library "/Users/andewl/openmcl/libtypetest.dylib")
      #<SHLIB /Users/andewl/openmcl/libtypetest.dylib #x638EF3E>

      ? (external-call "_f_f_test" :single-float -1.256791e+11 :single-float)
      Entered f_f_test:
      Data In: -1.256791e+11
      Exited  f_f_test:
      -1.256791E+11

      ? (external-call "_d_d_test" :double-float -1.256791d+290 :double-float)
      Entered d_d_test:
      Data In: -1.256791e+290
      Exited  d_d_test:
      -1.256791D+290
    

Notice that the number ends with "...e+11" for the single-float, and "...d+290" for the double-float. Lisp has both of these float types itself, and the d instead of the e is how you specify which to create. If you tried to pass :double-float 1.0e2 to external-call, Lisp would be nice enough to notice and give you a type error. Don't get the :double-float wrong, though, because then there's no protection.

Congratulations! You now know how to call external C functions from within CCL, and pass numbers back and forth. Now that the basic mechanics of calling and passing work, the next step is to examine how to pass more complex data structures around.

12.10.1. Acknowledgement

This chapter was generously contributed by Andrew P. Lentvorski Jr.


Previous Section Next Section Table of Contents Glossary Index