Mi conjetura: existe el concepto de address
en Solidity, pero representan contract
s. Los contratos pueden tener funciones de respaldo, pero no creo que pueda darles parámetros. Pensando en pasar la función como parámetro por dirección como lo harías en C.
¿Existe una forma legítima de pasar funciones como parámetros o, de no ser así, existe una forma pirateada?
Si lo hay, ¿cómo? Y si no lo hay, ¿por qué no?
Las funciones (también conocidas como métodos) están especificadas por ABI y tienen un ID de método, que son los primeros 4 bytes del sha3 (Keccak-256) de la firma del método.
Aquí hay un ejemplo de invocación someFunction
en contract
:contract.call(bytes4(sha3("someFunction()")))
Aquí hay una función probada con pasar a methodId
como parámetro:
contract C1 {
uint public _n; // public just for easy inspection in Solidity Browser
function foo(uint n) returns(uint) {
_n = n;
return _n;
}
function invoke(bytes4 methodId, uint n) returns(bool) {
return this.call(methodId, n);
}
}
Pruébelo en Solidity Browser usando "0x2fbebd38", 9
como parámetros para invoke
, luego vea que _n
es igual a 9
.
Notas:
0x2fbebd38
es el resultado de bytes4(sha3("foo(uint256)"))
(no olvide la necesidad de usar tipos canónicos, en este caso uint256
, según la ABI ) .
Los valores de retorno de call y callcode son booleanos, ya sea que la llamada se haya realizado correctamente o que haya fallado. No es posible devolver un valor de call ya que eso requeriría que el contrato conozca el tipo de devolución de antemano.
Para agregar a la respuesta de Nick Johnson, los tipos de función en versiones recientes de solidity le permiten describir punteros de función ahora:
http://solidity.readthedocs.io/en/latest/types.html#function-types
Los tipos de función son los tipos de funciones. Las variables del tipo de función se pueden asignar desde funciones y los parámetros de función del tipo de función se pueden usar para pasar funciones y devolver funciones desde llamadas a funciones. Los tipos de funciones vienen en dos sabores: funciones internas y externas
La respuesta de eth se aplica a las llamadas a funciones externas (entre contratos, o mediante el uso de la interfaz externa para llamar a su propio contrato); aquí intentaré responder a las llamadas de funciones internas.
Actualmente, Solidity no proporciona sintaxis para describir el tipo de un puntero de función, por lo que no puede tomarlos como argumentos o valores de retorno. Sin embargo, las funciones son de primera clase y se pueden asignar a variables usando var
; aquí está el ejemplo del manual de usuario de Solidity :
contract FunctionSelector {
function select(bool useB, uint x) returns (uint z) {
var f = a;
if (useB) f = b;
return f(x);
}
function a(uint x) returns (uint z) {
return x * x;
}
function b(uint x) returns (uint z) {
return 2 * x;
}
}
Para cualquiera que venga de Google y busque un ejemplo rápido de cómo pasar una función como parámetro, consulte el siguiente extracto del código (fuente https://docs.soliditylang.org/en/latest/types.html#function-types )
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;
library ArrayUtils {
// internal functions can be used in internal library functions because
// they will be part of the same code context
function map(uint[] memory self, function (uint) pure returns (uint) f)
internal
pure
returns (uint[] memory r)
{
r = new uint[](self.length);
for (uint i = 0; i < self.length; i++) {
r[i] = f(self[i]);
}
}
function reduce(
uint[] memory self,
function (uint, uint) pure returns (uint) f
)
internal
pure
returns (uint r)
{
r = self[0];
for (uint i = 1; i < self.length; i++) {
r = f(r, self[i]);
}
}
// more methods...
}
Como se señaló en otras respuestas, hay distintas respuestas a su pregunta dependiendo de si está hablando de llamar a la función "externamente" o llamarla "internamente".
Dentro del mismo contrato, las versiones recientes de Solidity incluyen funciones de lenguaje para pasar un "puntero de función" (no es realmente un puntero en sí, pero se comporta como tal). En otras palabras, existen variables de tipo "función". Esto es lo que quiero decir con pasar "internamente" una función y llamarla.
Para llamadas "externas", es decir, llamadas de función a una dirección en cadena, usaría el miembro de tipo de dirección de Solidity "llamada()" y/o Yul (ensamblaje), etc. para emular una llamada de contrato, ya que Solidity compilaría dicha llamada de función cuando se expresa como "someAddr.someFunction()"... y esto equivale a codificar la firma de la función como un selector (los selectores son los primeros 4 bytes del hash de la firma de la función) más los argumentos que desea pasar, y ejecutar el bytecodes EVM necesarios (ensamblado) usando esos datos.
En otras palabras, para funciones "externas", el "puntero de función" es la tupla (dirección, selector).
niksmac
Karl Floersch
niksmac
Karl Floersch
dbryson
niksmac
nick johnson