Re: Common FFI declarations

  • From: Jay Carlson <nop@xxxxxxx>
  • To: luajit@xxxxxxxxxxxxx
  • Date: Tue, 8 May 2012 12:10:35 -0400

On May 8, 2012, at 10:09 AM, Mike Pall wrote:

> Sadly, it's not that easy to automatically generate the target
> definitions. And the host compiler may not grok the target includes.


> So one probably has to parse the output of $(CROSS)$(CC) -E and
> grab only the needed definitions, but without all of the internal
> cruft.

Here's a snippet from preprocessor-fodder and a Makefile to go with it:

====
/* We would like to avoid including half the C library. Therefore
   lines generated between BARRIER1 and BARRIER2 are snipped
   from output. */

#include <stdint.h>
#include <stddef.h>

BARRIER1

#include <stdio.h>
#include <errno.h>
#include <math.h>
[...]

BARRIER2

/* We don't care about the internals of stdio, but some
   debug functions mention it. */

typedef struct { void*dummy;} FILE;
extern FILE *stdin, *stdout, *stderr;

#include "config.h"
#include "libfoo/foostring.h"
#include "libfoo/fooformat.h"
[...]

#include "lua-fooformat-const.h"
====
lua-foo.i: lua-foo.E
              $(CC) -x c -E $(CPPFLAGS) $(CFLAGS) $< | \
                awk '/^BARRIER1/,/^BARRIER2/ {next} /^#/ || /^[ \t]*$$/ {next} 
{print}' >$@
====

The contents of lua-foo.i are then suitable for feeding into the ffi parser.

Wait, so what's that lua-fooformat-const.h file at the end? It exists because 
libfoo uses "#define FOO_MYCONST expr" a lot. It can *mostly* be mechanically 
generated:

perl -l -n -e '/^#define\s+(\w+)\s/ && print "static const int l$1 = $1;";' 
$s/libfoo/fooformat.h >lua-fooformat-const.h

This generates a lua-format-const.h containing:

====
static const int lFOO_FLAG_OPT_X = FOO_FLAG_OPT_X;
static const int lFOO_FLAG_OPT_Y = FOO_FLAG_OPT_Y;
====

which the previous preprocessor stage will deal with. It is stupid; if you have 

#ifdef NEW_API
#define FOO_FLAG_OPT_X 8
#else
#define FOO_FLAG_OPT_X 10
#endif

it will spit out two static const int lFOO_FLAG_OPT_X declarations. I 
hand-edited that file anyway to deal with other stuff. It seemed like less work 
than writing a smarter #define-reader.

> With a cross-compiler you can't run the target
> program. 

(This is so cute I can't resist posting about it over and over again.)

This is not necessary in many cases, surprisingly. Autoconf gained this 
functionality when I wasn't looking. AC_LANG_BOOL_COMPILE_TRY will evaluate 
boolean expressions at compile time. 
http://git.savannah.gnu.org/cgit/autoconf.git/tree/lib/autoconf/c.m4?h=branch-2.59#n229
 . The basic trick is to declare

static int test_array[1 - 2 * !(your_boolean_expression)];

which is a compile-time error when it evaluates to declaring a negative length 
array.

AC_COMPUTE_INT will just compile and run a printf() of an expression when 
compiling natively, but falls back to _AC_COMPUTE_INT_COMPILE when it's not 
able to run programs. In essence, it can use AC_LANG_BOOL_COMPILE_TRY to 
binary-search the integers for the value of the expression, one trial 
compilation at a time. 
http://git.savannah.gnu.org/cgit/autoconf.git/tree/lib/autoconf/general.m4?h=branch-2.59#n2462
 is the actual implementation.

Jay

Other related posts: