Передача шестнадцатеричных значений в виде строки - PullRequest
3 голосов
/ 04 марта 2020

У меня есть функция, которая принимает список значений hexdump, и они передаются в функцию в качестве параметра списка инициализатора и копируются в буфер:

void func(void * buff, std::initializer_list<unsigned char> s) {
  memcpy(buff, s.begin(), s.size());
}

Что я хочу сделать, это передать эти значения как Строка типа "0x04 0x02 0x01", а также. Оба могут быть переданы в виде строки или initializer_list в аргумент функции выше. Я знаю, что для этого нужно создать шаблон. Это то, что я пробовал до сих пор. Я не мог быть в состоянии реализовать как шаблон

void func(void * buff, std::string s) {
    unsigned char arr[s.length() + 1];
    strcpy(arr, s.c_str());
    std::initializer_list<unsigned char> hex_values(arr); 
    memcpy(d, hex_values.begin(), hex_values.size());
  }

1 Ответ

2 голосов
/ 04 марта 2020

Вы можете поместить строку в std::istringstream и сделать форматированный ввод из этого:

#include <algorithm>
#include <iterator>

void func(void* buff, const std::string& s) {
    std::istringstream is(s);
    is.setf(is.hex, is.basefield);    // set the istream in hex mode

    // copy hex values from the istream and store in buff
    std::copy(std::istream_iterator<unsigned>(is), 
              std::istream_iterator<unsigned>{},
              static_cast<unsigned char*>(buff)
    );
}

Полный пример:

#include <algorithm>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <sstream>

void func(void* buff, std::initializer_list<unsigned char> s) {
    std::memcpy(buff, s.begin(), s.size());
}

void func(void* buff, const std::string& s) {
    std::istringstream is(s);
    is.setf(is.hex, is.basefield);    // set the istream in hex mode

    // copy hex values from the istream and store in buff
    std::copy(std::istream_iterator<unsigned>(is), 
              std::istream_iterator<unsigned>{},
              static_cast<unsigned char*>(buff)
    );
}

template <size_t N>
void show(unsigned char(&b)[N]) {
    for(unsigned ch : b) {
        std::cout << std::setw(2) << ch << ' ';
    }
    std::cout << '\n';
}

int main() {
    unsigned char buff[4];
    std::cout << std::hex;

    func(buff, "0x01 0xFF 0x7F 0x80");
    show(buff);
    func(buff, {0x12, 0xF4, 0x1A, 0x17});
    show(buff);
}

Выходные данные

 1 ff 7f 80 
12 f4 1a 17 

Вы также можете добавить более универсальную c версию, принимающую итераторы в качестве входных данных:

template<typename It>
void func(void* buff, It begin, It end) {
    std::copy(begin, end, static_cast<unsigned char*>(buff));
}

С SFINAE, проверяющим, что любой итератор выполняет итерацию, конвертируется в unsigned char, это может выглядеть так:

#include <type_traits>

template<typename It,
    std::enable_if_t<
        std::is_convertible_v<typename std::iterator_traits<It>::value_type,
                              unsigned char>>* = nullptr>
void func(void* buff, It begin, It end) {
    std::copy(begin, end, static_cast<unsigned char*>(buff));
}
...