2012-10-19 11 views
5

Angenommen, ich habe die Klasse Base und Derived : public Base. Ich habe ein gemeinsames Speichersegment mit boost :: interprocess library erstellt. Ist es möglich, Code ähnlich wie diese zu haben:Ist es möglich, polymorphe Klasse im Shared Memory zu speichern?

Base* b = new Derived(); 
write(b); //one app writes 
Base* b2 = read(b); //second app reads 
//b equals b2 (bitwise, not the ptr location) 

Die Probleme, die ich hier sehen, ist zum Beispiel, dass der erforderliche Raum für eine abgeleitete Klasse von Basis unbekannt ist (so wie viel shmem zuzuteilen?)

Q: wie Objekte über Zeiger zwischen Anwendungen übergeben werden?

Antwort

10

lesen gerade seine documentation

Insbesondere:

Virtuality

verboten

Der virtuelle Tabellenzeiger und der virtuellen Tabelle in der Adress Raum des Prozesses sind, der das Objekt konstruiert, so Wenn wir eine Klasse mit einer virtuellen Funktion oder einer virtuellen Basisklasse platzieren, ist der virtuelle Zeiger im Shared Memory für andere Prozesseungültigund sie werden abstürzen.

Dieses Problem ist sehr schwierig zu lösen, da jeder Prozess einen anderen virtuellen Tabellenzeiger benötigt und das Objekt, das den Zeiger enthält, wird über viele Prozesse verteilt. Selbst wenn wir die zugeordnete Region in jedem Prozess unter der gleichen Adresse abbilden, kann die virtuelle Tabelle an einer anderen Adresse in jedem Prozess sein. Um die virtuellen Funktionen für Objekte zu aktivieren, die von Prozessen gemeinsam genutzt werden, sind tiefgreifende Compiler-Änderungen erforderlich, und virtuelle Funktionen würden einen Leistungseinbruch erleiden. Deshalb hat Boost.Interprocess keinen Plan, um die virtuelle Funktion und die virtuelle Vererbung in gemappten Regionen zu unterstützen, die zwischen Prozessen geteilt werden.

+0

groß, genau das, was ich erwartet hatte. Vielen Dank! – Queequeg

+0

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

3

Ich glaube, Sie betrachten Serialisierung von Objekten. Werfen Sie einen Blick auf http://www.boost.org/doc/libs/1_51_0/libs/serialization/doc/index.html

Ein paar Möglichkeiten, wie Sie tun können, ist: 1. serialize Ihre C++ Klasse 2. Senden von Daten an eine andere App 3. deserialize in C++ Klasse.

+1

+1! Wie Tony Hoare in CSP sagte: * Kommunizieren Sie nicht durch Teilen, teilen durch Kommunikation. * –

+0

gute Idee mit Serialisierung, +1 – Queequeg

+0

natürlich können Sie die abgeleitete Klasse senden. Aber nicht als "Polymorphismus". – CashCow

3

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.

-1
//From the example above , I have removed VTable 
// I also removed static variables as per boost::interprocess 
// static variable don't work with shared memory, and also I did not see 
// any advantage in actually builting a VTable for all derived classes 
#include <vector> 
#include <boost/bind.hpp> 
#include <boost/function.hpp> 

template <class> class BaseT; 

class Base { 
    template <class> friend class BaseT; 
    boost::function< int (void) > _get; 
    boost::function< void (int) > _set; 
public: 

    int get() { 
     return _get(); 
    } //  -> Implement: 'int get() ' in Derived 

    void set(int i) { 
     _set(i); 
    } // = 0 -> Implement: 'void set(int i)' in Derived 
}; // class Base 

template <class Derived> 
class BaseT : public Base { 

public: 
    BaseT() : Base(), impl(static_cast<Derived *> (this)) { 
     Base::_get = boost::bind(&BaseT<Derived>::get, this); 
     Base::_set = boost::bind(&BaseT<Derived>::set, this, _1); 
    } 

    int get() { 
     return impl->get(); 
    } 

    void set(int i) { 
     impl->set(i); 
    } 

private: 
    Derived * impl; 
}; 


//some A implementation of Base 
struct A : BaseT<A> { 

    int get() { 
     return 101; //testing implementation 
    } 

    void set(int i) { 
     ; //implementation goes here 
    } 
}; 

//some B implementation of Base 
struct B : BaseT<B> { 

    int get() { 
     return 102; //testing implementation 
    } 

    void set(int i) { 
     ; //implementation goes here 
    } 
}; 

int main() { 
    BaseT<A> objectA; 
    BaseT<B> objectB; 
    Base *a = &objectA; 
    Base *b = &objectB; 
    std::cout << a->get() << " returned from A class , " 
      << b->get() << " returned from B class " << std::endl; 
    return 0; 
} 
+0

Ich sehe jemanden mit +10 sagte, dass es nicht möglich ist, polymorphe Klassen zu speichern, und jemand hat mich gewählt. Ich schlage vor, in google diese "Kompilierzeit und Laufzeitpolymorphie in C++" zu setzen. Obwohl CashCow 10 Stimmen erhalten hat, gilt seine Aussage nur für virtuelle Tabellen - Attribute von polymorphen Laufzeitklassen. Die Community ist diesen alten Mode-Lösungen weit voraus, wir benutzen bereits C++ 17. Bitte lesen Sie die ursprüngliche Frage: "Ist es möglich polymorphe Klasse im Shared Memory zu speichern?" . Es wird nicht speziell gesagt, ob es sich um einen Laufzeitpolymorphismus oder einen Kompilierzeitpolymorphismus handelt. –

-1
//While redefining I changed semantics of constnance in getter, 
//and had non- const Derived pointer used for both getter and setter. 
//But original simantics can be preserved as following: 

    int get() const { 
     //return impl->get(); 
     //this enforces that get has to be const 
     static_cast<const Derived *> (this)->get() ; 
    } 
+0

Ich sehe jemanden mit +10 sagte, dass es nicht möglich ist, polymorphe Klassen zu speichern, und jemand hat mich gewählt. Ich schlage vor, in google diese "Kompilierzeit und Laufzeitpolymorphie in C++" zu setzen. Obwohl CashCow 10 Stimmen erhalten hat, gilt seine Aussage nur für virtuelle Tabellen - Attribute von polymorphen Laufzeitklassen. Die Community ist diesen alten Mode-Lösungen weit voraus, wir benutzen bereits C++ 17. –

Verwandte Themen