Shared Memory erlaubt ursprünglich nur POD-Strukturen (im Kern können sie Konstruktoren haben/kopieren/etc ...).
Boost.Interprocess
erhöht die Messlatte durch Emulieren von Zeigersemantik über Offsets in das Shared-Memory-Segment. Ein virtueller Zeiger ist jedoch kein Zeiger auf reine Daten, es ist ein Zeiger auf Code-Abschnitte, und das ist, wo Dinge kompliziert werden, da Code-Abschnitte nicht unbedingt auf die gleiche Adresse von einem Prozess zum anderen zugeordnet werden (selbst wenn sie von derselben Binärdatei gestartet wurden).
Also ... nein, virtuelle Zeiger-polymorphe Objekte können nicht im Shared Memory gespeichert werden.
Allerdings nur, weil viele C++ Implementierungen wählte einen virtuellen Zeiger Mechanismus zu verwenden, bedeutet nicht, dass dies der einzige Weg ist polymorph Verhalten zu haben. Zum Beispiel bauen sie in LLVM und Clang auf ihren geschlossenen Hierarchien auf, um Polymorphismus ohne virtuelle Zeiger (und RTTI) zu erhalten, um die Speicheranforderungen zu verringern. Diese Objekte könnten effektiv in einem gemeinsamen Speicher gespeichert werden.
So, wie Polymorphismus mit Shared-Memory-kompatibel zu bekommen: brauchen wir nicht Zeiger zu speichern, um Tabellen/Funktionen, jedoch können wir Indizes speichern.
Beispiel für die Idee, könnte aber wahrscheinlich verfeinert werden.
/// 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
Okay ... Ich denke, dass Sie jetzt den Compiler Magie zu schätzen wissen ...
In Bezug auf die Nutzung, ist es glücklicherweise viel einfacher:
/// 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 Aktion bei ideone.
groß, genau das, was ich erwartet hatte. Vielen Dank! – Queequeg
@Queequeg: Interessanterweise habe ich eine Verwendung von benannten Segmenten von gemeinsam genutztem Speicher mit polymorphen Objekten gesehen. In diesem speziellen Fall greift ein einzelner Prozess (gleichzeitig) auf das Segment zu und das Shared-Memory-Segment wird verwendet, so dass bei einem Neustart des Prozesses der gesamte Status wieder gefunden werden kann. Es beinhaltet jedoch alle virtuellen Zeiger neu zu schreiben, also ist es definitiv beteiligt. –