La memoria condivisa originariamente consente solo le strutture POD (al cuore, possono avere costruttori/copia/ecc ...).
Boost.Interprocess
aumenta la barra emulando la semantica dei puntatori sopra gli offset nel segmento di memoria condivisa.
Tuttavia, un puntatore virtuale non è un puntatore a dati puri, è un puntatore a sezioni di codice, ed è qui che le cose si complicano perché sezioni di codice non sono necessariamente associati allo stesso indirizzo da un processo all'altro (anche se sono stati lanciati dallo stesso binario).
Quindi ... no, gli oggetti polimerici puntatori virtuali non possono essere archiviati nella memoria condivisa.
Tuttavia, solo perché molti C++ implementazioni scelto di utilizzare un meccanismo virtuale puntatore non significa che questo è l'unico modo per avere polimorfismo. Ad esempio, in LLVM e Clang si costruiscono sulle loro gerarchie chiuse per ottenere il polimorfismo senza puntatori virtuali (e RTTI) in modo da ridurre i requisiti di memoria. Tali oggetti potrebbero, in effetti, essere memorizzati nella memoria condivisa.
Così, come ottenere il polimorfismo compatibile con memoria condivisa: abbiamo bisogno non per memorizzare i puntatori alle tabelle/funzioni, tuttavia siamo in grado di memorizzare indici.
Esempio di idea, ma potrebbe probabilmente essere raffinato.
/// In header
#include <cassert>
#include <vector>
template <class, size_t> class BaseT;
class Base {
template <class, size_t> friend class BaseT;
public:
int get() const; // -> Implement: 'int getImpl() const' in Derived
void set(int i); // = 0 -> Implement: 'void setImpl(int i)' in Derived
private:
struct VTable {
typedef int (*Getter)(void const*);
typedef void (*Setter)(void*, int);
VTable(): _get(0), _set(0) {}
Getter _get;
Setter _set;
};
static std::vector<VTable>& VT(); // defined in .cpp
explicit Base(size_t v): _v(v) {}
size_t _v;
}; // class Base
template <class Derived, size_t Index>
class BaseT: public Base {
public:
BaseT(): Base(Index) {
static bool const _ = Register();
(void)_;
}
// Provide default implementation of getImpl
int getImpl() const { return 0; }
// No default implementation setImpl
private:
static int Get(void const* b) {
Derived const* d = static_cast<Derived const*>(b);
return d->getImpl();
}
static void Set(void* b, int i) {
Derived* d = static_cast<Derived*>(b);
d->setImpl(i);
}
static bool Register() {
typedef Base::VTable VTable;
std::vector<VTable>& vt = Base::VT();
if (vt.size() <= Index) {
vt.insert(vt.end(), Index - vt.size() + 1, VTable());
} else {
assert(vt[Index]._get == 0 && "Already registered VTable!");
}
vt[Index]._get = &Get;
vt[Index]._set = &Set;
}
}; // class BaseT
/// In source
std::vector<VTable>& Base::VT() {
static std::vector<VTable> V;
return V;
} // Base::VT
int Base::get() const {
return VT()[_v]._get(this);
} // Base::get
void Base::set(int i) {
return VT()[_v]._set(this, i);
} // Base::set
Va bene ... Credo che ora si apprezza la magia del compilatore ...
Per quanto riguarda l'utilizzo, è fortunatamente molto più semplice:
/// Another header
#include <Base.h>
// 4 must be unique within the hierarchy
class Derived: public BaseT<Derived, 4> {
template <class, size_t> friend class BaseT;
public:
Derived(): _i(0) {}
private:
int getImpl() const { return _i; }
void setImpl(int i) { _i = i; }
int _i;
}; // class Derived
in azione a ideone.
grande, esattamente quello che mi aspettavo. Grazie! – Queequeg
@Queequeg: È interessante notare che ho visto un uso di segmenti denominati di memoria condivisa con oggetti polimorfici. In questo caso particolare, un singolo processo accede sempre al segmento (simultaneamente) e il segmento di memoria condivisa viene utilizzato in modo che, in caso di arresto anomalo del processo, al riavvio possa ritrovare tutto il suo stato. Si tratta di riscrivere tutti i puntatori virtuali, quindi è sicuramente coinvolto. –