2012-10-19 12 views
5

Supponiamo che abbia classe Base e Derived : public Base. Ho costruito un segmento di memoria condivisa usando la libreria boost :: interprocess. E 'possibile avere il codice simile a questo:È possibile memorizzare una classe polimorfica nella memoria condivisa?

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

I problemi che vedo qui è per esempio che lo spazio necessario per una classe derivata di Base è sconosciuto (così quanto shmem allocare?)

Q: come passare oggetti tramite puntatori tra le applicazioni?

risposta

10

appena letto sua documentation

In particolare:

Virtuality proibito

Il puntatore tabella virtuale e la tabella virtuale sono nello spazio di indirizzi del processo che costruisce l'oggetto, così se poniamo una classe con una funzione virtuale o una classe base virtuale, il puntatore virtuale inserito nella memoria condivisa non sarà valido per altri processi e si romperanno.

Questo problema è molto difficile da risolvere, poiché ogni processo richiede un puntatore di tabella virtuale diverso da e l'oggetto che contiene il puntatore è condiviso su più processi. Anche se mappiamo la regione mappata nello stesso indirizzo in ogni processo, la tabella virtuale può essere in un indirizzo diverso in ogni processo. Per abilitare le funzioni virtuali per gli oggetti condivisi tra i processi, sono necessarie modifiche profonde del compilatore e le funzioni virtuali subirebbero un impatto sulle prestazioni. Ecco perché Boost.Interprocess non ha alcun piano per supportare la funzione virtuale e l'ereditarietà virtuale in regioni mappate condivise tra processi.

+0

grande, esattamente quello che mi aspettavo. Grazie! – Queequeg

+0

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

3

Credo che stiate osservando la serializzazione degli oggetti. Date un'occhiata a http://www.boost.org/doc/libs/1_51_0/libs/serialization/doc/index.html

A pochi modi si può fare è: 1. serializzare le C++ classe dati 2. invio a un'altra app 3. deserialize in classe C++.

+1

+1! Come ha detto Tony Hoare in CSP: * Non comunicare condividendo, condividendo comunicando. * –

+0

buona idea con la serializzazione, +1 – Queequeg

+0

ovviamente puoi inviare la classe derivata. Ma non come un "polimorfismo". – CashCow

3

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.

-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

Vedo qualcuno con +10 ha detto che non è possibile memorizzare classi polimorfiche e qualcuno mi ha votato. Suggerisco di votare su google questo "tempo di compilazione e polimorfismo di runtime in C++". Anche se CashCow ha ricevuto 10 voti, la sua affermazione si applica solo alle tabelle virtuali - attributi delle classi polimorfiche di runtime. La comunità si è mossa più avanti rispetto a quelle vecchie soluzioni di moda, già usiamo C++ 17. Leggi la domanda originale: "È possibile memorizzare una classe polimorfica nella memoria condivisa?" . Non dice in modo specifico se si tratta di polimorfismo di runtime o polimorfismo del tempo di compilazione. –

-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

Vedo qualcuno con +10 ha detto che non è possibile memorizzare classi polimorfiche e qualcuno mi ha votato. Suggerisco di votare su google questo "tempo di compilazione e polimorfismo di runtime in C++". Anche se CashCow ha ricevuto 10 voti, la sua affermazione si applica solo alle tabelle virtuali - attributi delle classi polimorfiche di runtime. La comunità si è mossa più avanti rispetto a quelle vecchie soluzioni di moda, già usiamo C++ 17. –

Problemi correlati