C API: Add nix_value_call_multi, NIX_VALUE_CALL

_multi can be implemented more efficiently.
NIX_VALUE_CALL is a convenient way to invoke it.
This commit is contained in:
Robert Hensing 2024-05-23 19:55:32 +02:00
parent ab106c5ca3
commit 2497d10351
3 changed files with 100 additions and 1 deletions

View File

@ -65,6 +65,17 @@ nix_err nix_value_call(nix_c_context * context, EvalState * state, Value * fn, V
NIXC_CATCH_ERRS
}
nix_err nix_value_call_multi(nix_c_context * context, EvalState * state, Value * fn, size_t nargs, Value ** args, Value * value)
{
if (context)
context->last_err_code = NIX_OK;
try {
state->state.callFunction(*(nix::Value *) fn, nargs, (nix::Value * *)args, *(nix::Value *) value, nix::noPos);
state->state.forceValue(*(nix::Value *) value, nix::noPos);
}
NIXC_CATCH_ERRS
}
nix_err nix_value_force(nix_c_context * context, EvalState * state, Value * value)
{
if (context)

View File

@ -12,6 +12,7 @@
#include "nix_api_store.h"
#include "nix_api_util.h"
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
@ -80,6 +81,46 @@ nix_err nix_expr_eval_from_string(
*/
nix_err nix_value_call(nix_c_context * context, EvalState * state, Value * fn, Value * arg, Value * value);
/**
* @brief Calls a Nix function with multiple arguments.
*
* Technically these are functions that return functions. It is common for Nix
* functions to be curried, so this function is useful for calling them.
*
* @param[out] context Optional, stores error information
* @param[in] state The state of the evaluation.
* @param[in] fn The Nix function to call.
* @param[in] nargs The number of arguments.
* @param[in] args The arguments to pass to the function.
* @param[out] value The result of the function call.
*
* @see nix_value_call For the single argument primitive.
* @see NIX_VALUE_CALL For a macro that wraps this function for convenience.
*/
nix_err nix_value_call_multi(
nix_c_context * context, EvalState * state, Value * fn, size_t nargs, Value ** args, Value * value);
/**
* @brief Calls a Nix function with multiple arguments.
*
* Technically these are functions that return functions. It is common for Nix
* functions to be curried, so this function is useful for calling them.
*
* @param[out] context Optional, stores error information
* @param[in] state The state of the evaluation.
* @param[out] value The result of the function call.
* @param[in] fn The Nix function to call.
* @param[in] args The arguments to pass to the function.
*
* @see nix_value_call_multi
*/
#define NIX_VALUE_CALL(context, state, value, fn, ...) \
do { \
Value * args_array[] = {__VA_ARGS__}; \
size_t nargs = sizeof(args_array) / sizeof(args_array[0]); \
nix_value_call_multi(context, state, fn, nargs, args_array, value); \
} while (0)
/**
* @brief Forces the evaluation of a Nix value.
*

View File

@ -250,7 +250,7 @@ static void primop_repeat(void * user_data, nix_c_context * context, EvalState *
nix_init_string(context, ret, result.c_str());
}
TEST_F(nix_api_expr_test, nix_expr_primop_arity_2)
TEST_F(nix_api_expr_test, nix_expr_primop_arity_2_multiple_calls)
{
PrimOp * primop =
nix_alloc_primop(ctx, primop_repeat, 2, "repeat", nullptr, "repeat a string", (void *) SAMPLE_USER_DATA);
@ -285,6 +285,38 @@ TEST_F(nix_api_expr_test, nix_expr_primop_arity_2)
ASSERT_STREQ("hellohellohello", r.c_str());
}
TEST_F(nix_api_expr_test, nix_expr_primop_arity_2_single_call)
{
PrimOp * primop =
nix_alloc_primop(ctx, primop_repeat, 2, "repeat", nullptr, "repeat a string", (void *) SAMPLE_USER_DATA);
assert_ctx_ok();
Value * primopValue = nix_alloc_value(ctx, state);
assert_ctx_ok();
nix_init_primop(ctx, primopValue, primop);
assert_ctx_ok();
Value * hello = nix_alloc_value(ctx, state);
assert_ctx_ok();
nix_init_string(ctx, hello, "hello");
assert_ctx_ok();
Value * three = nix_alloc_value(ctx, state);
assert_ctx_ok();
nix_init_int(ctx, three, 3);
assert_ctx_ok();
Value * result = nix_alloc_value(ctx, state);
assert_ctx_ok();
NIX_VALUE_CALL(ctx, state, result, primopValue, hello, three);
assert_ctx_ok();
std::string r;
nix_get_string(ctx, result, OBSERVE_STRING(r));
assert_ctx_ok();
ASSERT_STREQ("hellohellohello", r.c_str());
}
static void
primop_bad_no_return(void * user_data, nix_c_context * context, EvalState * state, Value ** args, Value * ret)
{
@ -357,4 +389,19 @@ TEST_F(nix_api_expr_test, nix_expr_primop_bad_return_thunk)
testing::HasSubstr("Implementation error in custom function: return value must not be a thunk")));
ASSERT_THAT(ctx->last_err, testing::Optional(testing::HasSubstr("badReturnThunk")));
}
TEST_F(nix_api_expr_test, nix_value_call_multi_no_args)
{
Value * n = nix_alloc_value(ctx, state);
nix_init_int(ctx, n, 3);
assert_ctx_ok();
Value * r = nix_alloc_value(ctx, state);
nix_value_call_multi(ctx, state, n, 0, nullptr, r);
assert_ctx_ok();
auto rInt = nix_get_int(ctx, r);
assert_ctx_ok();
ASSERT_EQ(3, rInt);
}
} // namespace nixC