Skip to content

Callback

Wang Renxin edited this page May 31, 2022 · 18 revisions

Call from BASIC to BASIC

The CALL statement is used to get a invokable routine value as:

def fun(msg)
	print msg;
enddef

routine = call(fun) ' Get a routine value
routine("hello")    ' Invoke a routine value

Be aware it requires a pair of brackets comes along with a CALL statement to get a invokable value, otherwise it calls the routine instantly.

This mechanism is useful when you are tending to store a sub routine value for later invoking.

Call from C to BASIC

Besides, it's possible to call a BASIC routine from C side as well, for instance, assuming we got a routine defined in BASIC:

def fun(num)
	print num;

	return num * 2 ' Return a new value back
enddef

native ' This is a registered native function

Note it won't work if call NATIVE before the DEF fun statement, because the interpreter doesn't know necessary information until a DEF statement is evaluated during runtime.

Now it's possible to callback fun at C side as follow:

static int _native(struct mb_interpreter_t* s, void** l) {
	int result = MB_FUNC_OK;

	mb_assert(s && l);

	mb_check(mb_attempt_func_begin(s, l));
	mb_check(mb_attempt_func_end(s, l));

	{
		mb_value_t routine;
		mb_value_t args[1];
		mb_value_t ret;

		mb_get_routine(s, l, "FUN", &routine);   /* Get the "FUN" routine */

		args[0].type = MB_DT_INT;
		args[0].value.integer = 123;
		mb_make_nil(ret);
		mb_eval_routine(s, l, routine, args, 1, &ret); /* Evaluate the "FUN" routine with arguments, and get the returned value */
		printf("Returned %d.\n", ret.value.integer);
	}

	return result;
}

Note it needs uppercase identifier to lookup a routine (and other symbols in MY-BASIC). You always have to pass an mb_value_t args[1] array to mb_eval_routine(s, l, routine, args, 0, 0);, even if it's a parameterless routine. The last parameter mb_value_t* ret is optional.

Call from BASIC to C

There is no difference from a routine defined in BASIC and in C. Assuming we have two C functions as follow:

static int _foo(struct mb_interpreter_t* s, void** l, mb_value_t* va, unsigned ca, void* r, mb_has_routine_arg_func_t has, mb_pop_routine_arg_func_t pop) {
	int result = MB_FUNC_OK;
	mb_value_t val;
	unsigned ia = 0;

	mb_assert(s && l);

	mb_check(mb_attempt_open_bracket(s, l));

	mb_make_nil(val);
	if(has(s, l, va, ca, &ia, r)) {
		mb_check(pop(s, l, va, ca, &ia, r, &val));
	}

	mb_check(mb_attempt_close_bracket(s, l));

	printf("%d\n", val.value.integer);

	return result;
}

static int _test(struct mb_interpreter_t* s, void** l) {
	int result = MB_FUNC_OK;

	mb_assert(s && l);

	mb_check(mb_attempt_func_begin(s, l));
	mb_check(mb_attempt_func_end(s, l));

	mb_set_routine(s, l, "FOO", _foo, true); /* Define a routine named "FOO" */

	return result;
}

Don't forget to register it:

mb_reg_fun(bas, _test);

Now we can call the C function _foo as a routine:

test     ' Call "test"

foo(123) ' "foo" is defined as a routine after calling "test"
Clone this wiki locally