A Software Tool to Bind Allegro CL to C++ Libraries


Draft History:


2000Oct31 Update to version 1.6.1 - ACL 6.0


Contents


Introduction

The purpose of this tool is to facilitate the creation of an interface between Allegro CL and a library of C++ classes, functions, and subprograms.

The C++ library is typically defined by one or more header files that declare the classes, functions and variables that make up the interface and define named constants significant at the interface. This tool parses the header files and generates appropriate Lisp and C code to create the interface. In some cases, warning messages are generated to point out areas where programmer intervention may be required.

The Binder tools assume that the (header file) input is a complete and accurate representation of the _interface_ to an application library.

The Binder tools are not designed to examine a complete application and to somehow abstract out the interface definition. We assume that this abstraction step has already been taken by the time the input is presented to the tools.

In the case of third party libraries, this is typically the case. The library is defined by a set of header files that are needed to write correct C++ code that calls these libraries. The same headers are necessary and usually sufficient to generate the Lisp interface to the library.

In the case of user written applications or libraries, it is necessary for the user to create appropriate header files defining the interface. The binder tools will fail miserably if simply presented with the application code files. For example, the body of function definitions is entirely skipped by the source code analyzer; thus, if some type declaration occurs only in the scope of a function definition, it will never be seen.

Using the Tool

The Main Conversion Function

The binding process is normally invoked with the function ff:build-C++binding with the following arguments:

ff:build-C++binding c-or-h-file                     Function
                    &key (c-args "-D__cplusplus")
                         (lisp-out t)
                         (c-out t)

                         include
                         exclude

                         (package 
                          ff:*default-foreign-symbol-package*)

                         (case ff:*decode-intern-case*)
                         (hyphen ff:*decode-intern-hyphen*)
                         (dash ff:*decode-intern-dash*)
                         (res ff:*decode-intern-res*)

                         (verbose nil)

The first and only required argument, c-or-h-file, is the pathname to a file of C code. This file, and any files that it includes, will be parsed in order to determine what interface components need to be generated. This is normally a header file (.h file type or extension), but may also be a C source file.

The keyword argument c-args is very important to the correct functioning of the interface generator. It is a string containing all the -D and -I switches required for a complete and correct compilation of the file specified in the c-or-h-file argument. If this argument is missing or incorrect, the effect is usually to print many pre-processor and parser warnings and error messages; but it may also silently result in an incorrect interface definition.

The keyword argument lisp-out is the pathname of a file where the generated Lisp code is placed. If this argument is NIL, nothing is generated. If this argument is t, the output is printed to the current value of *standard-output*.

The keyword argument c-out is the pathname of a file where the generated C code is placed. If this argument is NIL, nothing is generated. If this argument is t, the output is printed to the current value of *standard-output*. C program text is generated when wrapper functions are needed to transmit data correctly between Lisp and C; one situation is when we need to pass structure arguments by value between Lisp and C.

The keyword arguments include and exclude determine which C files are used to generate Lisp interface components. Only one of these arguments may be used in any call to ff:build-c-binding. When specified, the value must be a pathname or a list of pathnames. The effect of the include keyword is to generate Lisp interface components only from the specified file or files; any other C files included in the compilation are ignored by the Lisp binder but may be essential to the C parsing stage of the process. The effect of the exclude keyword is to ignore the specified files in the Lisp binder. For large libraries using complex collections of include files, it may be necessary to make several passes through the binding process in order to sort out the files needed in the Lisp interface. The file selection arguments apply only to interface components generated from C statements. The current implementation cannot determine the origin of a C macro and therefore all constant definitions are always included in the generated output.

The keyword arguments case, hyphen, dash, and res control how foreign symbol strings are translated into Lisp symbols. The default values are taken from corresponding special variables described in the sections on Lisp output and customization. The default behavior is to convert foreign name strings according to the current readtable case and to signal an error if a conflict occurs. A conflict occurs when two different foreign strings map to the same Lisp symbol or when the Lisp symbol is already bound or defined as a function or macro.

The keyword argument package is the name of a package where foreign symbols are interned. The default is NIL. The effect is to use the value of the global variable ff:*default-foreign-symbol-package*. If the value of this variable is NIL, the effect is to use a package defined as follows

(defpackage "C++" (:use common-lisp foreign-functions))

It is a good idea to place all the foreign symbols in a Lisp package that does not use any other Lisp packages. This is the only definitive way to avoid symbol name conflicts.

The keyword argument verbose controls the amount of information printed when a parse or translation error is encountered. When T, a fragment of the parse tree is printed.

Generated Output

The generated Lisp output consists of

The pupose of these macros is to allow further customization of the generated code when so desired by the user.

The default definitions of these macros expand to

forms. In addition, the default definitions emit a call to the ff:bind-c-export macro to cause the conditional export of Lisp symbols corresponding to foreign identifiers.

Ordinary Function Declaration

An ordinary function declaration generates the following C wrapper function


;; clex01.h:5 <1> void foo( int, int);

/* Wrapper function to call (possibly overloaded) C++ function. */
void  ACL_foo_I_I(  int Arg0, int Arg1)
{
  foo(Arg0, Arg1);
}

(bind-c-function foo_I_I
     :unconverted-entry-name "ACL_foo_I_I"
     :c-return-type ("void")
     :return-type :void
     :c-arg-types (("int") ("int"))
     :c-arg-names (Arg0 Arg1)
     :arguments (:int :int)
     :prototype t
     )

The prefix "ACL" identifies all generated foreign names. The suffix "_I_I" is an abbreviated representation of the argument type signature. The Lisp form is generated to complete the interface.

The C wrapper function is needed to avoid conflict with other functions since the name "foo" may be overloaded with many function definitions.

Simple Class or Struct Declaration

When a class declaration is not derived from any other classes, and there are no virtual members, we generate a corresponding ACL foreign struct type.

The generated C wrappers consist of wrapper functions to call the two explicit member functions, the single constructor, and the explicit destructor.

The generated ACL interface defines foreign functions to call all the wrapper functions.


;; clex02.h:5 <2> 
;; class cl {
;;         public: int a, b, c;
;;         int foo( int);
;;         int foo( int, int);
;;         cl( int);
;;         ~ cl();
;;         };

// Begin member function wrappers for class cl


// Wrapper to call member function.
int ACL_foo_cl_I(  cl * x0, int x1)
{
  return(x0->foo(x1));
}


// Wrapper to call member function.
int ACL_foo_cl_I_I(  cl * x0, int x1, int x2)
{
  return(x0->foo(x1, x2));
}


/* Wrapper function to call constructor member function. */
cl *  ACL_cl_I(  int Arg0)
{
  return(&cl::cl(Arg0));
}


/* Wrapper function to call destructor member function. */
void  ACLd_cl(  cl * Arg0)
{
  Arg0->cl::~cl();
}


// End member function wrappers for class cl

(bind-c-typedef cl dummy-forward-class) ;;  forward declaration 
;; 
;; Begin member function wrappers for class cl
;; 
(bind-c-function foo_cl_I
     :unconverted-entry-name "ACL_foo_cl_I"
     :c-return-type ("int")
     :return-type :int
     :c-arg-types (("cl" "*") ("int"))
     :c-arg-names (Arg0 Arg1)
     :arguments ((* cl) :int)
     :prototype t
     )
(bind-c-function foo_cl_I_I
     :unconverted-entry-name "ACL_foo_cl_I_I"
     :c-return-type ("int")
     :return-type :int
     :c-arg-types (("cl" "*") ("int") ("int"))
     :c-arg-names (Arg0 Arg1 Arg2)
     :arguments ((* cl) :int :int)
     :prototype t
     )
(bind-c-function cl_I
     :unconverted-entry-name "ACL_cl_I"
     :c-return-type ("cl" "*")
     :return-type (* cl)
     :c-arg-types (("int"))
     :c-arg-names (Arg0)
     :arguments (:int)
     :prototype t
     )
(bind-c-function ~cl
     :unconverted-entry-name "ACLd_cl"
     :c-return-type ("void")
     :return-type :void
     :c-arg-types (("cl" "*"))
     :c-arg-names (Arg0)
     :arguments ((* cl))
     :prototype t
     )
;; 
;; End member function wrappers for class cl
;; 
(bind-c-type cl (:struct
  (a :int)   ;; int a
  (b :int)   ;; int b
  (c :int)   ;; int c
  ))   ;; bind-c-type cl

Complex Class Declaration

When a class is derived from other classes or contains virtual member functions, we do not generate a foreign struct type to avoid dependency on the possibly non-portable representation of complex class instances. Instead, we generate a set of access wrappers for all the members of the class.

For all the data members, we generate access wrappers that allow values to be extracted from or stored into the members.


;; clex03.h:5 <3> class base { };
(bind-c-type base (:struct
  (nil :char)   ;; C++ class without slots
  ))   ;; bind-c-type base

;; clex03.h:7 <4> 
;; class clx: public base {
;;         public: long aa;
;;         int bb[  0x00000000a];
;;         void foo( int, int);
;;         };

// Begin member function wrappers for class clx


// Wrapper to call member function.
void ACL_foo_clx_I_I(  clx * x0, int x1, int x2)
{
  x0->foo(x1, x2);
}


// End member function wrappers for class clx


// Begin member access wrappers for class clx


// Wrapper to extract scalar value of class member.
long  ACLv_clx_aa ( clx* x ) {
     return ( ((x->aa)) );
}

// Wrapper to store scalar value into class member.
void ACLw_clx_aa ( clx* x, long y ) {
      ((x->aa)) = y;
}

// Wrapper to extract address of  class member.
int * ACLa_clx_bb ( clx* x ) {
     return ( (int *)((x->bb)) );
}

// Wrapper to extract scalar value of indexed class member.
int  ACLi_clx_bb ( clx* x , int i0 ) {
     return ( ((x->bb)[i0]) );
}

// Wrapper to store scalar value into indexed class member.
void ACLj_clx_bb ( clx* x , int i0, int y ) {
      ((x->bb)[i0]) = y;
}

// End member access wrappers for class clx

(bind-c-typedef clx dummy-forward-class) ;;  forward declaration 

;; 
;; Begin member function wrappers for class clx
;; 
(bind-c-function foo_clx_I_I
     :unconverted-entry-name "ACL_foo_clx_I_I"
     :c-return-type ("void")
     :return-type :void
     :c-arg-types (("clx" "*") ("int") ("int"))
     :c-arg-names (Arg0 Arg1 Arg2)
     :arguments ((* clx) :int :int)
     :prototype t
     )

;; 
;; End member function wrappers for class clx
;; 
(ff::bind-c-class clx opaque-C++class)  

;; 
;; Begin member access wrappers for class clx
;; 
(bind-c-function clx-aa
     :unconverted-entry-name "ACLv_clx_aa"
     :c-return-type ("long")
     :return-type :long
     :c-arg-types (("clx" "*"))
     :c-arg-names (Arg0)
     :arguments ((* clx))
     :prototype t
     )
(bind-c-function set-clx-aa
     :unconverted-entry-name "ACLw_clx_aa"
     :c-return-type ("void")
     :return-type :void
     :c-arg-types (("clx" "*") ("long"))
     :c-arg-names (Arg0 Arg1)
     :arguments ((* clx) :long)
     :prototype t
     )
(defsetf clx-aa set-clx-aa)  
(bind-c-function clx-bb-a
     :unconverted-entry-name "ACLa_clx_bb"
     :c-return-type ("int" "*")
     :return-type (* :int)
     :c-arg-types (("clx" "*"))
     :c-arg-names (Arg0)
     :arguments ((* clx))
     :prototype t
     )
(bind-c-function clx-bb-i
     :unconverted-entry-name "ACLi_clx_bb"
     :c-return-type ("int")
     :return-type :int
     :c-arg-types (("clx" "*") ("int"))
     :c-arg-names (Arg0 Arg1)
     :arguments ((* clx) :int)
     :prototype t
     )
(bind-c-function set-clx-bb-i
     :unconverted-entry-name "ACLj_clx_bb"
     :c-return-type ("void")
     :return-type :void
     :c-arg-types (("clx" "*") ("int") ("int"))
     :c-arg-names (Arg0 Arg1 Arg2)
     :arguments ((* clx) :int :int)
     :prototype t
     )
(defsetf clx-bb-i set-clx-bb-i)  

;; 
;; End member access wrappers for class clx
;; 

In the case of the member 'bb' which is an array, we generate an additional wrapper to extract the address of the array.

Friend and Static Declarations

In the following C++ example, we have a class that makes several friend and static declarations.

The generated C code consists of a wrapper to call the member function of class 'helper1, a wrapper to call the explicit friend function 'FriendFunc', and a wrapper to call the public member 'WithFriends::acc'.

The private friend declaration causes no action at all since it is invisible at the interface.

The private static slot is also invisible, but both the public slot and the public member function require a wrapper function.

Note that if the same function is declared as a friend in several classses, several (duplicate) wrapper functions will be generated.

The generated ACL interface consists of foreign functions to call all the defined wrapper functions. In this example, we can also define a foreign struct for class "WithFriends" since it has no base classes and no virtual members.


;; clex04.h:6 <5> class helper1 { int a, b; public: int acc(); };

// Begin member function wrappers for class helper1


// Wrapper to call member function.
int ACL_acc_helper1(  helper1 * x0)
{
  return(x0->acc());
}


// End member function wrappers for class helper1

(bind-c-typedef helper1 dummy-forward-class) ;;  forward declaration 

;; 
;; Begin member function wrappers for class helper1
;; 
(bind-c-function acc_helper1
     :unconverted-entry-name "ACL_acc_helper1"
     :c-return-type ("int")
     :return-type :int
     :c-arg-types (("helper1" "*"))
     :c-arg-names (Arg0)
     :arguments ((* helper1))
     :prototype t
     )

;; 
;; End member function wrappers for class helper1
;; 
(bind-c-type helper1 (:struct
  (nil :int)   ;; int a
  (nil :int)   ;; int b
  ))   ;; bind-c-type helper1

;; clex04.h:9 <6> class helper2;
(bind-c-typedef helper2 dummy-forward-class) ;;  forward declaration 

;; clex04.h:13 <7> 
;; class WithFriends {
;;         friend class helper2;
;;         int x, y, z;
;;         static int StaticPrivateSlot;
;;         public: friend void FriendFunc( int, int);
;;         friend int helper1:: acc();
;;         int acc();
;;         static int StaticPublicSlot;
;;         static void StaticFunc();
;;         };

// Begin member function wrappers for class WithFriends


/* Wrapper function to call explicit friend function. */
void  ACL_FriendFunc_I_I(  int Arg0, int Arg1)
{
  FriendFunc(Arg0, Arg1);
}


// Wrapper to call member function.
int ACL_acc_WithFriends(  WithFriends * x0)
{
  return(x0->acc());
}


// Wrapper to retrieve scalar value of static class slot
int  ACL_WithFriends_StaticPublicSlot() { return WithFriends::StaticPublicSlot; }

/* Wrapper function to call static member function. */
void  ACL_WithFriends_StaticFunc( )
{
  WithFriends::StaticFunc();
}


// End member function wrappers for class WithFriends

(bind-c-typedef WithFriends dummy-forward-class) ;;  forward declaration 

;; 
;; Begin member function wrappers for class WithFriends
;; 
(bind-c-function FriendFunc_I_I
     :unconverted-entry-name "ACL_FriendFunc_I_I"
     :c-return-type ("void")
     :return-type :void
     :c-arg-types (("int") ("int"))
     :c-arg-names (Arg0 Arg1)
     :arguments (:int :int)
     :prototype t
     )

;;    friend int helper1:: acc();
;;    Wrapper for the above member is defined with class helper1
(bind-c-function acc_WithFriends
     :unconverted-entry-name "ACL_acc_WithFriends"
     :c-return-type ("int")
     :return-type :int
     :c-arg-types (("WithFriends" "*"))
     :c-arg-names (Arg0)
     :arguments ((* WithFriends))
     :prototype t
     )
(bind-c-function WithFriends_StaticPublicSlot
     :unconverted-entry-name "ACL_WithFriends_StaticPublicSlot"
     :c-return-type ("int")
     :return-type :int
     :c-arg-types nil
     :c-arg-names nil
     :arguments (:void)
     :prototype t
     )
(bind-c-function WithFriends_StaticFunc
     :unconverted-entry-name "ACL_WithFriends_StaticFunc"
     :c-return-type ("void")
     :return-type :void
     :c-arg-types nil
     :c-arg-names nil
     :arguments (:void)
     :prototype t
     )

;; 
;; End member function wrappers for class WithFriends
;; 
(bind-c-type WithFriends (:struct
  (nil :int)   ;; int x
  (nil :int)   ;; int y
  (nil :int)   ;; int z
  ))   ;; bind-c-type WithFriends

Class Templates

Each new instance of a class template is treated as a separate class declaration and causes the generation of appropriate wrapper functions and ACL foreign functions.

Function Templates

Function templates have no effect at the C++/ACL interface. In order to generate a wrapper and a foreign function, it is necessary to insert a declaring instance of the template for each combination of template arguments that are needed in the application.

The C declaration

template <class TP> void tfunc(TP x, TP y) { ...}

does not generate any interface code.

In order to generate a wrapper to call a particular instance of the template function, insert a declaration for the desired instance in the binder input:

void tfunc(double, double);

If the interface definition header files fail to mention a set of useful template instances, they can be activated without too much name polution by adding a single union type that includes all the missing instances:

typedef union { foo a; foo b; ... } ignored;

We do not look at the type in a variable declaration. If a variable declaration is the only instance of some template class, then we will miss that type. Workaround: add a dummy typedef for the template instance.

Other Generated Constructs

When the C file contains a macro definition that defines an alternate name for a declared function, the additional names appear in a :all-names keyword argument in the ff:bind-c-function form. The value is an alist of a symbol and foreign string pair for each name of the foreign function.

By default this list is sorted on the length of the foreign string. The first item in the list is used for the name of the primary lisp function defined by defforeign. The other names are used to define alternate macros with a bind-c-alternate form such as

;; #define AddAtom AddAtomA
(ff:bind-c-alternate AddAtomA (&rest args) `(AddAtom ,@args))

When a C function is declared to receive a struct by value, we need to generate some new C program code because ACL only allows struct pointers as arguments. We create an intermediate C function that receives a pointer argument and passes the dereferenced pointer to the intended function. When this situation is encountered in the C source file, the following comment is generated along with an appropriate wrapper.

;;NOTE: C wrapper needed to pass structure or union type
;;    POINT  
;;   as argument - using 't

Note how the foreign name in this case is not identical to the Lisp name of the C function.

The additional C definition is generated in the C output file.

When a C function returns a struct by value, a similar wrapper is generated to return a pointer to the structure in freshly allocated malloc memory.

When the C function returns an unsigned value, we generate a Lisp wrapper to extract the value correctly.

Customizing Lisp Output

The macros ff:bind-c-function and friends are generated in the Lisp output file in order to allow convenient user customization of the actual foreign interface. The built-in definition in file cdbind.cl emits a ff:defforeign form formed by simply selecting the appropriate components of the ff:def-c-binding form. Users with their own foreign type layer may use other components of that form to generate a more specific foreign interface call.

The function ff::decode-intern is used exclusively to convert strings to symbols in the binder. It is a function of one argument, a string or symbol. If the argument is a symbol, the function assumes it is already converted and does nothing.

If the argument is a string, this function uses the following special variables (re-bound by build-c-binding) to determine how the conversion is done:

*decode-intern-hypen*

   NIL - no effect
    T  - Insert a hyphen at every lower-to-upper transition and every
         case-sensitive-to-insensitive transition:

	MenuItemFromPoint  ==>  Menu-Item-From-Point
        Menu3              ==>  Menu-3
         
*decode-intern-dash*

   NIL - no effect
    T  - translate underscore characters to hyphens
         
*decode-intern-case*
   
   a readtable   - use the value of readtable-case for that readtable
   :READER       - use the value of readtable-case for *readtable*
   :PRESERVE     - keep the case of the string unchanged
   :DOWNCASE     - convert the string to lowercase letters
   :UPCASE       - convert the string to uppercase letters
   :INVERT       - if all the case-sensitive letters are of one case
                   switch them all to the other case, otherwise leave it be
         
*decode-intern-res*

   :error - signal an error if a name conflict occurs
   :index - try adding 0, 1, 2 to the end of the string until the
            conflict is resolved
   a list - take suffix strings in order from the list and append 
            to the foreign until the conflict is resolved.  If the
            list runs out, signal an error.

If a different name translation scheme is desired, the function decode-intern must be redefined as required.

Dealing with Windows Calling Conventions

The Microsoft C and C++ compilers for Windows allow calling conventions to be specified in function types used in declarations and argument prototypes.

The Microsoft compiler expects the keyword tokens __cdecl, __fastcall, __stdcall, or __declspec(x) to appear as qualifiers on the function type, like const or volatile declarations.

The GNU grammar and predefined macros for __cdecl and friends assume that the calling convention is specified by an attribute list following the function prototype.

In order for MS header files to parse correctly with the GNU grammar, we set the calling convention macros to null strings.

Since the ACL foreign function definition can specify the calling convention to be used, it is useful to capture the calling convention information in the C++ Binder. To do so, we must use some minor sleight of hand to work around the incompatibilites between the MS and GNU grammars.

  1. Run through the header files with the default (null) definitions of __cdecl...
  2. Search the resulting Lisp output file for occurrences of "const" and "volatile" in the C++ comment lines.
  3. If all 3 calling conventions must be distinguished, and neither "const const" nor "volatile" declarations were found, we can use the following macro definitions:
    #define __stdcall const const
    #define __cdecl   volatile
    #define __stdcall volatile volatile
    
  4. Modify the bind-c-function macro to scan the :c-modifiers argument and emit the appropriate :convention argument:
    If "const" appears more than once in :c-modifiers,
       emit  :convention :stdcall
    If "volatile" appears only once
       emit  :convention :c
    If "volatile" appears more than once
       emit  :convention :fastcall
    

Warnings and Notes in Generated Output

Messages of the form

 ... ;;WARNING: ... 

are emitted when the binder detects a situation where the generated code may function incorrectly or when the binder is unable to generate any correct code at all.

Messages of the form

 ... ;;NOTE: ... 

are emitted when the binder generates additional Lisp or C code that may be needed to use the interface effectively.

In many cases, inspection of the generated code and specific application knowledge will reveal that the generated code is adequate. In some cases it may be necessary to modify the generated code.

The binder generates possibly incorrect code with a warning to prevent a possible cascade of subsequent warnings that might be caused by generating nothing. This might be the case if a type cannot be generated correctly

(FF:BIND-C-CONSTANT "FOO" "StrData")  ;;WARNING: C code expects wide string"

This warning is emitted when the C string is defined with the L modifier.


;; c-ex03.h:6 <7> typedef long long LongLong;

;;WARNING:  'long long' is implemented as a struct of 2 long!
(BIND-C-TYPE LONGLONG LONG-LONG)  

The binder defines and emits the following 64 and 128-bit types:

long long              -> FF:LONG-LONG
unsigned long long     -> FF:UNSIGNED-LONG-LONG
long double            -> FF:LONG-DOUBLE

These are defines as structs of two long or two double values. This definition has the correct storage size but may or may not have the correct alignment. In addition, none of these definitions have a numeric equivalent in ACL, and thus do not behave as numbers in Lisp code.

;;WARNING: Eval of above Lisp form resulted in error:
;;   Lisp error condition

This message is usually emitted following a foreign type definition. This result is likely to produce a cascade of subsequent error messages.

C Functions with Varying Number of Arguments

Since all C++ functions require a wrapper, varying number of arguments cannot be supported at the general Lisp/C++ interface. Only a fixed number of arguments may be passed through the wrapper. In that case we generate the warning of the form

;;WARNING: This wrapper function will pass exactly 2 arguments

Reporting Problems

The C parser and parse-tree decoder we use is far from complete from the point of view of C language semantics. We have attempted to handle a large collection of commonly found cases, but many others are still possible. The structure of the parse-tree decoder is modular and extensible so that new cases may be added easily when necessary.

When a new unhandled case is encountered, and the :verbose argument to build-c-binding is T, we print a warning message of the form

;;WARNING Unknown STATEMENT
#|
:tag1
parse-tree-dump-1
:tag2
parse-tree-dump-2
...
|#

and continue. In most cases, if you send us

we may be able to create an extension to the parse-tree decoder in short order.

Technical Details

The C++ Grammar

The parser used in this tool is derived from the GNU C++ compiler. It uses the Bison grammar for C++ included with the distribution of GCC. The specific version of the grammar and parser can be obtained in the usual manner by calling our modified version of cpp with the -version switch.

Wrapper Name Generation

This section describes in more detail how function names and type names are constructed for foreign wrappers and as Lisp symbols. The construction rules are not designed to guarantee unique names in all possible cases. If a conflict does occur in a real situation, we will need to adjust the process to eliminate it.

Ordinary Function
ACL_funcsig(arg, ...)
  (funcsig arg ...)

  funcsig -> funcname_argsig_argsig...
  argsig  -> _type   _typeX   _typeXX
          ;; Built-in types are abbreviated:
    type  -> C    char
          -> D    double
          -> F    float
          -> G    signed
          -> I    int
          -> L    long
          -> S    short
          -> U    unsigned
          -> V    void
          ;; Modifiers are also abbreviated (most as suffixes):
       X  -> M    array declaration (one for each dim)
          -> O    functional argument such as   type(*)(argtype, ...)
          -> P    pointer    '*'
          -> R    reference  '&'
          -> T    template argument (prefix)
Ordinary Member Function
ACL_memsig(instance, arg ...)
  (memsig instance, arg ...)

Qualified Class Name
Qualified class name foo::bar::baz  
  shows up as foo_bar_baz in Lisp function names
  shows up as foo/bar/baz in Lisp foreign type name


ACL_classname(arg, ...)          ;; constructor
  (classname arg ...)

ACLd_classname(instance)         ;; destructor
  (~classname instance)

ACL_class_slot()                 ;; static slot extractor
  (class-slot)
Slot Accessors
ACLv_class_slot(instance)                   ;; extract value of slot
  (class-slot instance)

ACLs_class_slot(instance, val)              ;; store value in slot
  (setf (class-slot instance) val)   
  (set-class-slot instance val)


ACLa_class_slot(instance)                   ;; extract address of slot
  (class-slot-a instance)

ACLu_class_slot(instance, ptr)              ;; store indirect value in slot
  (setf (class-slot-a instance) val)
  (set-class-slot-a instance val)


ACLi_class_slot(instance, i0, i1, ...)      ;; extract indexed value from member
  (class-slot-i instance i0 i1 ...)

ACLj_class_slot(instance, i0, i1, ..., val) ;; store value in indexed member
  (setf (class-slot-i instance i0 i1 ...) val)
  (set-class-slot-i instance i0 i1 ... val)


ACLr_class_slot(instance, i0, i1, ...)      ;; extract pointer to index part 
                                            ;; of member
  (class-slot-r instance i0 i1 ...)

ACLq_class_slot(instance, i0, i1, ..., ptr) ;; store indirect value in indexed 
                                            ;; part of member
  (setf (class-slot-r instance i0 i1 ...) val)
  (set-class-slot-r instance i0 i1 ... val)

C Macros

C macros are processed from the output of the GNU C pre-processor called with the -dM switch. This produces a list of #define lines that does not reflect the input order of the definitions. When dependencies between definitions can be determined, we order the corresponding Lisp definitions accordingly. Otherwise, the Lisp definitions are ordered alphabetically by name.

Macro definitions are parsed by our own ad hoc parser that is described in the C Binder manual.