rpav / cl-autowrap

(c-include "file.h") => complete FFI wrapper
BSD 2-Clause "Simplified" License
210 stars 41 forks source link

why are inline functions ignored? #123

Closed oldk1331 closed 1 year ago

oldk1331 commented 1 year ago

In parse.lisp, there is:

(defmethod parse-form (form (tag (eql 'function)) &key &allow-other-keys)
  (alist-bind (name inline parameters return-type variadic) form
    (unless inline
      (let ((sym (foreign-type-symbol name :cfun *package*)))
        (let ((cfun-fields (parse-fields parameters :cparam)))
          (push sym *foreign-function-list*)
          `(define-foreign-function '(,sym ,name
                                      ,@(when variadic '(:variadic-p t)))
               ',@(parse-type return-type (aval :tag return-type))
             ',cfun-fields))))))

I don't know why inlines functions are intentionally ignored.

In my case, this is library FLINT, in /usr/include/flint/fmpz.h for example, very common functions like fmpz_init are unable to call in Lisp because it is declared inline.

rpav commented 1 year ago

The short answer is because inline functions aren't in the library. They don't get compiled to symbols that are included in the .so, and therefore there is no way to call them. If autowrap tried to include them, it would result in an error when it tried to resolve their symbols.

But if you look at the symbols in libflint.so, fmpz_init is there!

$ nm libflint.so | grep 'T fmpz_init`
00000000000e3200 T fmpz_init
 :
$

This is because the authors of the flint library were aware of this; the definition is actually:

FMPZ_INLINE
void fmpz_init(fmpz_t f) ...

If you look up a bit, we find:

#ifdef FMPZ_INLINES_C
#define FMPZ_INLINE FLINT_DLL
#else
#define FMPZ_INLINE static __inline__
#endif

If you look in config.h or config.h.in we see the usual MSVC incantation for DLLs, and in fmpz/inlines.c they #define FMPZ_INLINES_C, which is likely the place where all of this gets pulled in and defined once for real.

So the long story is that, with a lot of special setup, you can handle including inline functions in the .so: you make them not-inline.

So how does this help you with autowrap?

What you want to do is make your own .h file that you include with your cl-flint (or whaever) wrapper, that looks something like:

#define FMPZ_INLINES_C

// Potentially... you may need to #include the flint config.h or 
// duplicate their block here; I actually don't recall how this 
// interacts with DLLs
#define FLINT_DLL

#include <fmpz.h>
// any other fmpz includes if necessary...

Then point autowrap at that. You may of course need to tweak this further as necessary. It's not uncommon to have to tweak or locally define things to properly set up the library for autowrap; after all, the library does for itself!

oldk1331 commented 1 year ago

Thanks for your reply!

I saw the FMPZ_INLINE as well and I was confused by it. I thought it was a hack to define FMPZ_INLINES_C. But now I understand this is the right way to go.