Add optional user_data to Nasal C functions.

A user_data pointer and another pointer to an optional
deleter function is stored in unused parts of the naPtr
union. The previous behavior of extension functions does
not change. Only one additional boolean comparison is
required upon each function call to check whether user
data is available.
This commit is contained in:
Thomas Geymayer 2013-03-01 12:22:51 +01:00
parent 081eba903f
commit 5e45bdeeda
5 changed files with 55 additions and 3 deletions

View File

@ -313,7 +313,10 @@ static struct Frame* setupFuncall(naContext ctx, int nargs, int mcall, int named
ctx->opFrame = opf;
if(IS_CCODE(code)) {
naRef result = (*PTR(code).ccode->fptr)(ctx, obj, nargs, args);
struct naCCode *ccode = PTR(code).ccode;
naRef result = ccode->fptru
? (*ccode->fptru)(ctx, obj, nargs, args, ccode->user_data)
: (*ccode->fptr)(ctx, obj, nargs, args);
if(named) ERR(ctx, "native functions have no named arguments");
ctx->opTop = ctx->opFrame;
PUSH(result);

View File

@ -160,7 +160,15 @@ struct naFunc {
struct naCCode {
GC_HEADER;
naCFunction fptr;
union {
naCFunction fptr; //<! pointer to simple callback function. Invalid if
// fptru is not NULL.
struct {
void* user_data;
void(*destroy)(void*);
naCFunctionU fptru;
};
};
};
struct naGhost {

View File

@ -128,6 +128,12 @@ static void naCode_gcclean(struct naCode* o)
naFree(o->constants); o->constants = 0;
}
static void naCCode_gcclean(struct naCCode* c)
{
if(c->fptru && c->user_data && c->destroy) c->destroy(c->user_data);
c->user_data = 0;
}
static void naGhost_gcclean(struct naGhost* g)
{
if(g->ptr && g->gtype->destroy) g->gtype->destroy(g->ptr);
@ -142,6 +148,7 @@ static void freeelem(struct naPool* p, struct naObj* o)
case T_VEC: naVec_gcclean ((struct naVec*) o); break;
case T_HASH: naiGCHashClean ((struct naHash*) o); break;
case T_CODE: naCode_gcclean ((struct naCode*) o); break;
case T_CCODE: naCCode_gcclean((struct naCCode*)o); break;
case T_GHOST: naGhost_gcclean((struct naGhost*)o); break;
}
p->free[p->nfree++] = o; // ...and add it to the free list

View File

@ -107,6 +107,24 @@ naRef naNewCCode(struct Context* c, naCFunction fptr)
{
naRef r = naNew(c, T_CCODE);
PTR(r).ccode->fptr = fptr;
PTR(r).ccode->fptru = 0;
return r;
}
naRef naNewCCodeU(struct Context* c, naCFunctionU fptr, void* user_data)
{
return naNewCCodeUD(c, fptr, user_data, 0);
}
naRef naNewCCodeUD( struct Context* c,
naCFunctionU fptr,
void* user_data,
void (*destroy)(void*) )
{
naRef r = naNew(c, T_CCODE);
PTR(r).ccode->fptru = fptr;
PTR(r).ccode->user_data = user_data;
PTR(r).ccode->destroy = destroy;
return r;
}

View File

@ -16,10 +16,14 @@ extern "C" {
#endif
typedef struct Context* naContext;
// The function signature for an extension function:
typedef naRef (*naCFunction)(naContext ctx, naRef me, int argc, naRef* args);
// The function signature for an extension function with userdata passed back:
typedef naRef (*naCFunctionU)
(naContext ctx, naRef me, int argc, naRef* args, void* user_data);
// All Nasal code runs under the watch of a naContext:
naContext naNewContext();
void naFreeContext(naContext c);
@ -146,7 +150,19 @@ naRef naNewString(naContext c);
naRef naNewVector(naContext c);
naRef naNewHash(naContext c);
naRef naNewFunc(naContext c, naRef code);
/**
* Register extension function
*
* @param fptr Pointer to C-function
* @param user_data Optional user data passed back on calling the function
* @param destroy Optional callback called if function gets freed by garbage
* collector to free user data if required.
*/
naRef naNewCCode(naContext c, naCFunction fptr);
naRef naNewCCodeU(naContext c, naCFunctionU fptr, void* user_data);
naRef naNewCCodeUD(naContext c, naCFunctionU fptr, void* user_data,
void (*destroy)(void*));
// Some useful conversion/comparison routines
int naEqual(naRef a, naRef b) GCC_PURE;