Я использую esp-idf для выполнения некоторых http-запросов и хочу использовать c++, потому что мне так удобнее. esp_http_client работает примерно так:
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
};
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[] для удобного динамического размещения. Как лучше всего это сделать?
Почему вектор? Почему не std::string?
что вы уже пробовали? Знаете ли вы, что динамическое размещение может быть источником серьезных ошибок в микроконтроллерах?
@MichaëlRoy Хорошо, это сработало. используя reinterpret_cast() и используя оператор += с std::string и char*. Но у меня есть вопрос, += более эффективен, чем выполнение str1=str1+str2? Я имею в виду, копирует ли он строку много раз? Если это так, то это может привести к сбою для моего варианта использования, стеки задач очень маленькие.
@Кристиан Б. Да, я знаю несколько примеров, я могу использовать std::array, если увижу какие-либо проблемы, но основная проблема заключается в приведении типов. По какой-то причине он не работает для std::vector, но работает для std::string.
Каков срок службы std::vector
? Просто помните, что независимо от того, передаете ли вы указатель на std::vector
или указатель на первый байт его содержимого, вектор должен оставаться в живых, пока вы не закончите использовать его/его содержимое. Как только вектор умирает, он освобождает принадлежащий ему буфер.
@BenVoigt Да, я понимаю. Я создаю только один векторный/строковый объект, который находится в той же области, в которой я его использую, после того, как он был заполнен обработчиком событий, и я передаю ссылку на него, а не на первый его элемент. Настоящая проблема оказалась непроблемой. по какой-то причине intellisense показал мне, что есть unquilified-id, хотя его не было, и мне было интересно, почему это не сработает. Ошибка исчезла после того, как я ее скомпилировал.
@RajatMondal «По какой-то причине это не работает для std::vector, но работает для std::string» - тогда вы должны были опубликовать фактический код, с которым у вас возникли проблемы. Кастинг должен работать точно так же, независимо от того, какой контейнер вы решите использовать.
@RemyLebeau, это была ошибка IDE, извините.
@RajatMondal std::string::operator+=() и std::string::operator+() в большинстве случаев одинаковы с точки зрения эффективности, особенно если дополнение к исходной строке велико. Если вы не планируете обрабатывать несколько сотен http-запросов в секунду, вам не следует ожидать каких-либо измеримых преимуществ от использования +=.
@MichaëlRoy, спасибо, что сказал мне это. Я этого не знал. У меня были проблемы с операциями типа str1 = str2 + str3, стек переполняется, потому что крошечный микроконтроллер не может копировать множество экземпляров больших строк, мне пришлось увеличить размер стека pthread по умолчанию. Вместо этого я использую метод добавления, как предложено в ответе. Также я мог бы изменить его на vector<char>, потому что данные ответа http могут содержать двоичные данные, и для меня это будет проще с точки зрения удобочитаемости и понимания.
@RajatMondal Обычно рекомендуется обрабатывать данные, поскольку они передаются на небольшие устройства. Нехватка памяти на встроенном устройстве никогда не должна быть в списке возможных результатов.
@MichaëlRoy, весь проект посвящен автоматической системе входа в портал для моего универмага, для которой требуется некоторая нетривиальная последовательность запросов GET / POST и вкладка браузера с поддержкой активности, которая повторно отправляет запрос GET после периода ожидания. Я знаю, что ответ может быть не более 10 КБ. Более сложная работа заключается в поиске регулярных выражений некоторых пар ключ-значение в данных размером 10 КБ, которые должны быть отправлены в запросе POST, который я едва заставил работать с регулярным выражением в стиле C (регулярное выражение C++ требует слишком много памяти стека)
@MichaëlRoy У меня есть и другие проекты, поэтому я оставляю варианты открытыми для большей гибкости.
@RajatMondal Моя точка зрения именно такая: сохранение открытых параметров никогда не должно приравниваться к потенциальному сбою из-за нехватки памяти. Сбойное устройство — это неработающее устройство.
@MichaëlRoy да. Я согласен. Тогда у меня есть два варианта: либо обрабатывать буфер ответов в отдельном потоке одновременно с его заполнением (возможно, с использованием очереди и условной переменной), но это сделало бы код бесконечно более сложным, либо отклонять полученные данные после была переведена определенная сумма (что может сработать в некоторых случаях). В любом случае, я не могу пытаться обрабатывать данные в обработчике событий, это было бы более подвержено сбоям (по опыту). Кроме того, мне нравится, как это работает сейчас.
Вы уже указали ответ в заголовке вашего вопроса. Вы можете безопасно привести указатель 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. Но теперь он компилируется и работает так, как задумано.
Вы можете использовать
static_cast()
: преобразование любого указателя в указатель на void и обратно в указатель на исходный (или более квалифицированный cv) тип сохраняет исходное значение. Но я бы также использовал класс сstd::vector
в качестве члена, а не только вектор сам по себе.