Sí para los indicadores de función y cierres. No para std::function
.
un puntero de función es la más simple - es sólo un puntero como cualquier otro lo que sólo puede leerlo como bytes:
template <typename _Res, typename... _Args>
std::string serialize(_Res (*fn_ptr)(_Args...)) {
return std::string(reinterpret_cast<const char*>(&fn_ptr), sizeof(fn_ptr));
}
template <typename _Res, typename... _Args>
_Res (*deserialize(std::string str))(_Args...) {
return *reinterpret_cast<_Res (**)(_Args...)>(const_cast<char*>(str.c_str()));
}
Pero se sorprendió al descubrir que, incluso sin recompilación la dirección de una función cambiará en cada invocación del programa. No es muy útil si quieres transmitir la dirección. Esto se debe a ASLR, que puede desactivar en Linux iniciando your_program
con setarch $(uname -m) -LR your_program
.
Ahora puede enviar el puntero a una máquina diferente ejecutando el mismo programa y ¡llámelo! (Esto no implica la transmisión de código ejecutable. Pero a menos que esté generando código ejecutable en tiempo de ejecución, no creo que esté buscando eso.)
Una función lambda es bastante diferente.
std::function<int(int)> addN(int N) {
auto f = [=](int x){ return x + N; };
return f;
}
El valor de f
será el int N
capturado. ¡Su representación en la memoria es la misma que int
! El compilador genera una clase sin nombre para la lambda, de la cual f
es una instancia. Esta clase tiene operator()
sobrecargado con nuestro código.
La clase sin nombre presenta un problema de serialización. También presenta un problema para devolver funciones lambda desde funciones. El último problema se resuelve por std::function
.
std::function
por lo que entiendo se implementa mediante la creación de una clase de contenedor de plantilla que efectivamente contiene una referencia a la clase sin nombre detrás de la función lambda a través del parámetro de tipo de plantilla. (Esto es _Function_handler
en functional.) std::function
toma un puntero a la función de un método estático (_M_invoke
) de esta clase contenedora y lo almacena más el valor de cierre.
Lamentablemente, todo está enterrado en private
miembros y el tamaño del valor de cierre no se almacena. (No es necesario, porque la función lambda conoce su tamaño).
De modo que std::function
no se presta a la serialización, pero funciona bien como un modelo. Seguí lo que hace, lo simplifiqué mucho (solo quería serializar lambdas, no la miríada de otras cosas invocables), guardé el tamaño del valor de cierre en un size_t
y agregué métodos para (de) serialización. ¡Funciona!
Olvídalo. Busque el concepto de una "llamada a procedimiento remoto" y implementaciones populares para ello. –
No, no y no. Cualquier objeto de este tipo, al despegar envoltorios bastante seguros, es un puntero a algún código de máquina. No puede enviar código de máquina ni punteros a otros procesos u otras máquinas. –
@ kerrek-sb No estoy seguro de si RPC se ajusta a mi propósito cuando tengo una gran cantidad de pequeños objetos de función construidos y deconstruidos continuamente en el lado del emisor. Sería mucho más agradable transmitir de alguna manera la lógica misma. – shaniaki