diff --git a/src/libexpr/c/nix_api_value.cc b/src/libexpr/c/nix_api_value.cc index 608a625cb..9a313d8fd 100644 --- a/src/libexpr/c/nix_api_value.cc +++ b/src/libexpr/c/nix_api_value.cc @@ -34,18 +34,39 @@ static nix::Value & check_value_not_null(Value * value) return *((nix::Value *) value); } +/** + * Helper function to convert calls from nix into C API. + * + * Deals with errors and converts arguments from C++ into C types. + */ +static void nix_c_primop_wrapper( + PrimOpFun f, void * userdata, nix::EvalState & state, const nix::PosIdx pos, nix::Value ** args, nix::Value & v) +{ + f(userdata, (State *) &state, *reinterpret_cast(&pos), (Value **) args, (Value *) &v); +} + PrimOp * nix_alloc_primop( - nix_c_context * context, PrimOpFun fun, int arity, const char * name, const char ** args, const char * doc) + nix_c_context * context, + PrimOpFun fun, + int arity, + const char * name, + const char ** args, + const char * doc, + void * user_data) { if (context) context->last_err_code = NIX_OK; try { - auto fun2 = (nix::PrimOpFun) fun; auto p = new #ifdef HAVE_BOEHMGC (GC) #endif - nix::PrimOp{.name = name, .args = {}, .arity = (size_t) arity, .doc = doc, .fun = fun2}; + nix::PrimOp{ + .name = name, + .args = {}, + .arity = (size_t) arity, + .doc = doc, + .fun = std::bind_front(nix_c_primop_wrapper, fun, user_data)}; if (args) for (size_t i = 0; args[i]; i++) p->args.emplace_back(*args); diff --git a/src/libexpr/c/nix_api_value.h b/src/libexpr/c/nix_api_value.h index 21647552b..ffba4c097 100644 --- a/src/libexpr/c/nix_api_value.h +++ b/src/libexpr/c/nix_api_value.h @@ -65,6 +65,7 @@ typedef struct ExternalValue ExternalValue; * @{ */ /** @brief Function pointer for primops + * @param[in] user_data Arbitrary data, passed to nix_alloc_primop and stored. * @param[in] state Evaluator state * @param[in] pos Call position, opaque index into the state's position table. * @param[in] args list of arguments. Note that these can be thunks and should be forced using nix_value_force before @@ -72,7 +73,7 @@ typedef struct ExternalValue ExternalValue; * @param[out] ret return value * @see nix_alloc_primop, nix_set_primop */ -typedef void (*PrimOpFun)(State * state, int pos, Value ** args, Value * ret); +typedef void (*PrimOpFun)(void * user_data, State * state, int pos, Value ** args, Value * ret); /** @brief Allocate a PrimOp * @@ -85,19 +86,27 @@ typedef void (*PrimOpFun)(State * state, int pos, Value ** args, Value * ret); * @param[in] name function name * @param[in] args array of argument names, NULL-terminated * @param[in] doc optional, documentation for this primop + * @param[in] user_data optional, arbitrary data, passed to the function when it's called * @return primop, or null in case of errors * @see nix_set_primop */ PrimOp * nix_alloc_primop( - nix_c_context * context, PrimOpFun fun, int arity, const char * name, const char ** args, const char * doc); + nix_c_context * context, + PrimOpFun fun, + int arity, + const char * name, + const char ** args, + const char * doc, + void * user_data); /** @brief add a primop to the `builtins` attribute set * * Only applies to States created after this call. * - * Moves your PrimOp into the global evaluator - * registry, meaning your input PrimOp pointer is no longer usable - * (but still possibly subject to garbage collection). + * Moves your PrimOp content into the global evaluator + * registry, meaning your input PrimOp pointer is no longer usable. + * You are free to remove your references to it, + * after which it will be garbage collected. * * @param[out] context Optional, stores error information * @return primop, or null in case of errors