【问题标题】:Is it possible to store polymorphic class in shared memory?是否可以将多态类存储在共享内存中?
【发布时间】:2012-10-09 18:45:02
【问题描述】:

假设我有课程Base 和Derived : public Base。 我使用 boost::interprocess 库构建了一个共享内存段。是否可以有类似这样的代码:

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

我在这里看到的问题是,例如 Base 派生类所需的空间是未知的(那么要分配多少 shmem?)

问:如何在应用程序之间通过指针传递对象?

【问题讨论】:

    标签: c++ boost shared-memory boost-interprocess


    【解决方案1】:

    只需阅读它的documentation

    特别是:

    虚拟禁止

    虚表指针和虚表在地址中 构造对象的过程的空间,所以如果我们放置一个 具有虚函数或虚基类的类,虚 放在共享内存中的指针对其他进程无效 他们会崩溃。

    这个问题很难解决,因为每个进程都需要一个 不同的虚拟表指针和包含它的对象 指针在许多进程之间共享。即使我们映射映射 每个进程在同一地址的区域,虚拟表可以是 在每个过程中的不同地址。启用虚拟功能 对于进程之间共享的对象,需要对编译器进行深度更改 并且虚函数会受到性能影响。这就是为什么 Boost.Interprocess 没有任何计划支持虚函数 以及进程之间共享的映射区域中的虚拟继承。

    【讨论】:

    • 太棒了,完全符合我的预期。谢谢!
    • @Queequeg:有趣的是,我看到共享内存的命名段与多态对象一起使用。在这种特殊情况下,单个进程(同时)访问该段并使用共享内存段,因此如果进程崩溃,在重新启动时它可以找到它的所有状态。它涉及重写所有虚拟指针,所以它肯定涉及。
    【解决方案2】:

    我相信您正在研究对象的序列化。看看http://www.boost.org/doc/libs/1_51_0/libs/serialization/doc/index.html

    您可以采取以下几种方法: 1.序列化你的C++类 2.将数据发送到另一个应用程序 3.反序列化成C++类。

    【讨论】:

    • +1!正如 Tony Hoare 在 CSP 中所说:不要通过分享来交流,要通过交流来分享。
    • 当然可以发送派生类。但不是作为“多态性”。
    【解决方案3】:

    共享内存最初只允许 POD 结构(本质上,它们可能有构造函数/复制/等...)。

    Boost.Interprocess 通过在共享内存段的偏移量之上模拟指针语义来提高标准。

    但是,虚拟指针不是指向纯数据的指针,它是指向代码段的指针,这就是事情变得复杂的地方,因为代码段不一定从一个地址映射到相同的地址进程到另一个进程(即使它们是从同一个二进制文件启动的)。

    所以...不,虚拟指针-多态对象不能存储在共享内存中。


    然而,仅仅因为许多 C++ 实现选择使用虚拟指针机制并不意味着这是具有多态行为的唯一方法。例如,在 LLVM 和 Clang 中,它们建立在封闭的层次结构上,以在没有虚拟指针(和 RTTI)的情况下获得多态性,从而降低内存需求。这些对象可以有效地存储在共享内存中。

    那么,如何获得与共享内存兼容的多态性:我们需要不来存储指向表/函数的指针,但是我们可以存储索引。

    这个想法的例子,但可能会被改进。

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

    好的...我想现在您欣赏编译器的魔力了...

    关于用法,幸好要简单得多:

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

    ideone 正在行动。

    【讨论】:

      【解决方案4】:
      //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;
      }
      

      【讨论】:

      • 我看到 +10 的人说无法存储多态类,有人投了反对票。我建议在投票之前将这个“c++ 中的编译时间和运行时多态性”放入谷歌。尽管 CashCow 获得了 10 票,但他的声明仅适用于虚拟表——运行时多态类的属性。社区远远领先于那些老式的解决方案,我们已经使用 c++17。请阅读原始问题:“是否可以在共享内存中存储多态类?” .它没有具体说明是运行时多态还是编译时多态。
      【解决方案5】:
      //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() ;
          }
      

      【讨论】:

      • 我看到有 +10 的人说无法存储多态类,有人投了反对票。我建议在投票之前将这个“c ++中的编译时间和运行时多态性”放入谷歌。尽管 CashCow 获得了 10 票,但他的声明仅适用于虚拟表——运行时多态类的属性。社区远远领先于那些老式的解决方案,我们已经在使用 c++17。
      猜你喜欢
      • 1970-01-01
      • 2011-07-18
      • 1970-01-01
      • 1970-01-01
      • 2020-12-23
      • 1970-01-01
      • 1970-01-01
      • 2021-03-13
      • 2014-07-16
      相关资源
      最近更新 更多