# define_c_func

Definition: | integer rid = define_c_func(object lib, object fname, sequence args, atom return_type, bool bCrash=true)
Description: | Define the characteristics of a C function that you wish to call from your Phix program. A small integer, known as a routine id, is returned, or -1 if the function cannot be found (and bCrash is false).
pwa/p2js: | Not supported.
Comments: | lib is either an (atom) address returned by open_dll(), or {}. If lib is an atom, fname is the (string) name of a function within that dll, optionally as either {'+',"name"} or "+name" to specify the CDECL calling convention. If lib is {}, fname is the (atom) address of a machine code function, optionally as {'+',addr} to specify the CDECL calling convention. CDECL is the default/only calling convention on a Linux system. STDCALL is the default in a Windows system. The CDECL convention requires the callee to clean up the stack, and is therefore more suited to a variable number of arguments, whereas with STDCALL the routine cleans up the stack before returning, however it is entirely dependent on how the dll/so was built. args is a list of the parameter types for the function, return_type should be self explanatory. The define_cffi_func routine is a string-based wrapper of this routine, which you may find slightly easier to use. The following C types are predefined (in psym.e/syminit):
                        C_BYTE = #01000001,     -- an 8 bit signed integer
                        C_CHAR = C_BYTE,
                       C_UCHAR = #02000001,     -- an 8 bit unsigned integer
                       C_UBYTE = C_UCHAR,
                       C_SHORT = #01000002,     -- a 16 bit signed integer
                        C_WORD = C_SHORT,
                      C_USHORT = #02000002,     -- a 16 bit unsigned integer
                         C_INT = #01000004,     -- a 32 bit signed integer
                        C_BOOL = C_INT,
                        C_UINT = #02000004,     -- a 32 bit unsigned integer
                       C_DWORD = C_UINT,
                       C_INT64 = #01000008,     -- a 64 bit signed integer
                       C_QWORD = #02000008,     -- a 64 bit unsigned integer
                        C_LONG = iff(machine_bits()=32 or platform()=WINDOWS ? C_INT  : C_INT64),
                       C_ULONG = iff(machine_bits()=32 or platform()=WINDOWS ? C_UINT : C_QWORD),
                         C_PTR = iff(machine_bits()=32                       ? C_UINT : C_QWORD),
                     C_POINTER = C_PTR,
                      C_HANDLE = C_PTR,
                        C_HWND = C_PTR,
                       C_FLOAT = #03000004,     -- a 32-bit float
                      C_DOUBLE = #03000008      -- a 64-bit float
The resulting routine id, rid, can be passed to c_func() when you want to call the C function. You can pass or return any C integer type or pointer type. You can also pass a Phix atom as a C double or float, and get a C double or float returned to you as a Phix atom. In C (on Windows and Linux), parameter types which use 4 bytes or less are all passed the same way, so it is not necessary to be exact. However the distinction between signed and unsigned may be important when you specify the return type of a function. Currently, there is no way to pass a C structure by value or get a C structure as a return result. You can only pass a pointer to a structure and get a pointer to a structure as a result. If you are not interested in using the value returned by the C function, you should instead define it with define_c_proc() and call it with c_proc(). The C type "long double" should be avoided at all costs - it is highly compiler-dependent and can be 10/8/12/16 bytes aligned to an 8/2/4/16 byte boundary.
Auxillary functions: | integer res = sizeof(integer c_type) - returns the size in bytes of the above types. string name = c_name(integer rid) - returns the name as originally passed to the define_c_func/proc that yielded rid. This is expected to be useful for diagnostic purposes only. Deliberately crashes should fname not have been a string. [Aside: any args/return details would not be very helpful, maybe a cffi_info() might offer something better...]
Example: |
    -- user32.dll contains the LoadIconA C function which takes a C pointer
    --            and a C int as parameters and returns a C int as a result.
    atom user32 = open_dll("user32.dll")
    integer xLoadIcon = define_c_func(user32, "LoadIconA", {C_PTR, C_INT}, C_INT)
The naming convention of xLoadIcon is partly a personal preference but it can also improve the help system in Edita/Edix, when F1 is keyed, as they both strip a leading "x" when looking for a chm file to open.
Implementation: | See builtins\VM\pcfunc.e (an autoinclude) for details of the actual implementation. The ancilliary function sizeof() is however implemented in the autoinclude builtins\dll.e
See Also: | c_func, define_cffi_func, define_c_proc, c_proc, open_dll, Calling C Functions, platform, machine_bits
Technicalia | C_LONG, C_ULONG, C_PTR are in fact aliases, and as such removed from the symbol table during binding. Their definitions, along with any further aliases of them, are all actually deferred until after any format directive has had a chance to be processed, and therefore now use the compiler internals X64 and PE (see pglobals.e), rather than machine_bits() and platform() directly. This allows, for instance, a 32-bit windows PE compiler to build a 64-bit linux ELF without problems, though to be fair there are likely more 32<->64 bit hiccups than PE<->ELF. Aside: as long as we can cross-compile a viable compiler, I don’t consider it a big issue should it need an extra round of self-hosted compilation on the target machine to iron out the last few remaining niggles, and certainly not if the first step of installation is a self-hosting round check compilation anyway.