# Xen

The Xen package provides macros and procedures making it possible for the same C code to support several different embedded (or extension) languages. Currently supported fully are Guile and no language; Ruby is usable, but not completely implemented.

Here's a complete program that defines a function (named "fnc" in the extension language) that takes an integer argument and increments it, a variable (named "var" in the extension language) that is initialized to 32, a constant (named "twelve") that has the value 12, then places you in a read-eval-print loop:

    #include <stdio.h>
    #include "xen.h"

    static XEN orig_function(XEN argument)
    {
      XEN_ASSERT_TYPE(XEN_INTEGER_P(argument), argument, XEN_ONLY_ARG, "fnc", "an integer");
      fprintf(stdout, "argument is %d\n", XEN_TO_C_INT(argument));
      return(C_TO_XEN_INT(XEN_TO_C_INT(argument) + 1));
    }

    #ifdef ARGIFY_1
      XEN_NARGIFY_1(function, orig_function);
    #else
      #define function orig_function
    #endif

    static XEN variable;

    int main(int argc, char **argv)
    {
      xen_initialize();

      XEN_DEFINE_VARIABLE("var", variable, C_TO_XEN_INT(32));
      XEN_DEFINE_CONSTANT("twelve", 12, "this is 12");
      XEN_DEFINE_PROCEDURE("fnc", function, 1, 0, 0, "this is our function");

      fprintf(stdout, "we're running: %s\n", xen_version());

      xen_repl(argc, argv);
      return(0);
    }

The "ARGIFY" step is needed for those languages that assume one calling sequence for a C-defined function; we have to wrap up the actual call in whatever sequence the extension language wants.

Currently constants are assumed to be integers. Type checks are handled by macros such as XEN_INTEGER_P; type conversions by macros such as XEN_TO_C_INT or C_TO_XEN_INT.

If you use Xen, you'll need to deal with the various compile-time switches that configure it for the current version of the extension language. The simplest approach would be to copy the relevant portions of Snd's or sndlib's configure.ac. This is an autoconf (2.50 or later) input file that generates the script that runs the configuration checks. Its output is guided by config.h.in (which has the various macros) and makefile.in (the makefile template). The Ruby portion is:

    AC_ARG_WITH(ruby-prefix,[  --with-ruby-prefix=PFX where Ruby is installed],
                ruby_prefix="$withval", ruby_prefix="")

    AC_ARG_WITH(ruby,
    	[  --with-ruby	  	try to use Ruby as the extension language],
      	if test "$with_ruby" = yes ; then
                AC_MSG_CHECKING([for Ruby])
    	    RUBY_VERSION=`ruby -e 'puts RUBY_VERSION'`
                if test "$RUBY_VERSION" > "0" ; then
    	      AC_DEFINE(HAVE_RUBY)
    	      AC_DEFINE(HAVE_EXTENSION_LANGUAGE)
                  AC_MSG_RESULT($RUBY_VERSION)
                  if test x$ruby_prefix != x ; then
          		GUILE_CFLAGS=" -I$ruby_prefix"
                    GUILE_LIBS="-L$ruby_prefix"
                  else
                    RUBY_LOC=`ruby -e [['puts \$:[0]']]`  # good grief...
                    GUILE_CFLAGS="-I$RUBY_LOC"
    	        GUILE_LIBS="-L$RUBY_LOC"
                    RUBY_LOC=`ruby -e [['puts \$:[1]']]`
    	        if test "$RUBY_LOC" != nil && test "$RUBY_LOC" != "."; then
                      GUILE_CFLAGS="$GUILE_CFLAGS -I$RUBY_LOC"
    	          GUILE_LIBS="$GUILE_LIBS -L$RUBY_LOC"
                      RUBY_LOC=`ruby -e [['puts \$:[2]']]`
    	          if test "$RUBY_LOC" != nil && test "$RUBY_LOC" != "."; then
                        GUILE_CFLAGS="$GUILE_CFLAGS -I$RUBY_LOC"
    	            GUILE_LIBS="$GUILE_LIBS -L$RUBY_LOC"
                        RUBY_LOC=`ruby -e [['puts \$:[3]']]`
    	            if test "$RUBY_LOC" != nil && test "$RUBY_LOC" != "."; then
                          GUILE_CFLAGS="$GUILE_CFLAGS -I$RUBY_LOC"
    	              GUILE_LIBS="$GUILE_LIBS -L$RUBY_LOC"
                          RUBY_LOC=`ruby -e [['puts \$:[4]']]`
    	              if test "$RUBY_LOC" != nil && test "$RUBY_LOC" != "."; then
                            GUILE_CFLAGS="$GUILE_CFLAGS -I$RUBY_LOC"
    	                GUILE_LIBS="$GUILE_LIBS -L$RUBY_LOC"
                            RUBY_LOC=`ruby -e [['puts \$:[5]']]`
    	                if test "$RUBY_LOC" != nil && test "$RUBY_LOC" != "."; then
                              GUILE_CFLAGS="$GUILE_CFLAGS -I$RUBY_LOC"
    	                  GUILE_LIBS="$GUILE_LIBS -L$RUBY_LOC"
                              RUBY_LOC=`ruby -e [['puts \$:[6]']]`
    	                  if test "$RUBY_LOC" != nil && test "$RUBY_LOC" != "."; then
                                GUILE_CFLAGS="$GUILE_CFLAGS -I$RUBY_LOC"
    	                    GUILE_LIBS="$GUILE_LIBS -L$RUBY_LOC"
      		          fi
    	 	        fi
    	              fi
    	  	    fi
    		  fi
    	        fi
    	      fi
                  GUILE_LIBS="$GUILE_LIBS -lruby -ldl -lcrypt"
    	      AC_CHECK_LIB(readline, readline,
    			   [AC_DEFINE(HAVE_READLINE)
    			    GUILE_LIBS="$GUILE_LIBS -lreadline -lncurses"], ,"-lncurses")
                  AC_SUBST(GUILE_LIBS)
                  AC_SUBST(GUILE_CFLAGS)
                else
                  AC_MSG_WARN([can't find Ruby!])
                fi
    	fi)

The endless RUBY_LOC foolishness is trying to make a list of all possible locations of the ruby header and library files. (It's using the name GUILE_LIBS for historical reasons). The corresponding Guile code is:

    if test "$with_ruby" != yes ; then

    GUILE_LIBS=""
    GUILE_CFLAGS=""
    GUILE_CONFIG_path=""
    GUILE_LIB_path=""

    if test "$with_no_guile" = yes ; then
      AC_DEFINE(HAVE_GUILE, 0)
      AC_DEFINE(HAVE_EXTENSION_LANGUAGE, 0)
    else

    AC_CHECK_FILE(/usr/lib/snd/bin/guile-config,[
      GUILE_CONFIG_path=/usr/lib/snd/bin/
      GUILE_LIB_path=/usr/lib/snd/lib
    	])

    AC_MSG_CHECKING(for Guile)
    if (${GUILE_CONFIG_path}guile-config link > /dev/null) 2>&1; then
      GUILE_CONFIG_works=yes
    else
      GUILE_CONFIG_works=no
      AC_MSG_RESULT(no)
    fi

    if test $GUILE_CONFIG_works = yes; then
      GUILE_CFLAGS="`${GUILE_CONFIG_path}guile-config compile`"
      if test "$GUILE_LIB_path" != "" ; then
        GUILE_LIBS="-Xlinker -rpath -Xlinker $GUILE_LIB_path `${GUILE_CONFIG_path}guile-config link`"
      else
        GUILE_LIBS="`${GUILE_CONFIG_path}guile-config link`"
      fi

      guile_version="`${GUILE_CONFIG_path}guile -c '(display (version))'`"
      AC_MSG_RESULT($guile_version)

      if test "`${GUILE_CONFIG_path}guile -c '(display (string>=? (version) "1.3.4"))'`" != "#t"; then
        AC_MSG_WARN(Snd needs Guile 1.3.4 or later)
        AC_DEFINE(HAVE_GUILE,0)
        AC_DEFINE(HAVE_EXTENSION_LANGUAGE, 0)
      else
        AC_SUBST(GUILE_CFLAGS)
        AC_SUBST(GUILE_LIBS)
        AC_DEFINE(HAVE_GUILE)
        AC_DEFINE(HAVE_EXTENSION_LANGUAGE)

        OLD_LIBS="$LIBS"
        LIBS="$GUILE_LIBS"
        OLD_CFLAGS="$CFLAGS"
        CFLAGS="$GUILE_CFLAGS"
        AC_CHECK_LIB(guile, scm_create_hook,        [AC_DEFINE(HAVE_SCM_CREATE_HOOK)])
        AC_CHECK_LIB(guile, scm_set_smob_apply,     [AC_DEFINE(HAVE_APPLICABLE_SMOB)])
        AC_CHECK_LIB(guile, scm_remember_upto_here, [AC_DEFINE(HAVE_SCM_REMEMBER_UPTO_HERE)])
        AC_CHECK_LIB(guile, scm_make_real,          [AC_DEFINE(HAVE_SCM_MAKE_REAL)])
        AC_CHECK_LIB(guile, scm_strport_to_string,  [AC_DEFINE(HAVE_SCM_STRPORT_TO_STRING)])
        AC_CHECK_LIB(guile, scm_object_to_string,   [AC_DEFINE(HAVE_SCM_OBJECT_TO_STRING)])
        AC_CHECK_LIB(guile, scm_num2long_long,      [AC_DEFINE(HAVE_SCM_NUM2LONG_LONG)])
        AC_CHECK_LIB(guile, scm_num2int,            [AC_DEFINE(HAVE_SCM_NUM2INT)])
        AC_CHECK_LIB(guile, scm_c_make_vector,      [AC_DEFINE(HAVE_SCM_C_MAKE_VECTOR)])
        AC_CHECK_LIB(guile, scm_c_define,           [AC_DEFINE(HAVE_SCM_C_DEFINE)])
        AC_CHECK_LIB(guile, scm_c_define_gsubr,     [AC_DEFINE(HAVE_SCM_C_DEFINE_GSUBR)])
        AC_CHECK_LIB(guile, scm_c_eval_string,      [AC_DEFINE(HAVE_SCM_C_EVAL_STRING)])
        AC_CHECK_LIB(guile, scm_list_n,             [AC_DEFINE(HAVE_SCM_LIST_N)])
        AC_CHECK_LIB(guile, scm_str2symbol,         [AC_DEFINE(HAVE_SCM_STR2SYMBOL)])
        AC_CHECK_TYPE(scm_t_catch_body,             [AC_DEFINE(HAVE_SCM_T_CATCH_BODY)], , [#include <guile/gh.h>])
        LIBS="$OLD_LIBS"
        CFLAGS="$OLD_CFLAGS"

        if test "`${GUILE_CONFIG_path}guile -c '(display (string<=? (version) "1.3.4"))'`" = "#t"; then
          echo found old out-of-date Guile library
        fi
      fi
    else
      AC_DEFINE(HAVE_GUILE,0)
      AC_DEFINE(HAVE_EXTENSION_LANGUAGE, 0)
    fi
    fi
    fi

Guile has changed a lot over the years; these macrors are trying to pick out which version is currently available. The /usr/lib/snd/bin stuff is specific to CCRMA.

The corresponding portion of config.h.in is

    #undef HAVE_GUILE
    #undef HAVE_RUBY
    #undef HAVE_EXTENSION_LANGUAGE
    #undef HAVE_APPLICABLE_SMOB
    #undef HAVE_SCM_REMEMBER_UPTO_HERE
    #undef HAVE_SCM_MAKE_REAL
    #undef HAVE_SCM_CREATE_HOOK
    #undef HAVE_SCM_STRPORT_TO_STRING
    #undef HAVE_SCM_OBJECT_TO_STRING
    #undef HAVE_SCM_NUM2LONG_LONG
    #undef HAVE_SCM_C_MAKE_VECTOR
    #undef HAVE_SCM_C_DEFINE
    #undef HAVE_SCM_C_DEFINE_GSUBR
    #undef HAVE_SCM_C_EVAL_STRING
    #undef HAVE_SCM_NUM2INT
    #undef HAVE_SCM_LIST_N
    #undef HAVE_SCM_STR2SYMBOL
    #undef HAVE_SCM_T_CATCH_BODY

And the corresponding portion of makefile.in:

    GUILE_LIBS = @GUILE_LIBS@
    GUILE_CFLAGS = @GUILE_CFLAGS@

I hope the xen.h names are largely self-explanatory; see the Snd sources for many examples.