Как передать std::vector<char>* как void*, а затем снова привести void* к std::vector<char>*?

Я использую esp-idf для выполнения некоторых http-запросов и хочу использовать c++, потому что мне так удобнее. esp_http_client работает примерно так:

  1. Вы заполняете назначенный инициализатор:
char[some_length] local_response_buffer;

// I want to use std::vector<char> local_response_buffer for dynamic allocation

esp_http_client_config_t config = {
        .host = "httpbin.org",
        .path = "/get",
        .disable_auto_redirect = true,
        .event_handler = http_event_handler,
        .user_data = local_response_buffer, // user_data is void* so the type is erased
    };

  1. При написании обработчика события при получении данных вы возвращаете void* user_data и копируете в него временный буфер фрагмента http-данных:
esp_err_t http_event_handler(esp_http_client_event_t *evt)
{
    switch (evt->event_id) {
    /* handle other cases... */
    case HTTP_EVENT_ON_DATA:
        // append evt->data (which is actually C-style char* casted to void*) 
        // into evt->user_data (this is our user_data that has been casted to void*).
        // I want to use std::vector::insert here for this purpose.
        break;
}

Я хочу использовать std::vector вместо char[] для удобного динамического размещения. Как лучше всего это сделать?

Вы можете использовать static_cast(): преобразование любого указателя в указатель на void и обратно в указатель на исходный (или более квалифицированный cv) тип сохраняет исходное значение. Но я бы также использовал класс с std::vector в качестве члена, а не только вектор сам по себе.

sklott 07.02.2023 20:54

Почему вектор? Почему не std::string?

Michaël Roy 07.02.2023 20:55

что вы уже пробовали? Знаете ли вы, что динамическое размещение может быть источником серьезных ошибок в микроконтроллерах?

Christian B. 07.02.2023 21:08

@MichaëlRoy Хорошо, это сработало. используя reinterpret_cast() и используя оператор += с std::string и char*. Но у меня есть вопрос, += более эффективен, чем выполнение str1=str1+str2? Я имею в виду, копирует ли он строку много раз? Если это так, то это может привести к сбою для моего варианта использования, стеки задач очень маленькие.

Rajat Mondal 07.02.2023 21:13

@Кристиан Б. Да, я знаю несколько примеров, я могу использовать std::array, если увижу какие-либо проблемы, но основная проблема заключается в приведении типов. По какой-то причине он не работает для std::vector, но работает для std::string.

Rajat Mondal 07.02.2023 21:18

Каков срок службы std::vector? Просто помните, что независимо от того, передаете ли вы указатель на std::vector или указатель на первый байт его содержимого, вектор должен оставаться в живых, пока вы не закончите использовать его/его содержимое. Как только вектор умирает, он освобождает принадлежащий ему буфер.

Ben Voigt 07.02.2023 22:26

@BenVoigt Да, я понимаю. Я создаю только один векторный/строковый объект, который находится в той же области, в которой я его использую, после того, как он был заполнен обработчиком событий, и я передаю ссылку на него, а не на первый его элемент. Настоящая проблема оказалась непроблемой. по какой-то причине intellisense показал мне, что есть unquilified-id, хотя его не было, и мне было интересно, почему это не сработает. Ошибка исчезла после того, как я ее скомпилировал.

Rajat Mondal 07.02.2023 22:35

@RajatMondal «По какой-то причине это не работает для std::vector, но работает для std::string» - тогда вы должны были опубликовать фактический код, с которым у вас возникли проблемы. Кастинг должен работать точно так же, независимо от того, какой контейнер вы решите использовать.

Remy Lebeau 07.02.2023 22:38

@RemyLebeau, это была ошибка IDE, извините.

Rajat Mondal 07.02.2023 22:40

@RajatMondal std::string::operator+=() и std::string::operator+() в большинстве случаев одинаковы с точки зрения эффективности, особенно если дополнение к исходной строке велико. Если вы не планируете обрабатывать несколько сотен http-запросов в секунду, вам не следует ожидать каких-либо измеримых преимуществ от использования +=.

Michaël Roy 07.02.2023 23:26

@MichaëlRoy, спасибо, что сказал мне это. Я этого не знал. У меня были проблемы с операциями типа str1 = str2 + str3, стек переполняется, потому что крошечный микроконтроллер не может копировать множество экземпляров больших строк, мне пришлось увеличить размер стека pthread по умолчанию. Вместо этого я использую метод добавления, как предложено в ответе. Также я мог бы изменить его на vector<char>, потому что данные ответа http могут содержать двоичные данные, и для меня это будет проще с точки зрения удобочитаемости и понимания.

Rajat Mondal 08.02.2023 10:48

@RajatMondal Обычно рекомендуется обрабатывать данные, поскольку они передаются на небольшие устройства. Нехватка памяти на встроенном устройстве никогда не должна быть в списке возможных результатов.

Michaël Roy 08.02.2023 14:07

@MichaëlRoy, весь проект посвящен автоматической системе входа в портал для моего универмага, для которой требуется некоторая нетривиальная последовательность запросов GET / POST и вкладка браузера с поддержкой активности, которая повторно отправляет запрос GET после периода ожидания. Я знаю, что ответ может быть не более 10 КБ. Более сложная работа заключается в поиске регулярных выражений некоторых пар ключ-значение в данных размером 10 КБ, которые должны быть отправлены в запросе POST, который я едва заставил работать с регулярным выражением в стиле C (регулярное выражение C++ требует слишком много памяти стека)

Rajat Mondal 08.02.2023 23:53

@MichaëlRoy У меня есть и другие проекты, поэтому я оставляю варианты открытыми для большей гибкости.

Rajat Mondal 08.02.2023 23:54

@RajatMondal Моя точка зрения именно такая: сохранение открытых параметров никогда не должно приравниваться к потенциальному сбою из-за нехватки памяти. Сбойное устройство — это неработающее устройство.

Michaël Roy 09.02.2023 00:05

@MichaëlRoy да. Я согласен. Тогда у меня есть два варианта: либо обрабатывать буфер ответов в отдельном потоке одновременно с его заполнением (возможно, с использованием очереди и условной переменной), но это сделало бы код бесконечно более сложным, либо отклонять полученные данные после была переведена определенная сумма (что может сработать в некоторых случаях). В любом случае, я не могу пытаться обрабатывать данные в обработчике событий, это было бы более подвержено сбоям (по опыту). Кроме того, мне нравится, как это работает сейчас.

Rajat Mondal 09.02.2023 00:57
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
16
107
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы уже указали ответ в заголовке вашего вопроса. Вы можете безопасно привести указатель std::vector* к указателю void*, а затем вернуть его обратно к указателю std::vector*.

Вы можете использовать оператор &, чтобы получить адрес vector, например:

vector<char> local_response_buffer;

esp_http_client_config_t config = {
    ...
    .user_data = &local_response_buffer
};

...

esp_err_t http_event_handler(esp_http_client_event_t *evt)
{
    ...
    vector<char> *buffer = static_cast<vector<char>*>(evt->user_data);
    char *data = static_cast<char*>(evt->data);
    buffer->insert(buffer->end(), data, data + evt->data_len);
    ...
}

В качестве альтернативы вы можете использовать std::string вместо std::vector, например:

string local_response_buffer;

esp_http_client_config_t config = {
    ...
    .user_data = &local_response_buffer
};

...

esp_err_t http_event_handler(esp_http_client_event_t *evt)
{
    ...
    string *buffer = static_cast<string*>(evt->user_data);
    buffer->append(static_cast<char*>(evt->data), evt->data_len);
    ...
}

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

Rajat Mondal 07.02.2023 21:22

Другие вопросы по теме