c
#c
Вопрос:
Я хотел бы найти способ взять массив байтов произвольного размера и вернуть шестнадцатеричную строку. Специально для протоколирования пакетов, отправляемых по сети, но я использую эквивалентную функцию, которая часто использует std::vector . Что-то вроде этого, возможно, шаблон?
std::string hex_str(const std::array<uint8_t,
??? > array);
Я искал, но все решения говорят: «рассматривайте это как массив в стиле C», и я специально спрашиваю, есть ли способ не делать этого. Я предполагаю, что причина, по которой это не в каждом FAQ по C , заключается в том, что это невозможно, и если да, может кто-нибудь объяснить, почему?
У меня уже есть эти перегрузки, а вторую можно использовать для std::array, разложив в массив в стиле C, поэтому, пожалуйста, не говорите мне, как это сделать.
std::string hex_str(const std::vector<uint8_t> amp;data);
std::string hex_str(const uint8_t *data, const size_t size);
(редактировать: вектор — это ссылка в моем коде)
Комментарии:
1. Что вы имеете в виду шестнадцатеричную строку ? Кодировка Base64 или что?
2. Кажется, вам нужен шаблон?
template <std::size_t N> std::string hex_str(const std::array<uint8_t, N> array);
3.
std::array
Известны ли размеры во время компиляции? Если нет, то нет, размер должен быть известен во время компиляции. Что, если вместо этого вы взяли итераторы? Таким образом, кто-то может использовать любой контейнер, который он захочет.4. Почему не один шаблон, который принимает оба
std::vector
иstd::array
? Или, что еще лучше, диапазон итераторов? Весь смысл стандартных контейнеров, имеющих согласованный интерфейс, предназначен для подобных случаев, поэтому вы можете написать единый алгоритм, который одинаково обрабатывает несколько типов контейнеров5. @Yksisarvinen да. Но я изо всех сил пытался написать то, что, как я полагаю, является довольно тривиальным кодом, чтобы на самом деле это сделать. Я никогда раньше не писал шаблон
Ответ №1:
Вам следует подумать о написании функции для работы с итераторами, как это делают стандартные алгоритмы. Затем вы можете использовать его с обоими std::vector
std::array
входами и, например:
template<typename Iter>
std::string hex_str(Iter begin, Iter end)
{
std::ostringstream output;
output << std::hex << std::setw(2) << std::setfill('0');
while(begin != end)
output << static_cast<unsigned>(*begin );
return output.str();
}
Если вы действительно хотите избежать необходимости вызывать begin()
/ end()
в любом контейнере, который вы передаете, вы можете определить помощника для обработки этого для вас, например:
template<typename C>
std::string hex_str(const C amp;data) {
return hex_str(data.begin(), data.end());
}
Или, если вы действительно хотите, вы можете просто свести все это к одной функции, например:
template <typename C>
std::string hex_str(const Camp; data)
{
std::ostringstream output;
output << std::hex << std::setw(2) << std::setfill('0');
for(const auto amp;elem : data)
output << static_cast<unsigned>(elem);
return output.str();
}
Комментарии:
1. Могу я просто спросить, и я не хочу показаться грубым, но действительно ли люди пишут код, используя такие потоки по-настоящему? Мне это кажется саркастичным, как будто вы говорите: «и вот почему потоки — плохая идея». Я имею в виду, что, по крайней мере, вам нужно обернуть его в «сохранить состояние потока» … «восстановить состояние потока» (если вы делаете это с помощью cout).
2. @CodeAbominator что не так с тем, как это написано? В этом случае нет состояния, заслуживающего «сохранения и восстановления». Чего мне не хватает? Конечно, манипуляторы в этом случае на самом деле не нужно повторять снова и снова, по крайней мере. Я обновил свой пример относительно этого.
3. по сравнению с printf(«% 02x») он кажется подробным и трудным для понимания. Я подозреваю, что, поскольку я не понимаю, насколько задействована магия компилятора, мне кажется, что «сначала создайте поток. Затем для каждого байта задайте потоку нужные характеристики, затем выведите в него байт. Когда вы закончите, преобразуйте его в строку» многословно и, вероятно, будет медленным, а также подверженным ошибкам. Дело даже не в том, что за несколько часов работы я могу показать, что эта точная деталь работает, а в том, что C , похоже, делает это дюжину раз в день, каждый день.
4. В чем преимущество приведения u8 к общему беззнаковому здесь? Это потому, что собственный размер быстрее или здесь есть какое-то подлинное «вы всегда должны»? Например, ошибка компилятора, если данные являются недопустимым типом для приведения или что-то в этом роде?
5. @CodeAbominator » это кажется подробным и трудным для понимания » — неважно. Вопрос не в том, как использовать потоки, а в том, как реализовать
hex_str()
. Я решил использовать потоки для использованияstd::hex
манипулятора. Используйте любую реализацию, которую вы хотите. «В чем преимущество приведения u8 к общему беззнаковому здесь? » —operator<<
выводит 8-битные значения в виде текстовых символов, будь тоchar
,unsigned char
,(u)int8_t
, и т.д. В данном случае мы этого не хотим, поэтому приведение к int> 8 бит. Я мог бы использоватьuint16_t
, ноunsigned
это более естественный выбор для компилятора
Ответ №2:
Если вы знаете размер std::array
во время компиляции, вы можете использовать параметр шаблона, не относящийся к типу.
template<std::size_t N>
std::string hex_str( const std::array<std::uint8_t, N>amp; buffer )
{ /* Implementation */ }
int main( )
{
// Usage.
std::array<std::uint8_t, 5> bytes = { 1, 2, 3, 4, 5 };
const auto value{ hex_str( bytes ) };
}
Или вы можете просто создать шаблон для всего контейнера (сократить ваши перегрузки).
template<typename Container>
std::string hex_str( const Containeramp; buffer )
{ /* Implementaion */ }
int main( )
{
// Usage.
std::array<std::uint8_t, 5> bytes = { 1, 2, 3, 4, 5 };
const auto value{ hex_str( bytes ) };
}
Комментарии:
1. большое вам спасибо. Это выглядит достаточно тривиально, и я рад, что потратил всего пару часов, пытаясь сделать это, прежде чем спрашивать.
2. @CodeAbominator Я отредактировал ответ, чтобы показать вам, как вы также можете создать шаблон для всего контейнера.
3. Если у вас есть
std::array
, вы можете быть уверены, что у вас есть размер во время компиляции. Я предпочитаю итераторный подход, предложенный Реми выше, но, поскольку это не обсуждается, это то, что я бы сделал.4. И я полагаю, что могу использовать static_assert и ввести материал для самоанализа, чтобы ограничить входные данные вещами, которые действительно действительны.
5. @user4581301 Я упомянул об
iterator
этом подходе в комментариях, но оператору эта идея не понравилась.