2012-09-09 17 views
10

¿Es posible serializar y deserializar un std::function, un objeto de función o un cierre en general en C++? ¿Cómo? ¿C++ 11 facilita esto? ¿Hay algún soporte de biblioteca disponible para tal tarea (por ejemplo, en Boost)?Objetos de función de serialización

Por ejemplo, supongamos que un programa en C++ tiene una std::function que se necesita para ser comunicada (decir a través de un socket TCP/IP) a otro programa C++ que reside en otra máquina. ¿Qué sugieres en tal escenario?


Editar:

Para aclarar, las funciones que han de ser trasladado se supone que deben ser puros y de lado de libre efecto. Entonces no tengo problemas de seguridad o de desajuste de estado.

Una solución al problema es crear un pequeño lenguaje específico de dominio incrustado y serializar su árbol de sintaxis abstracta. Tenía la esperanza de encontrar algún tipo de soporte de idioma/biblioteca para mover una representación de funciones independiente de la máquina.

+2

Olvídalo. Busque el concepto de una "llamada a procedimiento remoto" y implementaciones populares para ello. –

+0

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. –

+0

@ 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

Respuesta

6

C++ no tiene soporte integrado para la serialización y nunca fue concebido con la idea de transmitir el código de un proceso a otro, no sea una máquina a otra. Los idiomas que pueden hacerlo generalmente tienen un IR (representación intermedia del código que es independiente de la máquina) y una reflexión.

Así que le queda escribir un protocolo para transmitir las acciones que desea, y el enfoque DSL es ciertamente viable ... dependiendo de la variedad de tareas que desea realizar y la necesidad de rendimiento.

Otra solución sería ir con un idioma existente. Por ejemplo, la base de datos Redis NoSQL incorpora un motor LUA y puede ejecutar scripts LUA, puede hacer lo mismo y transmitir scripts LUA en la red.

8

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!

+1

Pero esto dependería de la arquitectura. Por ejemplo, esto no funcionaría desde x86 hasta el brazo. – portforwardpodcast

+0

@daniel podría poner su código para el último bit con 'std :: function' – subzero

+0

Lo hice en el trabajo, así que tendré que pedir primero una versión de copyright. ¡Informaré de nuevo! –

Cuestiones relacionadas