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!
Lascia perdere. Cerca il concetto di una "chiamata a procedura remota" e implementazioni popolari per questo. –
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. –
@ 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