Skip to content

[DRAFT] C++ chapter 15.6#387

Open
Microvenator wants to merge 5 commits intomainfrom
cpp-chapter-15-5
Open

[DRAFT] C++ chapter 15.6#387
Microvenator wants to merge 5 commits intomainfrom
cpp-chapter-15-5

Conversation

@Microvenator
Copy link
Copy Markdown
Contributor

@Microvenator Microvenator commented Mar 30, 2026

НЕ ГОТОВА, СМОТРЕТЬ РАНО

@Microvenator Microvenator changed the title Cpp chapter 15 5 [DRAFT] C++ chapter 15.6 Mar 30, 2026
Comment on lines +53 to +54
std::size_t rows = 3;
std::size_t cols = 4;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Не критично, но лучше добавить const. Здесь и в других похожих местах.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment on lines +137 to +143
JaggedBuffer(std::size_t row_count, const std::size_t * col_sizes);

// Освобождает всю память
~JaggedBuffer();

// Записывает сегмент по индексу
void set_segment(std::size_t index, const int * segment);
Copy link
Copy Markdown
Collaborator

@khva khva Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. JaggedBuffer(std::size_t row_count, const std::size_t * col_sizes)
    Сильно сбивает с толку такая сигнатура и наименование. Сразу возникает пара вопросов. Какие столбцы? И сколько их? И только потом понимаешь, что сначала идет количество строк, а потом их длины.

Кроме того традиционно принято сначала передавать массив, а потом его длину:
JaggedBuffer(const std::size_t * segment_sizes, std::size_t segment_count)

  1. void set_segment(std::size_t index, const int * segment)
    Это некорректный дизайн интерфейса. Вызывающая сторона где-то независимо от JaggedBuffer должна хранить длину каждой строки и как-то учитывать это при каждом вызове. Недружелюбный и очень ломкий интерфейс. Легко передать segment не той длины.

Предлагаю изменить задачу. Суть:

Данные передаются по каналу связи с негарантированной доставкой (например, используетс протокол UDP). Имеется библиотека написанная на Си, которая обеспечивает гарантированную доставку данных. Библиотека позволяет получить данные по частям (сегментами):

// Дескриптор канала данных.
typedef void* recv_desc_t;

// Функция инициализирует получение данных. Параметры:
// [in] sgm_size - максимальный размер сегмента,
// [out] pdesc - дескриптор канала данных.
// Функция инициализирует дескриптор и возвращает 0 в случае успеха,
// отрицательный код ошибки в противном случае. Код ошибки err_no_memory
// означает, что не удалось выделить память для дескриптора.
int init_recv(size_t sgm_size, recv_desc_t* pdesc);

// Функция завершает получение данных и закрывает дескриптор канала 
// данных. Параметры:
// [in/out] pdesc - валидный дескриптор канала данных.
void close_recv(recv_desc_t* pdesc);

// Функция возвращает ожидаемое количество сегментов,
// 0 означает, что все сегменты были получены. Параметры:
// [in] desc - валидный дескриптор канала данных.
size_t sgm_left(recv_desc_t desc);

// Функция возвращает максимальный размер сегмента для указанного
// канала данных. Параметры:
// [in] desc - валидный дескриптор канала данных.
size_t max_sgm_size(recv_desc_t desc);

// Функция возвращает сегмент данных. Параметры:
// [in] desc - валидный дескриптор канала данных,
// [out] idx - индекс сегмента,
// [out] buf - буффер для сохранения сегмента,
// [in/out] size - размер буффера на входе и реальный размер
// сегмента данных на выходе.
// Функция возвращает 0 в случае успеха и отрицательный код ошибки
// в противном случае. Код ошибки err_wait_for_data означает, что
// нужно попробовать вызвать функцию позже. Код ошибки 
// err_insufficient_buffer сигнализирует, что размер переданного
// буффера недостаточен, а в size возвращается ожидаемый размер.
int recv_sgm(recv_desc_t desc, size_t* idx, void* buf, size_t* size);

Необходимо реализовать класс враппер для Си интерфейса:

template <class T = std::uint8_t>
class JaggedBuffer {
public:
    // Конструктор инициализирует канал данных и буффер 
    // для хранения сегментов. Выбрасывает std::bad_alloc()
    // при нехватки памяти.
    explicit JaggedBuffer(std::size_t max_segment_size);
    ~JaggedBuffer();

    // Метод получает очередной сегмент данных и сохраняет
    // его во внутренний буффер. Возвращает общее количество
    // полученных сегментов и обшее количество сегментов.
    std::pair<std::size_t, std::size_t> receive_segment();

    // Метод компанует сегменты и возвращает собранные во
    // едино данные. Выбрасывает std::runtime_error, если
    // были получены не все сегменты.
    std::vector<T> compose_segments() const;
private:
    // Дескриптор канала данных
    recv_desc_t m_channel = nullptr;
    // Массив сегментов
    T ** m_segments = nullptr;
    // Длины сегментов
    std::size_t * m_sizes;
    // Количество сегментов
    std::size_t m_count = 0;
};

P.S. Напишу сишную либу и эталонный вариант JaggedBuffer, если предложение будет принято.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Предложение принято. Реализовывай=)

Comment on lines +197 to +198
for (std::size_t i = 0; i < rows; ++i)
data[i] = nullptr;
Copy link
Copy Markdown
Collaborator

@khva khva Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Лучше использовать алгоритм:

std::fill(data, data + rows, nullptr);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ок.


С точки зрения компилятора разницы между этими вариантами записи нет. Потому что при передаче сишного массива в функцию происходит [низведение массива:](/courses/cpp/chapters/cpp_chapter_0132/#block-array-to-pointer-decay) он автоматически приводится к указателю на нулевой элемент. Зато с точки зрения разработчика запись `char* argv[]` более «говорящая»: сразу понятно, что `argv` — это массив указателей на символы `char`.

Итак, аргументы попадают в `main()` в качестве сишных строк. Чтобы получить из них числа, есть функции:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Есть более эффективные функции преобразования строки в число std::from_chars():
https://en.cppreference.com/w/cpp/utility/from_chars.html

Предлагаю использовать их. Не настаиваю, на твоё усмотрение.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ими гораздо сложнее пользоваться. А глава и так не простая. Предлагаю оставить как есть.


----------

## Резюме
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ещё двойные указатели нужны при взаимодействии с Си-шными интерфейсами.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants