У меня есть структура, которую я хотел бы сериализовать на диск как часть формата файла сохранения JSON. (Упрощенная) структура структуры выглядит следующим образом:
pub struct Effect {
pub source: Source,
pub duration: Duration,
pub implementation: fn(&mut GameState, Source) -> Result<()>
}
Я хотел бы сериализовать эту структуру через serde
. Сериализация указателей функций, конечно, невозможна, поэтому я хочу аннотировать каждую функцию, которую я использую, как implementation
с уникальным идентификатором, записать этот идентификатор как часть сохраненного JSON, а затем использовать идентификатор для поиска функции. указатель, который будет использоваться при десериализации структуры, возможно, путем создания большой карты functionId->functionPointer
в качестве первого шага. Есть ли хороший способ пометить определения функций такими метаданными?
(примечание: я знаю, что могу преобразовать Effect
в признак, но мне действительно хочется использовать здесь указатели на функции по ряду причин — эргономика, производительность, большое количество существующего кода, зависящего от этой структуры, и т. д.)
Построить отображение fnId->fnPtr несложно, по крайней мере вручную, но вообще невозможно построить обратное отображение, так как одна и та же функция может иметь несколько адресов. Таким образом, единственный жизнеспособный способ, как сказал @drewtato, - каким-то образом закодировать идентификатор в самом коде функции. Это может быть тип возвращаемого значения или более экзотические способы, такие как интерпретация машинного кода функции с некоторым известным префиксом.
Указатели функций указывают непосредственно на машинный код. К ним невозможно прикрепить дополнительные метаданные. Вы также не можете сравнивать указатели функций: вы можете получить как ложноотрицательные результаты (функции, которые были скомпилированы дважды), так и ложноположительные результаты (функции, машинный код которых был дедуплицирован с другой функцией, которая имеет идентичный код).
Единственный способ получить достоверную информацию об указателе функции — это вызвать ее и посмотреть, что она делает. Но в этот момент вы могли бы вместо этого использовать объект типажа, а не увлекаться сигнатурой функции. Объект типажа может иметь метод для выполнения операции и метод для запроса его идентификатора в таблице сериализации.
В качестве альтернативы, если вам все равно нужно построить таблицу и все возможные параметры известны во время компиляции, перечисление также будет уместным.
честно говоря, единственным разумным решением, вероятно, является какая-то схема «толстого указателя», где каждый указатель функции также несет идентификатор для сериализации и реализует признак Fn
, но автоматическое присвоение такого идентификатора было бы очень сложно (возможно, вам придется каким-то образом создать собственный const POINTER
для каждой функции со своим указателем и использовать его везде вместо исходного указателя?)
Единственный способ, который я могу придумать, чтобы сделать эту работу без серьезных изменений, — это заставить эту функцию вычислять собственную сериализацию. Например, он может возвращать идентификатор вместо
()
, а сериализация может вызывать его с помощью фиктивныхGameState
иSource
, чтобы получить его. Для десериализации вам все равно придется вручную перечислять все функции, которые вы планируете использовать. Если бы мне пришлось делать это с нуля, я бы использовал перечисление с кучей вариантов модулей вместо указателя на функцию.