2000Oct31 Update to version 1.6.1 - ACL 6.0
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.
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.
The generated Lisp output consists of
ff:bind-c-function
or ff:bind-c-alternate
forms
for declared C functions or for generated C wrapper functions. ff:bind-c-type
or ff:bind-c-typedef
forms for
declared C types ff:bind-c-constant
forms for C constants defined with #define
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
ff:defforeign
, ff:def-c-type
or ff:def-c-typedef
, and defconstant
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.
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.
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
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.
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
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 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 { fooa; 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.
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.
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.
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.
#define __stdcall const const #define __cdecl volatile #define __stdcall volatile volatile
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
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.
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
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.
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.
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.
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)
ACL_memsig(instance, arg ...) (memsig instance, arg ...)
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)
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 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.
The tool consists of two Unix executable files and 3 Lisp source files. The Lisp files are loaded in the following sequence:
:ld loadcplb
All three Lisp files are required in order to run the Binder. Only one file, cdbind
,
is needed to compile and/or load the output of the binder. This file may also need to be
modified if the output of the binder needs to be customized.
The following two variables must be initialized in loadcplb.cl
to the
correct pathname for the two Unix executables:
ff:*c-parser-cpp*
ff:*c-parser-cc1plus*
Other variables:
ff:*default-foreign-symbol-package*
The setting of this variable is discussed above.
ff:*export-foreign-symbols*
When this variable is set to a non-NIL value, all symbols created in the above package are exported. When this variable is non-NIL, foreign symbols are referenced with single-colon notation from other Lisp packages. When this variable is set to NIL, double-colon notation must be used for all symbols in the foreign-symbol package.
NOTE: in the current implementation, only symbols passed to ff:bind-c-export
are exported.
ff:*c-compiler-macro-names*
The value of this variable is a list of strings naming variables that should be ignored by the interface generator. These are typically state variables for the compilation and do not affect the usage of the interface.