代码之家  ›  专栏  ›  技术社区  ›  dempzorz

将任意元素存储在连续内存中

  •  2
  • dempzorz  · 技术社区  · 8 年前

    我试图创建一个数据结构,它将在连续内存中容纳N个不同类型的数据。所以在编译时,我可以说我想存储3种不同类型的4个元素,在内存中看起来像111122223333。

    template<std::size_t N, typename... Args>
    class Batch
    {
        private:
            std::tuple<std::array<Args, N>...> data_;
            size_t currentPos_;
    
        public:
            template<typename T>
            void addToArray(std::array<T, N>& array, const T& value)
            {
                array[currentPos_] = value;
            }
    
            void add(const Args&... values)
            {
                //????
                addToArray(/*array, value*/);
    
                currentPos_++;
            }
    
            const void* data()
            {
                &return data_;
            }
    };
    
    
    int main()
    {
        Batched<3, float, double, int> b;
    
        b.add(1.0f, 1.0, 1);
        b.add(2.0f, 2.0, 2);
        b.add(3.0f, 3.0, 3);
        b.add(4.0f, 4.0, 4);
        return 0;
    }
    

    即使我能做到这一点,内存布局是否正确?有更好的方法吗?

    2 回复  |  直到 8 年前
        1
  •  3
  •   max66    8 年前

    我认为这不是一个好主意,但……我只是为了好玩才展示它

    使用 std::vector<char> (以及C++11添加的方法授予的对以下内存的访问权限 data() )和善良的老人 memcpy() ,我想你可以简单地做如下

    #include <vector>
    #include <cstring>
    #include <iostream>
    
    template <typename... Args>
    class Batch
     {
       private:
          std::vector<char> buffer;
    
       public:
    
          void addHelper ()
           { }
    
          template <typename T, typename ... Ts>
          void addHelper (T const & v0, Ts ... vs)
           { 
             auto  pos = buffer.size();
    
             buffer.resize(pos + sizeof(T));
    
             std::memcpy(buffer.data() + pos, & v0, sizeof(T));
    
             addHelper(vs...);
           }
    
          void add (const Args&... values)
           { addHelper(values...); }
    
          const void * data()
           { return buffer.data(); }
    
          void toCout ()
           { toCoutHelper<Args...>(0U, buffer.size()); }
    
          template <typename T, typename ... Ts>
          typename std::enable_if<(0U < sizeof...(Ts)), void>::type
             toCoutHelper (std::size_t  pos, std::size_t  size)
           {
             if ( pos < size )
              {
                T val;
    
                std::memcpy( & val, buffer.data() + pos, sizeof(T) );
    
                std::cout << " - " << val << std::endl;
    
                toCoutHelper<Ts...>(pos+sizeof(T), size);
              }
           }
    
          template <typename T, typename ... Ts>
          typename std::enable_if<0U == sizeof...(Ts), void>::type
             toCoutHelper (std::size_t  pos, std::size_t  size)
           {
             if ( pos < size )
              {
                T val;
    
                std::memcpy( & val, buffer.data() + pos, sizeof(T) );
    
                std::cout << " - " << val << std::endl;
    
                toCoutHelper<Args...>(pos+sizeof(T), size);
              }
           }
    
     };
    
    
    int main()
     {
       Batch<float, double, int> b;
    
       b.add(1.0f, 1.0, 1);
       b.add(2.0f, 2.0, 2);
       b.add(3.0f, 3.0, 3);
       b.add(4.0f, 4.0, 4);
    
       b.toCout();
    
       return 0;
     }
    

    ---编辑--- toCout() 打印(至 std::cout )所有存储值;只是建议如何使用这些值。

    正如ildjarn所指出的(谢谢!)如果在 Args... 类型是一些非POD(普通旧数据)类型。

    this page .

    我转录了相关部分

    无法使用memcpy安全复制的类型示例如下 标准::字符串。这通常使用计数的引用来实现 使计数器递增。如果使用memcpy制作副本 则不会调用复制构造函数,计数器将 左边的值比它应该的值低一。这可能会 导致过早释放包含

    ---编辑3---

    正如ildjarn所指出的(再次感谢!)使用此解决方案,离开 成员

    如果有人以这种方式使用返回的指针

       char const * pv = (char const *)b.data();
    
       size_t  pos = { /* some value here */ };
    
       float  f { *(float*)(pv+pos) };  // <-- risk of unaligned access
    

    在某些体系结构中,可能会导致访问 float * 在未对齐的地址中,可能会导致程序终止

    从返回的指针恢复值的正确(安全)方法 数据() toCoutHelper()

       char const * pv = (char const *)b.data();
    
       size_t  pos = { /* some value here */ };
    
       float  f; 
    
       std::memcpy( & f, pv + pos, sizeof(f) );
    
        2
  •  2
  •   Trevor Hickey    8 年前

    有两种词汇类型可以帮助您。
    std::variant std::any .

    std::variant更适合您的预期用途。

    而不是像这样创建自己的类型:

    Batched<3, float, double, int> b;
    

    考虑使用:

    std::vector<std::variant<float, double, int>> vec;
    

    然后可以正常添加元素:

    vec.emplace_back(1);    //int
    vec.emplace_back(1.0f); //float
    vec.emplace_back(1.0);  //double