I think that any canonical answer (wrt reward notes) will include some distinct phases in the solution:
- Validation of input
- Length check and
- Checking Data Content
- Element conversion
- Output creation
Given the usefulness of such conversions, the solution should probably include some flexibility wrt types used and language required.
From the very beginning, given the date of the request for a βmore canonical answerβ (around August 2014), the liberal use of C ++ 11 will be applied.
An annotated version of the code with types matching OP:
std::vector<std::uint8_t> convert(std::string const& src) { // error check on the length if ((src.length() % 2) != 0) { throw std::invalid_argument("conversion error: input is not even length"); } auto ishex = [] (decltype(*src.begin()) c) { return std::isxdigit(c, std::locale()); }; // error check on the data contents if (!std::all_of(std::begin(src), std::end(src), ishex)) { throw std::invalid_argument("conversion error: input values are not not all xdigits"); } // allocate the result, initialised to 0 and size it to the correct length std::vector<std::uint8_t> result(src.length() / 2, 0); // run the actual conversion auto str = src.begin(); // track the location in the string std::for_each(result.begin(), result.end(), [&str](decltype(*result.begin())& element) { element = static_cast<std::uint8_t>(std::stoul(std::string(str, str + 2), nullptr, 16)); std::advance(str, 2); // next two elements }); return result; }
The boilerplate version of the code adds flexibility;
template <typename Int , typename Char = char, typename Traits = std::char_traits<Char>, typename Allocate = std::allocator<Char>, typename Locale = std::locale> std::vector<Int> basic_convert(std::basic_string<Char, Traits, Allocate> const& src, Locale locale = Locale()) { using string_type = std::basic_string<Char, Traits, Allocate>; auto ishex = [&locale] (decltype(*src.begin()) c) { return std::isxdigit(c, locale); }; if ((src.length() % 2) != 0) { throw std::invalid_argument("conversion error: input is not even length"); } if (!std::all_of(std::begin(src), std::end(src), ishex)) { throw std::invalid_argument("conversion error: input values are not not all xdigits"); } std::vector<Int> result(src.length() / 2, 0); auto str = std::begin(src); std::for_each(std::begin(result), std::end(result), [&str](decltype(*std::begin(result))& element) { element = static_cast<Int>(std::stoul(string_type(str, str + 2), nullptr, 16)); std::advance(str, 2); }); return result; }
Then the convert()
function can be based on basic_convert()
as follows:
std::vector<std::uint8_t> convert(std::string const& src) { return basic_convert<std::uint8_t>(src, std::locale()); }
Live sample .