.. raw:: html
8. Características avanzadas
============================
8.1 Modo ``estricto``
---------------------
Berry permite total libertad del desarrollador. Pero después de un poco
de experiencia en la codificación con Berry, encontrará que hay errores
comunes que son difíciles de encontrar y que el compilador podría
ayudarlo a detectar. El modo ``estricto`` realiza verificaciones
adicionales **en tiempo de compilación** sobre algunos errores comunes.
Este modo está habilitado con ``import strict`` o cuando se ejecuta
Berry con la opción ``-s``: ``berry -s``
``var`` obligatorio para variables locales
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Este es el error más común, una variable asignada sin ``var`` es global
si ya existe una variable global o local en caso contrario. El modo
estricto rechaza la asignación si no hay un global con el mismo nombre.
No más permitido:
.. code:: berry
def f()
i = 0 # this is a local variable
var j = 0
end
| syntax_error: stdin:2: strict: no global 'i', ¿quiso decir 'var i'?
|
Pero todavía funciona para globales:
.. code:: berry
g_i = 0
def f()
g_i = 1
end
Sin anulación de elementos integrados
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Berry permite anular una función incorporada. Sin embargo, esto
generalmente no es deseable y es una fuente de errores difíciles de
encontrar.
::
map = 1
syntax_error: stdin:1: estricto: redefinición de 'map' incorporado
Múltiples ``var`` con el mismo nombre no permitidos en el mismo ámbito
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Berry toleraba la declaración múltiple de una variable local con el
mismo nombre. Esto ahora se considera como un error (incluso sin modo
estricto).
::
def f()
var a
var a # redefinición de a
end
syntax_error: stdin:3: redefinición de 'a'
No ocultar la variable local del alcance externo
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
En Berry puedes declarar variables locales con el mismo nombre en el
ámbito interno. La variable en el ámbito interno oculta la variable del
ámbito externo durante la duración del ámbito.
La única excepción son las variables que comienzan con el punto ‘.’ que
se pueden enmascarar desde el alcance externo. Este es el caso de la
variable local oculta ``.it`` cuando se incrustan múltiples ``for``.
::
def f()
var a # variable en el ámbito externo
if a
var a # redefinición de a en ámbito interno
end
end
syntax_error: stdin:4: estricto: redefinición de 'a' desde el ámbito externo
8.2 Miembros virtuales
----------------------
Los miembros virtuales le permiten agregar de forma dinámica y
programática miembros y métodos a clases y módulos. Ya no está limitado
a los miembros declarados en el momento de la creación.
Esta función está inspirada en ``__getattr__()`` / ``__setattr__()`` de
Python. La motivación proviene de la integración de LVGL a Berry en
Tasmota. La integración necesita cientos de constantes en un módulo y
miles de métodos asignados a funciones C. La creación estática de
atributos y métodos funciona, pero consume una cantidad significativa de
espacio de código.
Esta característica permite crear dos métodos:
+-----+----------------------------------------------------------------+
| Mét | Descripción |
| odo | |
| Be | |
| rry | |
+=====+================================================================+
| ` | ``(nombre:cadena) -> any``\ Debería devolver el valor del |
| `me | ``nombre`` |
| mbe | |
| r`` | |
+-----+----------------------------------------------------------------+
| ` | ``(nombre:cadena, valor:any) especificado -> nil``\ Debería |
| `se | almacenar el ‘valor’ en el miembro virtual con el ‘nombre’ |
| tme | especificado |
| mbe | |
| r`` | |
+-----+----------------------------------------------------------------+
Módulo ``undefined``
~~~~~~~~~~~~~~~~~~~~
La función ``member()`` debe ser capaz de distinguir entre un miembro
con un valor ``nil`` y el miembro que no existe. Para evitar cualquier
ambigüedad, la función ``member()`` puede indicar que el miembro no
existe de dos maneras:
- generar una excepción - o ``import undefined`` y devolver el módulo
``undefined``. Esto se usa como un marcador para que la VM sepa que
el atributo no existe, mientras se beneficia de excepciones
consistentes.
Ejemplo de un objeto dinámico al que puede agregar miembros, pero
devolvería un error si el miembro no se agregó previamente.
.. code:: berry
class dyn
var _attr
def init()
self._attr = {}
end
def setmember(nombre, valor)
self._attr[nombre] = valor
end
def member(nombre)
if self._attr.contains(nombre)
return self._attr[nombre]
else
import undefined
return undefined
end
end
end
Ejemplo de uso:
.. code:: berry
a = dyn()
a.a
attribute_error: el objeto 'dyn' no tiene el atributo 'a'
stack traceback:
stdin:1: en función `main`
a.a = 1
a.a
1
a.a = nil
a.a
Llamada implícita de ``member()``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Cuando se ejecuta el siguiente código ``a.b``, Berry VM hace lo
siguiente:
- Obtiene el objeto llamado ``a`` (local o global), genera una
excepción si no existe
- Comprueba si el objeto ``a`` es de tipo ``módulo``, ``instancia`` o
``clase``. Genera una excepción de lo contrario
- Comprueba si el objeto ``a`` tiene un miembro llamado ``b``. En caso
afirmativo, devuelve su valor, en caso negativo, procede a
continuación
- Si el objeto ``a`` es del tipo ``clase``, genera una excepción porque
los miembros virtuales no funcionan para métodos estáticos (clase)
- Comprueba si el objeto ``a`` tiene un miembro llamado ``member`` y es
una ``función``. En caso afirmativo, lo llama con el parámetro
``"b"`` como cadena. Si no, genera una excepción
- Comprueba el valor de retorno. Si es el módulo ``undefined`` genera
una excepción que indica que el miembro no existe
Llamada implícita de ``setmember()``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Cuando se ejecuta el siguiente código ``ab = 0`` (mutador), Berry VM
hace lo siguiente:
- Obtiene el objeto llamado ``a`` (local o global), genera una
excepción si no existe
- Comprueba si el objeto ``a`` es de tipo ``módulo``, ``instancia`` o
``clase``. Genera una excepción de lo contrario
- Si ``a`` es del tipo ``clase``, comprueba si existe el miembro
``b``. En caso afirmativo, cambia su valor. Si no, genera una
excepción. (los miembros virtuales no funcionan para clases o
métodos estáticos)
- Si ``a`` es del tipo ``instancia``, comprueba si existe el miembro
``b``. En caso afirmativo, cambia su valor. Si no, procede a
continuación
- Comprueba si ``a`` tiene un miembro llamado ``setmember``. Si
es así, lo llama, si no, genera una excepción.
- Si ``a`` es de tipo ``módulo``. Si el módulo no es de solo
lectura, crea o cambia el valor (``setmember`` nunca se llama para
un módulo de escritura). Si el módulo es de solo lectura, entonces
se llama a ``setmember`` si existe.
Manejo de excepciones
~~~~~~~~~~~~~~~~~~~~~
Para indicar que un miembro no existe, ``member()`` devolverá
``undefined`` después de ``import undefined``. También puede generar una
excepción en ``member()``, pero tenga en cuenta que Berry podría
intentar llamar a métodos como ``tostring()`` que aterrizarán en su
método ``member()`` si no existen como métodos estáticos. Para indicar
que un miembro no es válido, ``setmember()`` debe generar una excepción
o devolver ``undefined``. Devolver cualquier otra cosa como ``nil``
indica que la asignación fue exitosa. Tenga en cuenta que puede recibir
nombres de miembros que no sean identificadores válidos de Berry. La
sintaxis ``a.("<->")`` llamará a ``a.member("<->")`` con un nombre de
miembro virtual que no es léxicamente válido, es decir, no se puede
llamar en código normal, excepto mediante el uso indirecto formas como
``introspect`` o ``member()``.
Especificidades para las clases
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
El acceso a los miembros del objeto de clase no desencadena miembros
virtuales. Por lo tanto, no es posible tener métodos estáticos
virtuales.
Especificidades de los módulos
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Los módulos admiten la lectura de miembros estáticos con ``member()``.
Al escribir en un miembro, el comportamiento depende de si el módulo es
de escritura (en la memoria) o de solo lectura (en el firmware). Si se
puede escribir en el módulo, los nuevos miembros se agregan directamente
al módulo y nunca se llama a ``setmember()``. Si el módulo es de solo
lectura, se llama a ``setmember()`` cada vez que intenta cambiar o crear
un miembro. Entonces es su responsabilidad almacenar los valores en un
objeto separado como un global.
Ejemplo
~~~~~~~
.. code:: python
class T
var a
def init()
self.a = 'a'
end
def member(nombre)
return "miembro "+nombre
end
def setmember(nombre, valor)
print("Almacenar '"+nombre+"': "+str(valor))
end
end
t=T()
Ahora intentémoslo:
.. code:: berry
t.a
'a'
.. code:: berry
t.b
'miembro b'
.. code:: berry
t.foo
'miembro foo'
.. code:: berry
t.bar = 2
Almacenar 'bar': 2
Esto también funciona para los módulos:
.. code:: berry
m = module()
m.a = 1
m.member = def (nombre)
return "miembro "+nombre
end
m.setmember(nombre, valor)
print("Almacenar '"+nombre+"': "+str(valor))
end
Intentemoslo:
.. code:: berry
m.a
1
.. code:: berry
m.b
'miembro b'
.. code:: berry
m.c = 3 # la asignación es válida por lo que no se llama a `setmember()
m.c
3
Ejemplo más avanzado:
.. code:: berry
class A
var i
def member(n)
if n == 'ii' return self.i end
return nil # lo hacemos explícito aquí, pero esta línea es opcional
end
def setmember(n, v)
if n == 'ii' self.i = v end
end
end
a=A()
a.i # devuelve nil
a.ii # i llama implícitamente `a.member("ii")`
| attribute_error: el objeto 'A' no tiene atributo 'ii'
| stack traceback:
| stdin:1: en función `main`
|
.. code:: berry
# devuelve un excepción ya que el miembro es nulo (considerado inexistente)
a.ii = 42 # llama implícitamente `a.setmember("ii", 42)`
a.ii # llama implícitamente `a.member("ii")` and returns `42`
42
.. code:: berry
a.i # la variable concreta también fue cambiada
42
8.3 Cómo empaquetar un módulo
-----------------------------
Esta guía lo lleva a través de las diferentes opciones de empaquetado de
código para su reutilización utilizando la directiva de “import” de
Berry.
Comportamiento de ``import``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Cuando se utiliza ``import [as ]``, suceden los
siguientes pasos:
- Hay una caché global de todos los módulos ya importados. Si
```` ya fue importado, ``import`` devuelve el valor en caché
ya devuelto por la primera llamada a ``import``. No se realizan otras
acciones.
- ``import`` busca un módulo de nombre ```` en el siguiente
orden:
1. en módulos nativos incrustados en el firmware en tiempo de
compilación
2. en el sistema de archivos, comenzando con el directorio actual, luego
iterando en todos los directorios desde ``sys.path``: busque el
archivo ````, entonces ``.bec`` (código de bytes
compilado), luego ``.be``. Si ``BE_USE_SHARED_LIB`` está
habilitado, también busca bibliotecas compartidas como
``.so que`` o ``.dll`` aunque esta opción
generalmente no está disponible en MCU.
- Se ejecuta el código cargado. El código debe terminar con una
declaración ``return``. El objeto devuelto se almacena en la memoria
caché global y se pone a disposición de la persona que llama (en el
ámbito local o global).
- Si el objeto devuelto es un ``módulo`` y si el módulo posee un
miembro ``init``, entonces se toma un paso adicional. La función
``.init(m)`` se llama pasando como argumento el propio objeto
del módulo. El valor devuelto por ``init()`` reemplaza el valor en el
caché global. Tenga en cuenta que ``init()`` se llama como máximo una
vez durante la primera ``importación``.
Nota: una función ``init(m)`` implícita siempre está presente en todos
los módulos, incluso si no se declaró ninguno. Esta función implícita no
tiene ningún efecto.
Empaquetado de un módulo
~~~~~~~~~~~~~~~~~~~~~~~~
Aquí hay un ejemplo simple de un módulo:
Archivo ``demo_modulo.be``:
.. code:: berry
# modulo simple
# use `import demo_modulo`
demo_module = module("demo_module")
demo_modulo.foo = "bar"
demo_modulo.decir_hola = def ()
print("Hola Berry!")
end
return demo_modulo # devuelve el módulo como salida de import
Ejemplo de uso:
.. code:: berry
import demo_modulo
demo_modulo
demo_module.decir_hola()
Hola Berry!
.. code:: berry
demo_modulo.foo
'bar'
.. code:: berry
demo_modulo.foo = "baz" # el módulo se puede escribir, aunque esto es muy desaconsejado
demo_modulo.foo
'baz'
Empaquetar un singleton (mónada)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
El problema de usar módulos es que no tienen variables de instancia para
realizar un seguimiento de los datos. Están diseñados esencialmente para
bibliotecas sin estado.
A continuación, encontrará una forma elegante de empaquetar una clase
única devuelta como una “declaración de importación”.
Para ello, utilizamos diferentes trucos. Primero, declaramos la clase
para el singleton como una clase interna de una función, esto evita que
se contamine el espacio de nombres global con esta clase. Es decir, la
clase no será accesible por otro código.
En segundo lugar, declaramos una función ``init()`` del módulo que crea
la clase, crea la instancia y la devuelve.
Según este esquema, ``import `` en realidad devuelve una
instancia de una clase oculta.
Ejemplo de ``demo_monad.be``:
.. code:: berry
# monada simple
# use `import demo_monad`
demo_monad = module("demo_monad")
# el módulo tiene un solo miembro `init()` y delega todo a la clase interna
demo_monad.init = def (m)
# inncer class
class my_monad
var i
def init()
self.i = 0
end
def say_hello()
print("Hola Berry!")
end
end
# rdevolver una sola instancia para esta clase
return my_monad()
end
return demo_monad # evuelve el módulo como la salida de importación, que eventualmente se reemplaza por el valor de retorno de 'init()'
Ejemplo:
.. code:: berry
import demo_monad
demo_monad
# es una instancia no un modulo
demo_monad.say_hello()
Hola Berry!
.. code:: berry
demo_monad.i = 42 # puedes usarlo como cualquier instancia
demo_monad.i
42
.. code:: berry
demo_monad.j = 0 # hay una fuerte verificación de miembros en comparación con los módulos
Attribute_error: la clase 'my_monad' no puede asignarse al atributo 'j'
stack traceback:
stdin:1: en función `main`
8.4 Solidificación
------------------
La solidificación es el proceso de capturar estructuras y códigos Berry
compilados (clases, módulos, mapas, listas…) y almacenarlos en el
firmware. Reduce drásticamente el uso de la memoria, pero tiene algunas
limitaciones.
Módulo ``solidify``
~~~~~~~~~~~~~~~~~~~
La solidificación es manejada por el módulo ``solidify``. Este módulo no
está compilado por defecto debido a su tamaño (~10kB). Debe compilar con
la directiva ``#define BE_USE_SOLIDIFY_MODULE 1``.
El módulo tiene un solo miembro ``dump(x)`` que toma un solo argumento
(el objeto a solidificar) y envía a ``stdout`` el código solidificado.
De forma predeterminada, solidify agrega todas las constantes de cadena
al grupo global. En su lugar, puede generar cadenas débiles (elegibles
para la poda por parte del enlazador) estableciendo el segundo argumento
en “verdadero”.
Por defecto, ``solidify.dump`` genera el código solidificado en la
salida estándar. Puede especificar un archivo como tercer argumento. El
archivo debe estar abierto en modo de escritura y no está cerrado para
que pueda concatenar varios objetos.
``solidify.dump(object:any, [, strings_weak:bool, file_out:file]) -> nil``
Solidificación de funciones
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Puede solidificar una sola función.
Ejemplo:
.. code:: berry
> def f() return "hello" end
> import solidify
> solidify.dump(f)
.. code:: c
/********************************************************************
** Solidified function: f
********************************************************************/
be_local_closure(f, /* name */
be_nested_proto(
0, /* nstack */
0, /* argc */
0, /* varg */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
( &(const bvalue[ 1]) { /* constants */
/* K0 */ be_nested_str(hello),
}),
&be_const_str_f,
&be_const_str_solidified,
( &(const binstruction[ 1]) { /* code */
0x80060000, // 0000 RET 1 K0
})
)
);
/*******************************************************************/
Para compilar utilizando cadenas débiles (es decir, cadenas que el
enlazador puede eliminar si el objeto no está incluido en el ejecutable
de destino), use ``solidify.dump(f, true)``:
.. code:: c
/********************************************************************
** Solidified function: f
********************************************************************/
be_local_closure(f, /* name */
be_nested_proto(
0, /* nstack */
0, /* argc */
0, /* varg */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
( &(const bvalue[ 1]) { /* constants */
/* K0 */ be_nested_str_weak(hello),
}),
be_str_weak(f),
&be_const_str_solidified,
( &(const binstruction[ 1]) { /* code */
0x80060000, // 0000 RET 1 K0
})
)
);
/*******************************************************************/
Solidificación de clases
~~~~~~~~~~~~~~~~~~~~~~~~
Cuando solidifica una clase, incrusta todos los subelementos. También se
agrega un código auxiliar ``C`` para crear la clase y agregarla al
ámbito global.
.. code:: berry
> class demo
var i
static foo = "bar"
def init()
self.i = 0
end
def say_hello()
print("Hello Berry!")
end
end
> import solidify
> solidify.dump(demo)
.. code:: c
/********************************************************************
** Solidified function: init
********************************************************************/
be_local_closure(demo_init, /* name */
be_nested_proto(
1, /* nstack */
1, /* argc */
2, /* varg */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
( &(const bvalue[ 2]) { /* constants */
/* K0 */ be_nested_str(i),
/* K1 */ be_const_int(0),
}),
&be_const_str_init,
&be_const_str_solidified,
( &(const binstruction[ 2]) { /* code */
0x90020101, // 0000 SETMBR R0 K0 K1
0x80000000, // 0001 RET 0
})
)
);
/*******************************************************************/
/********************************************************************
** Solidified function: say_hello
********************************************************************/
be_local_closure(demo_say_hello, /* name */
be_nested_proto(
3, /* nstack */
1, /* argc */
2, /* varg */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
( &(const bvalue[ 1]) { /* constants */
/* K0 */ be_nested_str(Hello_X20Berry_X21),
}),
&be_const_str_say_hello,
&be_const_str_solidified,
( &(const binstruction[ 4]) { /* code */
0x60040001, // 0000 GETGBL R1 G1
0x58080000, // 0001 LDCONST R2 K0
0x7C040200, // 0002 CALL R1 1
0x80000000, // 0003 RET 0
})
)
);
/*******************************************************************/
/********************************************************************
** Solidified class: demo
********************************************************************/
be_local_class(demo,
1,
NULL,
be_nested_map(4,
( (struct bmapnode*) &(const bmapnode[]) {
{ be_const_key(i, -1), be_const_var(0) },
{ be_const_key(say_hello, 2), be_const_closure(demo_say_hello_closure) },
{ be_const_key(init, -1), be_const_closure(demo_init_closure) },
{ be_const_key(foo, 1), be_nested_str(bar) },
})),
(bstring*) &be_const_str_demo
);
/*******************************************************************/
void be_load_demo_class(bvm *vm) {
be_pushntvclass(vm, &be_class_demo);
be_setglobal(vm, "demo");
be_pop(vm, 1);
}
Las subclases también son compatibles.
.. code:: berry
> class demo_sub : demo
var j
def init()
super(self).init()
self.j = 1
end
end
> solidify.dump(demo_sub)
.. code:: c
/********************************************************************
** Solidified function: init
********************************************************************/
be_local_closure(demo_sub_init, /* name */
be_nested_proto(
3, /* nstack */
1, /* argc */
0, /* varg */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
( &(const bvalue[ 3]) { /* constants */
/* K0 */ be_nested_str(init),
/* K1 */ be_nested_str(j),
/* K2 */ be_const_int(1),
}),
&be_const_str_init,
&be_const_str_solidified,
( &(const binstruction[ 7]) { /* code */
0x60040003, // 0000 GETGBL R1 G3
0x5C080000, // 0001 MOVE R2 R0
0x7C040200, // 0002 CALL R1 1
0x8C040300, // 0003 GETMET R1 R1 K0
0x7C040200, // 0004 CALL R1 1
0x90020302, // 0005 SETMBR R0 K1 K2
0x80000000, // 0006 RET 0
})
)
);
/*******************************************************************/
/********************************************************************
** Solidified class: demo_sub
********************************************************************/
extern const bclass be_class_demo;
be_local_class(demo_sub,
1,
&be_class_demo,
be_nested_map(2,
( (struct bmapnode*) &(const bmapnode[]) {
{ be_const_key(init, -1), be_const_closure(demo_sub_init_closure) },
{ be_const_key(j, 0), be_const_var(0) },
})),
be_str_literal("demo_sub")
);
/*******************************************************************/
void be_load_demo_sub_class(bvm *vm) {
be_pushntvclass(vm, &be_class_demo_sub);
be_setglobal(vm, "demo_sub");
be_pop(vm, 1);
}
Solidificación de módulos
~~~~~~~~~~~~~~~~~~~~~~~~~
Cuando solidifica un módulo, incrusta todos los subelementos. También
funciona con listas o mapas incrustados.
.. code:: berry
> def say_hello() print("Hello Berry!") end
> m = module("demo_module")
> m.i = 0
> m.s = "foo"
> m.f = say_hello
> m.l = [0,1,"a"]
> m.m = {"a":"b", "2":3}
> import solidify
> solidify.dump(m)
.. code:: c
/********************************************************************
** Solidified function: say_hello
********************************************************************/
be_local_closure(demo_module_say_hello, /* name */
be_nested_proto(
2, /* nstack */
0, /* argc */
0, /* varg */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
( &(const bvalue[ 1]) { /* constants */
/* K0 */ be_nested_str(Hello_X20Berry_X21),
}),
&be_const_str_say_hello,
&be_const_str_solidified,
( &(const binstruction[ 4]) { /* code */
0x60000001, // 0000 GETGBL R0 G1
0x58040000, // 0001 LDCONST R1 K0
0x7C000200, // 0002 CALL R0 1
0x80000000, // 0003 RET 0
})
)
);
/*******************************************************************/
/********************************************************************
** Solidified module: demo_module
********************************************************************/
be_local_module(demo_module,
"demo_module",
be_nested_map(5,
( (struct bmapnode*) &(const bmapnode[]) {
{ be_const_key(l, -1), be_const_simple_instance(be_nested_simple_instance(&be_class_list, {
be_const_list( * be_nested_list(3,
( (struct bvalue*) &(const bvalue[]) {
be_const_int(0),
be_const_int(1),
be_nested_str(a),
})) ) } )) },
{ be_const_key(m, 3), be_const_simple_instance(be_nested_simple_instance(&be_class_map, {
be_const_map( * be_nested_map(2,
( (struct bmapnode*) &(const bmapnode[]) {
{ be_const_key(a, -1), be_nested_str(b) },
{ be_const_key(2, -1), be_const_int(3) },
})) ) } )) },
{ be_const_key(i, 4), be_const_int(0) },
{ be_const_key(f, -1), be_const_closure(demo_module_say_hello_closure) },
{ be_const_key(s, -1), be_nested_str(foo) },
}))
);
BE_EXPORT_VARIABLE be_define_const_native_module(demo_module);
/********************************************************************/
limitaciones de la solidificación
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
La solidificación funciona para muchos objetos: ``clase``, ``módulo``,
``funciones`` y constantes incrustadas u objetos como ``int``, ``real``,
``string``, ``list`` y ``map``.
Limitaciones:
- Los upvals no son compatibles. No puede solidificar un cierre que
captura upvals del alcance externo
- La captura de variables globales requiere compilar con la opción
``-g`` “globales con nombre” (habilitada de forma predeterminada en
Tasmota)
- Las constantes de cadena están limitadas a 255 bytes, cadenas largas
(más de 255 caracteres no son compatibles, porque nadie nunca los
necesitó)
- Los objetos solidificados son de solo lectura, esto tiene algunas
consecuencias en las clases. Puede solidificar una clase con sus
miembros estáticos cuando se crea, pero no puede solidificar una
función que crea una clase derivada de otra clase o con miembros
estáticos. La razón principal es que la configuración de la
superclase o la asignación de miembros estáticos se implementa
mediante el código mutante en la nueva clase, que no puede funcionar
en una clase no mutante de solo lectura.
- El código solidificado puede depender del tamaño de “int” y “real” y
es posible que no se transfiera a través de MCU con tipos de
diferentes tamaños. Es posible que deba volver a solidificar para
cada objetivo.