Skip to content

Defining a class in C

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

It's possible to define class, add member variable and routine from C side. There's no difference between a class prototype defined in BASIC and in C, a C class can link a BASIC class as meta class, and vice versa.

Assuming we have a NATIVE_METHOD function defined in C for later use:

static int _native_method(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;
}

A routine of a class instance may be invoked not only from the BASIC side, but also from the native side. But the mb_has_arg and mb_pop_* functions are dedicated to manipulate arguments at BASIC side. So for this case, the proper way is using the mb_has_routine_arg_func_t has and mb_pop_routine_arg_func_t pop instead as equivalents respectively to mb_has_arg and mb_pop_value. You don't need to care about the parameters _value_t* va, unsigned ca, unsigned ia and void* r, the only places you have to write them is passing to them has and pop. All the rest parts of a native method routine are similar to regular int (* mb_func_t)(struct mb_interpreter_t*, void**) signatured functions, including the usage of mb_attempt_func_begin, mb_attempt_func_end, mb_attempt_open_bracket, mb_attempt_close_bracket and mb_push_*.

There are two functions mb_begin_class, mb_end_class correspond to the CLASS/ENDCLASS statements which can be used to declare a class in C; and use mb_add_var and mb_set_routine to set member of a class. For example:

static int _do_something_in_c(struct mb_interpreter_t* s, void** l) {
	int result = MB_FUNC_OK;
	mb_value_t clz;
	mb_value_t script_base;
	mb_value_t* metas[1];
	mb_value_t cval;

	mb_assert(s && l);

	mb_check(mb_attempt_open_bracket(s, l));
	mb_check(mb_attempt_close_bracket(s, l));

	mb_make_nil(script_base);
	mb_check(mb_get_value_by_name(s, l, "SCRIPT_BASE", &script_base));     /* Get the "SCRIPT_BASE" class from BASIC */
	metas[0] = &script_base;

	mb_make_nil(clz);
	mb_check(mb_begin_class(s, l, "NATIVE_BASE", metas, 1, &clz));         /* Declare a "NATIVE_BASE" class which links "SCRIPT_BASE" as a meta class */
	mb_make_int(cval, 123);
	mb_check(mb_add_var(s, l, "C", cval, true));                           /* Add a member variable "C" */
	mb_check(mb_set_routine(s, l, "NATIVE_METHOD", _native_method, true)); /* Add a member method "NATIVE_METHOD" */
	mb_check(mb_end_class(s, l));

	return result;
}

Usage at BASIC side:

class script_base       ' A BASIC class
	def add(a, b)
		return a + b
	enddef
endclass

do_something_in_c()     ' Do something in c

class foo(native_base)  ' Another BASIC class links "native_base" as a meta class
endclass

print foo.add(1, 2);    ' A method in meta class
print foo.c;            ' A variable in native meta class
foo.native_method(456); ' A method in native meta class
Clone this wiki locally