/* libq.h: C interface library */

/* ALWAYS include gmp.h prior to this header when your module links to a
   static version of the GMP library. This is required to properly initialize
   GMP memory management in your module. */

/*  Q eQuational Programming System
    Copyright (c) 1991-2001 by Albert Graef
    <ag@muwiinfa.geschichte.uni-mainz.de, Dr.Graef@t-online.de>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 1, or (at your option)
    any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef LIBQ_H
#define LIBQ_H 1

#include <stdlib.h>
#include <stdio.h>

/* __declspec magic required to make Windows DLLs work */
#ifndef __DLL_BUILD
#if defined _WIN32
#define __DLLIMPORT __declspec(dllimport)
#define __DLLEXPORT __declspec(dllexport)
#else
#define __DLLIMPORT extern
#define __DLLEXPORT
#endif
#endif

/* WIN32 KLUDGE ALERT: Mingw and MSVC malloc() are not compatible. This may
   lead to memory corruption when using mingw- and msvc-compiled modules
   together. We redefine the memory management routines here to use routines
   provided by libq instead. */

#ifndef __DLL_BUILD
#if defined _WIN32
#include <stdlib.h>
#define malloc libq_malloc
#define realloc libq_realloc
#define calloc libq_calloc
#define free libq_free
#endif
#endif

/* support C++ */
#ifdef __cplusplus
extern "C" {
#endif

__DLLIMPORT void *libq_malloc(size_t size);
__DLLIMPORT void *libq_realloc(void *p, size_t size);
__DLLIMPORT void *libq_calloc(size_t num, size_t size);
__DLLIMPORT void libq_free(void *p);

/* Q expression type (opaque). */

typedef void *expr;

/* Declaration macros. */

/* The module header. */

#ifndef __GNU_MP_VERSION

#define MODULE(id) static int __modno = -1; \
__DLLEXPORT void __ ## id ## __initmod(int modno) { __modno = modno; }

#else

#define MODULE(id) static int __modno = -1; \
__DLLEXPORT void __ ## id ## __initmod(int modno, \
void *(*gmp_allocate) (), void *(*gmp_reallocate) (), void (*gmp_free) ()) \
{ \
  __modno = modno; \
  mp_set_memory_functions((void *(*) (size_t))gmp_allocate, \
			  (void *(*) (void *, size_t, size_t))gmp_reallocate, \
			  (void (*) (void *, size_t))gmp_free); \
}

#endif

/* User-defined module initialization/finalization code. */

#define INIT(id) \
__DLLEXPORT void __ ## id ## __init(void)

#define FINI(id) \
__DLLEXPORT void __ ## id ## __fini(void)

/* Declare functions and destructors. */

#define FUNCTION(id,name,argc,argv) __DLLEXPORT expr __F__ ## id ## _ ## name (int argc, expr *argv)

#define DESTRUCTOR(id,name,ptr) __DLLEXPORT void __D__ ## id ## _ ## name (void *ptr)

/* Call an external function in C. */

#define FUNCALL(id,name,argc,argv) __F__ ## id ## _ ## name(argc,argv)

/* Special expr return values in case of error conditions. */

#define __FAIL NULL		/* failure to apply dl function (no error) */
#define __ERROR __mkerror()	/* error in dl function (aborts evaluation) */

/* Macros to access the symbol table. sym(name) returns the integer code for
   symbol name (without double quotes). type(name) does the same for type
   symbols. You can use these macros to pass symbol values to mksym() and
   mkobj(). */

#define sym(name) __getsym( #name , __modno)
#define type(name) __gettype( #name , __modno)

/* Predefined function and type symbols. */

__DLLIMPORT const int truesym, falsesym, nilsym, voidsym;
__DLLIMPORT const int inttype, floattype, booltype, strtype, filetype,
  listtype, tupletype;

/* Expression construction. */

/* Expressions are allocated dynamically on the interpreter's expression
   heap. If this fails, NULL is returned, and any dynamic argument values are
   collected using dispose(). You can use the constructed expressions as
   return values in interface functions, or as temporary values during the
   execution of an interface function. In the former case the return value
   will be evaluated by the interpreter automatically. In the latter case the
   values should be destroyed using dispose() (see below) before the interface
   function finishes. Note that it is generally *not* save to assume that a
   temporary expression persists after an external function invokation, since
   the garbage collector will collect all unreachable expression nodes,
   including temporaries created by hosted modules. */

/* Atomic objects: integers, real values, strings, files, and pipes created
   with popen(3). */

/* Note that mkint() and mkuint() can only be used to construct machine
   integers. For constructing arbitrary-sized integers from GMP mpz_t values,
   the mkmpz() routine is used. It takes a mpz_t value (cast to void*) as its
   argument. To avoid the overhead of taking an extra copy, this value is
   taken over by the interpreter, hence the calling program must _not_
   manipulate the passed value afterwards. The mkmpz_float() function is a
   convenience function which creates an integer expression from a floating
   point number. */

__DLLIMPORT expr mkint(long i);
__DLLIMPORT expr mkuint(unsigned long i);
__DLLIMPORT expr mkmpz(void *z /* mpz_t z */);
__DLLIMPORT expr mkmpz_float(double f);
__DLLIMPORT expr mkfloat(double f);
__DLLIMPORT expr mkstr(char *s);
__DLLIMPORT expr mkfile(FILE *fp);
__DLLIMPORT expr mkpipe(FILE *fp);

/* Function (and variable) symbols and external objects. The mkbool() routine
   is a convenience function which creates a Bool value from an int (0 for
   false, nonzero for true). */

__DLLIMPORT expr mksym(int sym);
__DLLIMPORT expr mkbool(int flag);
__DLLIMPORT expr mkobj(int type, void *ptr);

/* Applications, list and tuple construction ([|] resp. (|)). */

__DLLIMPORT expr mkapp(expr fun, expr arg);
__DLLIMPORT expr mkcons(expr hd, expr tl);
__DLLIMPORT expr mkcont(expr hd, expr tl);

/* Create a list or tuple from a given element list. The mklistvl()/mktuplel()
   functions take the elements directly as parameters, while the mklistv()/
   mktuplev() functions take an expression vector. The interpreter takes
   control of these vectors and will deallocate them when it is appropriate. */

__DLLIMPORT expr mklistl(int nelems, ...);
__DLLIMPORT expr mklistv(int nelems, expr *elems);

__DLLIMPORT expr mktuplel(int nelems, ...);
__DLLIMPORT expr mktuplev(int nelems, expr *elems);

/* Common types of constants defined as parameterless macros. */

#define mktrue mksym(truesym)
#define mkfalse mksym(falsesym)
#define mknil mksym(nilsym)
#define mkvoid mksym(voidsym)

/* Type checking and unboxing. exprsym() and exprtype() return the function
   and type symbol of an expression. The remaining functions are used to check
   for a specific kind of object, and return the corresponding component
   values. */

__DLLIMPORT int exprsym(const expr x);
__DLLIMPORT int exprtype(const expr x);

/* In analogy to the construction functions, there are several functions to
   check for integer objects. The isint() and isuint() functions will only
   return an integer value which fits into a machine integer. Arbitrary-sized
   integers can be obtained using the ismpz() function which returns an mpz_t
   value. This function overwrites the value of the given mpz_t variable,
   hence the variable must not be initialized. Also note that the value
   returned by ismpz() is owned by the interpreter and hence must not be
   modified by the calling program in any way. If necessary, you can make your
   own copy of the value using the GMP mpz_set() function. Finally, the
   ismpz_float() function returns an integer value as a floating point number,
   and isbool() returns a Bool value as an integer (0 for false, 1 for
   true). */

__DLLIMPORT int isint(const expr x, long *i);
__DLLIMPORT int isuint(const expr x, unsigned long *i);
__DLLIMPORT int ismpz(const expr x, void *z /*mpz_t z*/);
__DLLIMPORT int ismpz_float(const expr x, double *f);
__DLLIMPORT int isfloat(const expr x, double *f);
__DLLIMPORT int isstr(const expr x, char **s);
__DLLIMPORT int isfile(const expr x, FILE **fp);

__DLLIMPORT int issym(const expr x, int sym);
__DLLIMPORT int isbool(const expr x, int *flag);
__DLLIMPORT int isobj(const expr x, int type, void **ptr);

__DLLIMPORT int isapp(const expr x, expr *fun, expr *arg);
__DLLIMPORT int iscons(const expr x, expr *hd, expr *tl);
__DLLIMPORT int iscont(const expr x, expr *hd, expr *tl);

/* The elems vector returned by istuple() is owned by the interpreter, and
   must *not* be manipulated in any way. This is again to save the overhead of
   creating an extra copy. */

__DLLIMPORT int istuple(const expr x, int *nelems, expr **elems);

/* Check for the predefined constants. */

#define istrue(x) issym(x, truesym)
#define isfalse(x) issym(x, falsesym)
#define isnil(x) issym(x, nilsym)
#define isvoid(x) issym(x, voidsym)

/* Expression evaluation. eval() recursively invokes the interpreter on the
   given expression and returns the computed result (NULL if an error occurs
   or the evaluation is aborted). In any case, the argument (if it is
   different from the result) is collected using dispose(). Thus, after
   applying eval() to a temporary value, never use dispose() on the argument,
   but only on the evaluated result. */

__DLLIMPORT expr eval(const expr x);

/* Garbage collection of temporary toplevel values (created with the
   construction functions or with eval()). You can safely apply this to any
   expression, since only top level temporaries will actually be
   destroyed. Component values of a toplevel expression will be collected
   automatically when they are not referred to any more. */

__DLLIMPORT void dispose(expr x);

/* Multithreading support. As of Q 3.0, you can safely invoke eval() and the
   other libq operations in child threads created by a hosted module. For this
   to work, some amount of synchronization with the interpreter is required.

   First, you must invoke init_thread() from the new thread before invoking
   other libq operations. This function initializes some thread-local
   resources in the interpreter and obtains an internal mutex which allows the
   other libq operations to run in a safe context. It also returns the id
   under which the thread is known by the interpreter. (If the result is -1
   then the operation was unsuccessful, possibly because the interpreter does
   not support multithreading. In this case it is *not* safe to call the other
   operations afterwards.)

   Second, you call eval() and/or whatever other calculations with the
   interpreter are required.

   Third, once you're finished with the interpreter, you must invoke the
   exit_thread() function with the id returned by init_thread() to release the
   internal mutex obtained with the init_thread() function, and to update some
   other internal bookkeeping information. After calling exit_thread(), it is
   *not* safe to call other libq operations any more. But you can still access
   the results of your calculations as the thread-local resources are still
   available.

   Fourth, you call fini_thread() with the id returned by init_thread() to
   release the thread-local resources associated with the new thread. This can
   be done from the new thread, but also from any other thread. Once this
   operation is completed, any trace of the new thread will have vanished from
   the interpreter.

   Note that the new thread, while performing an evaluation, is subject to
   preemption, since the interpreter will switch over to other waiting tasks
   in its internal execution loop. Moreoever, the thread may also be canceled
   during evaluation when the interpreter is about to exit or run a new
   script. Therefore you should provide an appropriate cleanup handler. At the
   very least, your cleanup handler must call exit_thread() in case the thread
   is canceled after init_thread() but before the thread reaches the
   exit_thread() call. Finally, if your module uses any internal
   synchronization objects, these should be handled at fork time with
   appropriate "atfork" handlers, see the POSIX threads manual for
   details. (Never call pthread_atfork yourself to install the handlers, but
   use the provided atfork() interface routine. This is necessary to prevent
   dangling function pointers if the module is unloaded by the interpreter.)

   Another issue arises with external operations which may block for extended
   or even indefinite periods of time. By default, the interpreter will only
   allow one external function to be entered at any time, hence such an
   operation may block other threads and the interpreter itself. To prevent
   this, the release_lock() and acquire_lock() operation can be used to
   temporarily release the lock on the interpreter's mutex, which lets the
   interpreter continue with another thread while your thread performs a
   lengthy or potentially blocking operation. These operations also establish
   cancellation points. Note that after releasing the mutex your thread
   continues to run in parallel with the interpreter, so the executed code
   must be thread-safe. In particular, you must not invoke any other libq
   operations after releasing the mutex, and you must do your own mutex
   locking on private globals if necessary. Before returning from the
   operation, you must *always* reacquire the mutex. The have_lock() function
   can be used to test whether the current thread currently holds the mutex.

   Synchronization with the interpreter is also required if you want to do
   terminal input, to prevent strange interactions with the readline
   library. A "tty" mutex is provided for that purpose. When you're about to
   read something from stdin you should acquire this mutex and release it when
   finished.

   Finally, when passing an (argument or temporary) expression to a newly
   created thread, you must ensure that the expression is not garbage
   collected by the main thread or some other thread while your thread is
   still executing. For this purpose, you can count a new reference to the
   expression using the newref() function, which will cause garbage collection
   to be deferred until you call freeref(). Note that each call to newref()
   counts a new reference, so the same number of freeref() invokations is
   required to actually free the expression. There also is an unref()
   operation which merely decreases the reference count without actually
   deallocating the expression. Note that you must make sure that the
   expression is actually freed *before* the thread terminates. */

/* thread initialization and termination */
__DLLIMPORT int init_thread(void);
__DLLIMPORT void exit_thread(int id);
__DLLIMPORT void fini_thread(int id);

/* get the id of the current thread */
__DLLIMPORT int this_thread(void);

/* enable background processing */
__DLLIMPORT int have_lock(void);
__DLLIMPORT void release_lock(void);
__DLLIMPORT void acquire_lock(void);

/* stdin access */
__DLLIMPORT void acquire_tty(void);
__DLLIMPORT void release_tty(void);

/* defer garbage collection */
__DLLIMPORT expr newref(expr x);
__DLLIMPORT expr unref(expr x);
__DLLIMPORT void freeref(expr x);

/* register pthread_atfork handlers (only one set of handlers can be
   installed for each module) */
#define thread_atfork(prepare,parent,child) \
__thread_atfork(prepare,parent,child,__modno)

/* Internals (*never* access these directly!). */

__DLLIMPORT int __libq_init();
__DLLIMPORT expr __mkerror(void);
__DLLIMPORT int __getsym(const char *name, int modno);
__DLLIMPORT int __gettype(const char *name, int modno);
__DLLIMPORT const char *__getpname(int sym, int modno);
__DLLIMPORT void __thread_atfork(void (*prepare)(void), void (*parent)(void),
				 void (*child)(void), int modno);

#ifdef __cplusplus
}
#endif

#endif


syntax highlighted by Code2HTML, v. 0.9.1