2012-09-09 7 views
10

È possibile serializzare e deserializzare uno std::function, un oggetto funzione o una chiusura in generale in C++? Come? C++ 11 facilita questo? È disponibile un supporto di libreria per tale attività (ad esempio, in Boost)?Oggetti funzione serializzazione

Ad esempio, supponiamo che un programma C++ abbia un std::function che è necessario comunicare (ad esempio tramite un socket TCP/IP) a un altro programma C++ che si trova su un'altra macchina. Cosa suggerisci in uno scenario del genere?


Edit:

per chiarire, le funzioni che devono essere trasferiti dovrebbero essere pura e priva di effetti collaterali. Quindi non ho problemi di sicurezza o di disallineamento di stato.

Una soluzione al problema è la creazione di un linguaggio di dominio limitato incorporato e la serializzazione del suo albero di sintassi astratto. Speravo di trovare un supporto per la lingua/libreria per spostare invece una rappresentazione indipendente dalle funzioni della macchina.

+2

Lascia perdere. Cerca il concetto di una "chiamata a procedura remota" e implementazioni popolari per questo. –

+0

No, no e no. Qualsiasi oggetto di questo tipo, quando si rimuovono i wrapper piuttosto tipografici, è un puntatore a qualche codice macchina. Non è possibile inviare né codice macchina né puntatori ad altri processi o altre macchine. –

+0

@ kerrek-sb Non sono sicuro che RPC si adatti al mio scopo, in cui ho un sacco di oggetti di piccole dimensioni costruiti e decostruiti continuamente nel lato mittente. Sarebbe molto più bello trasmettere in qualche modo la logica stessa. – shaniaki

risposta

6

No.

C++ non ha supporto integrato per la serializzazione e non è mai stato concepito con l'idea di trasmettere il codice da un processo all'altro, affinchè una macchina all'altra. Le lingue che possono farlo sono generalmente caratterizzate da una IR (rappresentazione intermedia del codice indipendente dalla macchina) e dalla riflessione.

Così ti rimane un protocollo per la trasmissione delle azioni che desideri e l'approccio DSL è sicuramente praticabile ... a seconda della varietà di attività che desideri eseguire e della necessità di prestazioni.

Un'altra soluzione sarebbe quella di andare con una lingua esistente. Ad esempio, il database Redis NoSQL incorpora un motore LUA e può eseguire script LUA, è possibile fare lo stesso e trasmettere gli script LUA sulla rete.

8

Sì per i puntatori di funzione e le chiusure. Non per std::function.

un puntatore a funzione è il più semplice - si tratta solo di un puntatore come qualsiasi altro modo si può solo leggere come byte:

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())); 
}     

Ma sono rimasto sorpreso di scoprire che anche senza ricompilazione l'indirizzo di una funzione cambierà su ogni invocazione del programma. Non molto utile se si desidera trasmettere l'indirizzo. Ciò è dovuto a ASLR, che è possibile disattivare su Linux avviando your_program con setarch $(uname -m) -LR your_program.

Ora è possibile inviare il puntatore a una macchina diversa che esegue lo stesso programma e chiamarlo! (Ciò non implica la trasmissione di codice eseguibile. A meno che non si generi codice eseguibile in fase di esecuzione, non credo che lo si stia cercando)

Una funzione lambda è molto diversa.

std::function<int(int)> addN(int N) { 
    auto f = [=](int x){ return x + N; }; 
    return f; 
} 

Il valore di f sarà catturato int N. La sua rappresentazione in memoria è la stessa di int! Il compilatore genera una classe senza nome per il lambda, di cui f è un'istanza. Questa classe ha il sovraccarico del codice operator().

La classe senza nome presenta un problema di serializzazione. Presenta anche un problema per il ritorno delle funzioni lambda dalle funzioni. Quest'ultimo problema è risolto da std::function.

std::function per quanto ho capito viene implementato creando una classe wrapper basata su modello che trattiene in modo efficace un riferimento alla classe senza nome dietro la funzione lambda tramite il parametro del tipo di modello. (Questo è _Function_handler in functional.) std::function accetta un puntatore a funzione di un metodo statico (_M_invoke) di questa classe wrapper e memorizza quello più il valore di chiusura.

Sfortunatamente, tutto è sepolto nei membri private e la dimensione del valore di chiusura non viene memorizzata. (Non è necessario, perché la funzione lambda conosce le sue dimensioni.)

Quindi std::function non si presta alla serializzazione, ma funziona bene come un progetto. Ho seguito quello che fa, semplificato molto (volevo solo serializzare lambda, non la miriade di altre cose richiamabili), ho salvato la dimensione del valore di chiusura in un size_t e ho aggiunto metodi per la (de) serializzazione. Funziona!

+1

Ma questo dipenderebbe dall'architettura. Ad esempio questo non funzionerebbe da x86 a braccio. – portforwardpodcast

+0

@daniel puoi inserire il tuo codice per l'ultimo bit con 'std :: function' – subzero

+0

L'ho fatto al lavoro, quindi dovrò prima chiedere una liberatoria per il copyright. Ti riporto! –