Мое предположение:address
в Solidity есть понятие s, но они представляют contract
s. Контракты могут иметь резервные функции, но я не думаю, что вы можете задавать им параметры. Думая о передаче функции в качестве параметра по адресу, как вы делали бы это в C.
Есть ли законный способ передавать функции в качестве параметров, а если нет, то есть ли хакерский способ?
Если есть то как? А если нет, то почему?
Функции (также известные как методы) определяются ABI и имеют идентификатор метода, который представляет собой первые 4 байта sha3 (Keccak-256) сигнатуры метода.
Вот пример вызова someFunction
on contract
:contract.call(bytes4(sha3("someFunction()")))
Вот проверенная функция с передачей a methodId
в качестве параметра:
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);
}
}
Протестируйте его в браузере Solidity , используя "0x2fbebd38", 9
в качестве параметров invoke
, затем убедитесь, что он _n
равен 9
.
Заметки:
0x2fbebd38
является результатом bytes4(sha3("foo(uint256)"))
(не забывайте о необходимости использовать канонические типы, в данном случае uint256
, в соответствии с ABI. )
Возвращаемые значения от call и callcode являются логическими, либо вызов выполнен успешно, либо вызов не выполнен. Невозможно вернуть значение из вызова, поскольку для этого контракту необходимо заранее знать тип возвращаемого значения.
Чтобы добавить к ответу Ника Джонсона, типы функций в последних версиях Solidity теперь позволяют описывать указатели функций:
http://solidity.readthedocs.io/en/latest/types.html#function-types
Типы функций — это типы функций. Переменные функционального типа могут быть назначены из функций, а функциональные параметры функционального типа могут использоваться для передачи функций и возврата функций из вызовов функций. Типы функций бывают двух видов — внутренние и внешние функции.
ответ eth относится к вызовам внешних функций (между контрактами или с помощью внешнего интерфейса для вызова вашего собственного контракта); здесь я попытаюсь ответить на вызовы внутренних функций.
В настоящее время Solidity не предоставляет синтаксиса для описания типа указателя функции, поэтому вы не можете использовать их в качестве аргументов или возвращаемых значений. Однако функции являются первоклассными и могут быть присвоены переменным с помощью var
; вот пример из руководства пользователя 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;
}
}
Для тех, кто приходит из Google и ищет быстрый пример того, как передать функцию в качестве параметра, ознакомьтесь со следующей выдержкой кода (источник 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...
}
Как отмечалось в других ответах, на ваш вопрос есть разные ответы в зависимости от того, говорите ли вы о вызове функции «извне» или о вызове ее «внутри».
В том же контракте последние версии Solidity включают языковые функции для передачи «указателя функции» (на самом деле это не указатель, но он ведет себя как таковой). Другими словами, существуют переменные типа «функция». Это то, что я имею в виду под "внутренней" передачей функции и ее вызовом.
Для «внешних» вызовов, то есть вызовов функций на адрес в цепочке, вы должны использовать член типа адреса Solidity «call()» и/или Yul (сборка) и т. д. для эмуляции вызова контракта, поскольку Solidity скомпилирует такой вызов функции. при выражении как "someAddr.someFunction()" ... и это означает кодирование сигнатуры функции в качестве селектора (селекторы - это первые 4 байта хэша сигнатуры функции) плюс аргументы, которые вы хотите передать, и выполнение необходимые байт-коды EVM (сборка) с использованием этих данных.
Другими словами, для «внешних» функций «указатель на функцию» — это кортеж (адрес, селектор).
никсмак
Карл Флерш
никсмак
Карл Флерш
Дбрайсон
никсмак
Ник Джонсон