C ++ способ создания структуры с переменным количеством элементов - PullRequest
0 голосов
/ 28 мая 2018

Мне нужно создать область памяти с переменным количеством элементов в конце.Что-то, что я могу написать так:

#pragma pack(push,0)
struct MyData
{
    int noOfItems;
    int buffer[0];
};
#pragma pack(pop)

MyData * getData(int size)
{
     bufferSize = size* sizeof(int) + sizeof(MyData ::noOfItems);
     MyData * myData= (MyData *)new char[m_bufferSize];
     return myData;
}

Этот код работает на VS 2015 с предупреждением, что массив нулевого размера не является стандартным

Немного поиска показал, что этохакер C и не поддерживается в C ++.

Массив с размером 0

Массив нулевой длины

Что произойдет, если я определю массив 0 размера в C / C ++? .

Как я могу сделать это в C ++

Ответы [ 2 ]

0 голосов
/ 28 мая 2018
template<class T, class D>
struct var_array_at_end {
  var_array_at_end( std::size_t N ) {
    ::new( (void*)data() ) std::aligned_storage_t<sizeof(T)*N, alignof(T)>;
    for (std::size_t i = 0; i < N; ++i) {
      ::new( (void*)( data()+sizeof(T)*i) ) ) T();
    }
  }
  char* data() { return reinterpret_cast<char*>(this)+sizeof(D); }
  char const* data() const { return reinterpret_cast<char*>(this)+sizeof(D); }
  T* ptr(std::size_t i = 0) { return reinterpret_cast<T*>( data()+sizeof(T)*i ); }
  T const* ptr(std::size_t i = 0) const { return reinterpret_cast<T*>( data()+sizeof(T)*i ); }
  T& operator[](std::size_t n) {
    return *ptr(n);
  }
  T const& operator[](std::size_t n) const {
    return *ptr(n);
  }
};

struct MyData:
  var_array_at_end<int, MyData>
{
private:
  explicit MyData( int count ):
    var_array_at_end<int, MyData>( count>0?(unsigned)count:0 ),
    noOfItems(count)
  {}
  struct cleanup {
    void operator()(MyData* ptr) {
      char* buff = reinterpret_cast<char*>(ptr);
      ptr->~MyData();
      delete[] buff;
    }
  };
public:
  using up = std::unique_ptr<MyData*, cleanup>;
  static up create( int count ) {
    if (count < 0) count = 0;
    std::unique_ptr<char> buff = std::make_unique<char[]>( sizeof(MyData)+sizeof(int)*count );
    auto* ptr = ::new( (void*)buff.get() ) MyData( count );
    (void)buff.release();
    return up( ptr, {} );
  }
  int noOfItems;
};

MyData * getData(int size)
{
   return MyData::create(size).release(); // dangerous, unmanaged memory
}

Я считаю, что это соответствует стандарту, если ваша реализация не добавляет заполнение для массивов тривиальных типов (например, char).Я не знаю ни о какой реализации.

Я не предполагал, что MyData содержит только простые старые данные;Вы могли бы вставить std::vector в это с вышеупомянутым.Я мог бы упростить несколько строк с этим предположением.

Это больше, чем немного боли.

auto foo = MyData::create(100) создает уникальный ptr для MyData, который имеет буферпосле 100 int с.(*foo)[77] обращается к 77-му элементу буфера.

Из-за дефекта в стандарте у вас нет массива после MyData, а скорее буфер, содержащий 100 различных int объектов в смежной памятиместах.Есть очень раздражающие различия между этими двумя вещами;арифметика наивного указателя гарантированно работает внутри массивов, но не между упакованными смежными int s в буфере.Я не знаю о компиляторе, который обеспечивает это различие.

0 голосов
/ 28 мая 2018

Как насчет этого?Это работает для тривиально копируемых типов:

template <typename T, typename INT = size_t>
class packed_array {
  public:
    packed_array(INT size) {
      buf_ = new char[sizeof(INT) + size * sizeof(T)];
      memcpy(buf_, &size, sizeof(INT));
    }
    ~packed_array() { delete[] buf_; }

    void set(INT index, T val) {
      memcpy(buf_ + sizeof(INT) + index * sizeof(T), &val, sizeof(T));
    }
    T get(INT index) const {
        T temp;
        memcpy(&temp, buf_ + sizeof(INT) + index * sizeof(T), sizeof(T));
        return temp;
    }

    const char* data() const { return buf_; }

  private:
    char* buf_;

  static_assert(std::is_trivially_copyable<T>::value);
};

int main() {
  int n;
  std::cin >> n;
  packed_array<double, int> a(n);
  for (int i = 0; i < n; i++)
    a.set(i, pow(2.0, i)); 
  for (int i = 0; i < n; i++)
    std::cout << a.get(i) << std::endl;
}

Live демо: https://wandbox.org/permlink/Vc4ok756R1Sxieoj

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...