2012-10-19 11 views
5

Załóżmy, że mam klasę Base i Derived : public Base. Skonstruowałem segment pamięci współużytkowanej przy użyciu biblioteki boost :: interprocess. Czy to możliwe, aby mieć kodu podobnego do tego:Czy można przechowywać klasę polimorficzną we wspólnej pamięci?

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

Problemy Widzę tu jest, na przykład, że wymagana przestrzeń dla klasy pochodnej of Base jest nieznana (tak jak wiele shmem przeznaczyć?)

Q: jak przekazywać obiekty za pośrednictwem wskaźników między aplikacjami?

Odpowiedz

10

Wystarczy przeczytać jego documentation

w szczególności:

Wirtualność zabronione

wirtualny wskaźnik stolik i stół wirtualne są na adres przestrzeni procesu, który konstruuje obiekt, tak jeśli umieścimy klasę z funkcją wirtualną lub wirtualną klasą podstawową, wirtualny wskaźnik umieszczony w pamięci współdzielonej będzie nieprawidłowy dla innych procesów , a one ulegną awarii.

Ten problem jest bardzo trudny do rozwiązania, ponieważ każdy proces wymaga innego wirtualnego wskaźnika tabeli, a obiekt, który zawiera ten wskaźnik jest współdzielony z wieloma procesami. Nawet jeśli mapujemy odwzorowany region w tym samym adresie w każdym procesie, tabela wirtualna może być w innym adresie w każdym procesie. Aby włączyć funkcje wirtualne dla obiektów współużytkowanych między procesami, potrzebne są głębokie zmiany kompilatora , a funkcje wirtualne ucierpiałyby na wydajności. Z tego powodu Boost.Interprocess nie ma żadnego planu obsługującego funkcję wirtualną i dziedziczenia wirtualnego w mapowanych regionach współużytkowanych między procesami.

+0

super, dokładnie to, czego oczekiwaliśmy. Dzięki! – Queequeg

+0

@Queequeg: Co ciekawe, widziałem wykorzystanie nazwanych segmentów pamięci współdzielonej z obiektami polimorficznymi. W tym konkretnym przypadku pojedynczy proces uzyskuje dostęp do segmentu (jednocześnie), a segment pamięci współdzielonej jest wykorzystywany, więc w przypadku awarii procesu, po ponownym uruchomieniu może on znaleźć cały swój stan z powrotem. Wymaga to przepisywania wszystkich wirtualnych wskaźników, więc jest zdecydowanie zaangażowany. –

3

Uważam, że patrzysz na serializację obiektów. Wystarczy popatrzeć na http://www.boost.org/doc/libs/1_51_0/libs/serialization/doc/index.html

Kilka sposobów można zrobić, to: 1. serialize twoja klasa C++ 2. wysyłania danych do innej aplikacji 3. Cofnięcie do klasy C++.

+1

+1! Jak powiedział Tony Hoare w CSP: * Nie komunikuj się poprzez dzielenie się, dziel się przez komunikowanie. * –

+0

Dobry pomysł serializacji, +1 – Queequeg

+0

oczywiście możesz wysłać klasę pochodną. Ale nie jako "polimorfizm". – CashCow

3

Współdzielona pamięć początkowo pozwala tylko na konstrukcje POD (w zasadzie mogą mieć konstruktory/kopie/etc ...).

Boost.Interprocess podnosi poprzeczkę, emulując semantykę wskaźników na szczycie przesunięć w segmencie pamięci wspólnej.

Jednak wskaźnik wirtualny nie jest wskaźnikiem do czystych danych, jest wskaźnikiem do sekcji kodu, i to jest, gdzie rzeczy się komplikują, ponieważ sekcje kodu niekoniecznie są mapowane na ten sam adres z jednego procesu do innego (nawet jeśli zostały uruchomione z tego samego pliku binarnego).

Tak ... nie, wskaźniki wirtualne - obiekty polimorficzne nie mogą być przechowywane we wspólnej pamięci.


Jednak tylko dlatego, że wiele implementacji C++ zdecydowali się wykorzystać mechanizm virtual-pointer nie oznacza, że ​​jest to jedyny sposób, aby mieć polimorficzne. Na przykład w LLVM i Clang budują na swoich zamkniętych hierarchiach, aby uzyskać polimorfizm bez wirtualnych wskaźników (i RTTI), aby zmniejszyć wymagania dotyczące pamięci. Te obiekty mogłyby być skutecznie przechowywane we wspólnej pamięci.

Tak, jak się polimorfizm zgodny z pamięci współdzielonej: musimy nie przechowywać wskaźniki do tablic/funkcji, jednak możemy przechowywać indeksy.

Przykład pomysłu, ale mógłby prawdopodobnie zostać dopracowany.

/// 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 

porządku ... myślę, że teraz docenić kompilator magia ...

odniesieniu do użytkowania, to na szczęście o wiele prostsze:

/// 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 

w akcji na 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

Widzę, że ktoś z +10 powiedział, że nie można przechowywać klas polimorficznych, a ktoś inny mnie głosował. Proponuję, aby przed głosowaniem w dół umieścić w google ten "czas kompilacji i polimorfizm runtime w C++". Mimo że CashCow otrzymał 10 głosów, jego oświadczenie dotyczy tylko tabel wirtualnych - atrybutów klas polimorficznych środowiska wykonawczego. Społeczność przeniosła się daleko przed tymi starymi rozwiązaniami mody, używamy już C++ 17. Proszę przeczytać oryginalne pytanie: "Czy można przechowywać klasę polimorficzną we wspólnej pamięci?" . Nie mówi konkretnie, czy jest to polimorfizm runtime, czy polimorfizm czasu kompilacji. –

-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

Widzę, że ktoś z +10 powiedział, że nie można przechowywać polimorficznych klas, a ktoś mnie głosował. Proponuję, aby przed głosowaniem w dół umieścić w google ten "czas kompilacji i polimorfizm runtime w C++". Mimo że CashCow otrzymał 10 głosów, jego oświadczenie dotyczy tylko tabel wirtualnych - atrybutów klas polimorficznych środowiska wykonawczego. Społeczność przeniosła się daleko przed tymi starymi rozwiązaniami mody, używamy już C++ 17. –

Powiązane problemy