Назначение двумерных массивов переменного размера - PullRequest
0 голосов
/ 03 октября 2018

Я пытаюсь сохранить двумерный массив c-строк переменной длины в структуре, чтобы я мог передавать и перестраивать его через сетевой сокет.

План состоит в том, чтобы иметь строки и столбцы, чтов заголовке пакета, помогите мне прочитать линзы переменного размера и arr, которые идут после.Я считаю, что я должен синтаксически писать указатели неправильно, или есть какой-то вспомогательный указатель, который мне нужно использовать при установке их в структуру.

struct STORAGE {
   int rows;     // hdr
   int cols;     // hdr
   int** lens;
   const char*** arr;
}

// code
int rows = 11;
int cols = 2;

int lens[rows][cols];
const char* arr[rows][cols];
// ... fill with strings ...
// ... along with lens ...

STORAGE store;
store.rows = rows;
store.cols = cols;
store.lens = lens;
store.arr = arr;

Я получаю следующие ошибки при компиляции этого кода:

ошибка: неверное преобразование из int в int** [-fpermissive]

ошибка: невозможно преобразовать const char* [11][2] в `const char *** 'в присваивании

Я работаю в основном на Java, но понимаю, как работают указатели и тому подобное.Синтаксис этого кода немного сбоку для человека с моим прошлым (в основном пишу java / c ++ и меньше c).Любые предложения?

Примечание: причина, по которой я не использую более сложные типы, такие как строки, карты, векторы и т. Д., Заключается в том, что мне нужно передавать структуру по сети (то есть указатели на кучу не будутработать, если они имеют переменные размеры).Это должны быть массивы низкого уровня, если кто-то не может предложить лучшее решение.

Ответы [ 2 ]

0 голосов
/ 03 октября 2018

Использование низкоуровневых вещей, таких как массивы, мало чем поможет, это уже слишком сложно.Кроме того, это может привести к адским проблемам с совместимостью (например, думать о порядке следования байтов).

Если у вас нет очень строгих ограничений производительности, используйте вместо этого решение, разработанное специально для работы в сети: буферы протокола .Это немного излишне для вашего случая, но хорошо масштабируется, если вам нужно что-то добавить.

Чтобы использовать буферы протокола, вы сначала определяете «сообщения» (структуры) в файле .proto, затемскомпилируйте их в C ++ с помощью Proto Compiler.

Вы определяете свое сообщение следующим образом (это полный файл .proto):

syntax = "proto2";

package test;

message Storage {
   message Row {
       repeated string foo = 1;
   }
   repeated Row row = 1;
}

Прямая поддержка 2D-массивов отсутствует, нос массивом массивов все будет в порядке (repeated означает, что в данном поле может быть несколько значений, в основном это вектор).Вы можете добавить поля для размера массива, если вам нужен быстрый доступ к ним, но проверки размера повторяющихся полей должно быть достаточно в большинстве практических случаев.

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

Код на C ++ становится немного длиннее в некоторых местах, так как вам нужно использовать методы получения и установки, но этодолжно быть хорошо компенсировано тем, что вам никогда не нужно думать о сериализации - это происходит само по себе.

Пример использования этой вещи в C ++ может выглядеть так:

#include "test.pb.h"  // Generated from test.proto

using ::test::Storage;

int main() {
  Storage s;
  Storage::Row* row1 = s.add_row();
  row1->add_foo("foo 0,0");
  row1->add_foo("foo 0,1");
  Storage::Row* row2 = s.add_row();
  row2->add_foo("foo 1,0");
  row2->add_foo("foo 1,1");

  assert(s.row_size() == 2);
  assert(s.row(0).foo_size() == 2);

  s.PrintDebugString();  // prints to stdout
}

InВ результате вы получите этот вывод (обратите внимание, что это отладочный вывод, а не реальная сериализация):

row {
  foo: "foo 0,0"
  foo: "foo 0,1"
}
row {
  foo: "foo 1,0"
  foo: "foo 1,1"
}

Для полноты: в приведенном выше примере исходные файлы были test.proto и test.cpp, скомпилированные с использованием:

protoc --cpp_out=. test.proto
g++ test.cpp test.pb.cc -o test -lprotobuf
0 голосов
/ 03 октября 2018

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

Одномерный std::vector<int> или std::vector<uint8_t> уже предоставляет вам низкий уровеньМассив выделяется непрерывно с использованием члена std::vector::data().

Любые дополнительные измерения, которые вам нужны, могут быть определены путем правильного разбиения этих данных на части.Для передачи по сети вам нужно будет заранее указать необходимые размеры секционирования и затем отправить данные.

Что-то вроде:

Transmit num_of_dimensions
Transmit dim_size_1, dim_size_2, dim_size_3, ...
Transmit data

Receive num_of_dimensions
Loop Receiving dimension sizes
Receive dim_size_1 * dim_size_2 * dim_size_3 * ... of data

Что мне, вероятно, придется решать в такой ситуациикласс / структура выглядит так:

template<typename T>
class MultiDimensional {
    size_t num_dimensions_; // If known in advance can be made a template parameter also
    std::vector<size_t> dimension_sizes_;
    std::vector<T> data_;
public:
    const T& indexing_accessor(...) const;
    T& indexing_accessor(...);
    std::vector<uint8_t> render_transmision_data();
    // construct from transmission data
    MultiDimensional(std::vector<uint8_t>& transmission_data); 
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...