.. raw:: html Hacer una función nativa ======================== La FFI en C (interfaz de función externa) de Berry opera en una pila virtual para interactuar con la máquina virtual. Si necesitamos hacer una función ``add`` para sumar dos números y usarla en Berry de esta manera: .. code:: ruby result = add(1, 2) Necesitamos saber cómo el código C obtiene los argumentos de la llamada a la función Berry y cómo devolver el valor. Los argumentos de la función se almacenan en una pila, y desde el primer argumento hasta el último argumento de la función se almacenan desde la parte inferior de la pila hasta la parte superior de la pila. Si desea utilizar C para obtener elementos de la pila, utilice el siguiente conjunto de FFI: .. code:: c int be_toint(bvm *vm, int index); breal be_toreal(bvm *vm, int index); int be_tobool(bvm *vm, int index); const char* be_tostring(bvm *vm, int index); void* be_tocomptr(bvm *vm, int index); Si desea probar si un valor en la pila es de un tipo específico, use el siguiente conjunto de FFI: .. code:: c int be_isnil(bvm *vm, int index); int be_isbool(bvm *vm, int index); int be_isint(bvm *vm, int index); int be_isreal(bvm *vm, int index); int be_isnumber(bvm *vm, int index); int be_isstring(bvm *vm, int index); int be_isclosure(bvm *vm, int index); int be_isntvclos(bvm *vm, int index); int be_isfunction(bvm *vm, int index); int be_isproto(bvm *vm, int index); int be_isclass(bvm *vm, int index); int be_isinstance(bvm *vm, int index); int be_islist(bvm *vm, int index); int be_ismap(bvm *vm, int index); int be_iscomptr(bvm *vm, int index); Si necesita enviar valores a la pila, use estos FFI: .. code:: c void be_pushnil(bvm *vm); void be_pushbool(bvm *vm, int b); void be_pushint(bvm *vm, bint i); void be_pushreal(bvm *vm, breal r); void be_pushstring(bvm *vm, const char *str); void be_pushnstring(bvm *vm, const char *str, size_t n); const char* be_pushfstring(bvm *vm, const char *format, ...); void be_pushvalue(bvm *vm, int index); void be_pushntvclosure(bvm *vm, bntvfunc f, int nupvals); void be_pushntvfunction(bvm *vm, bntvfunc f); void be_pushclass(bvm *vm, const char *name, const bnfuncinfo *lib); void be_pushcomptr(bvm *vm, void *ptr); ``index`` es la posición del elemento en la pila, un valor positivo es el desplazamiento desde la parte inferior de la pila hasta la parte superior de la pila, y un valor negativo es el desplazamiento desde la parte superior de la pila hasta la parte inferior de la pila. El valor de retorno utiliza dos FFI: .. code:: c be_return(vm) be_return_nil(vm) Estos FFI son en realidad macros. ``be_return`` devuelve el objeto en la parte superior de la pila, y ``be_return_nil`` devuelve ``nil``. Estas FFI se definen en berry.h. Ahora implementemos la función ``add``: .. code:: c int my_add_func(bvm *vm) { /* comprobar que los argumentos son todos enteros */ if (be_isint(vm, 1) && be_isint(vm, 2)) { bint a = be_toint(vm, 1); /* obtener el primer argumento */ bint b = be_toint(vm, 2); /* obtener el segundo argumento */ be_pushint(vm, a + b); /* empuja el resultado a la pila */ } else if (be_isnumber(vm, 1) && be_isnumber(vm, 2)) { /* comprobar que los argumentos son todos números */ breal a = be_toreal(vm, 1); /* obtener el primer argumento */ breal b = be_toreal(vm, 1); /* empuja el resultado a la pila */ be_pushreal(vm, a + b); /* empuja el resultado a la pila */ } else { /* parámetros inaceptables */ be_pushnil(vm); /* empuja nil a la pila */ } be_return(vm); /* devuelve el resultado del cálculo */ } Luego regístrelo en el lugar apropiado: .. code:: c be_regcfunc(vm, "add", my_add_func); Crear una instancia de un objeto ``list`` en una función nativa =============================================================== La generación de clases nativas instanciadas en C puede ser engorrosa en comparación con los tipos simples. Esta sección guiará al lector a instanciar la clase ``list``. La clase ``list`` es un contenedor alrededor de la estructura de la lista, que tiene una propiedad ``.data`` para la estructura de la lista. Por lo tanto, primero necesitamos construir una estructura de lista: .. code:: c be_newlist(vm); La función ``be_newlist`` construye un valor de tipo ``BE_LIST``. Entonces podemos operar sobre los datos: .. code:: c be_pushint(vm, 100); be_data_append(vm, -2); be_pop(vm, 1); /* extraer el entero 100 */ Las dos primeras líneas de código se utilizan para añadir el entero ``100`` a la lista, y la tercera línea del entero ``100`` se extrae para facilitar las operaciones posteriores. Dado que el tipo ``BE_LIST`` no se puede usar directamente en Berry, pero lo usa la clase ``list``, tenemos que construir la clase ``list`` para él: .. code:: c be_getglobal(vm, "list"); be_pushvalue(vm, -2); /* empuja los datos de la lista al principio */ be_call(vm, 1); /* llama al constructor */ El constructor de la clase ``list`` permite el uso del parámetro de tipo ``BE_LIST``, que toma el argumento como datos de lista. El código completo es el siguiente: .. code:: c int m_listtest(bvm *vm) { be_getglobal(vm, "list"); be_newlist(vm); be_pushint(vm, 100); be_data_append(vm, -2); be_pop(vm, 1); be_call(vm, 1); be_pop(vm, 1); /* pop the arguments */ be_return(vm); } Registre la función nativa en el lugar apropiado: .. code:: c be_regcfunc(vm, "listtest", m_listtest);