convert std::array of bytes to hex std::string - c++

I'd like a way to take an arbitrary-size array of bytes and return a hex string. Specifically for logging packets sent over the net, but I use the equivalent function that takes a std::vector a lot. Something like this, probably a template?
std::string hex_str(const std::array<uint8_t,???> array);
I've searched but the solutions all say "treat it as a C-style array" and I am specifically asking whether there's a way not to do that. I assume the reason this isn't in every single C++ FAQ is that it's impossible and if so can someone outline why?
I already have these overloads and the second one can be used for std::array by decaying into a C-style array, so please don't tell me how to do that.
std::string hex_str(const std::vector<uint8_t> &data);
std::string hex_str(const uint8_t *data, const size_t size);
(edit: vector is a reference in my code)

You should consider writing the function to work with iterators, like standard algorithms do. Then you can use it with both std::vector and std::array inputs, eg:
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();
}
Online Demo
If you really want to avoid having to call begin()/end() on whatever container you pass in, you can define a helper to handle that for you, eg:
template<typename C>
std::string hex_str(const C &data) {
return hex_str(data.begin(), data.end());
}
Online Demo
Or, if you really wanted to, you can just flatten this all down into a single function, eg:
template <typename C>
std::string hex_str(const C& data)
{
std::ostringstream output;
output << std::hex << std::setw(2) << std::setfill('0');
for(const auto &elem : data)
output << static_cast<unsigned>(elem);
return output.str();
}
Online Demo

If you know the size of the std::array at compile time you can use a non type template parameter.
template<std::size_t N>
std::string hex_str( const std::array<std::uint8_t, N>& buffer )
{ /* Implementation */ }
int main( )
{
// Usage.
std::array<std::uint8_t, 5> bytes = { 1, 2, 3, 4, 5 };
const auto value{ hex_str( bytes ) };
}
Or you can just template the entire container (cut down on your overloads).
template<typename Container>
std::string hex_str( const Container& buffer )
{ /* Implementaion */ }
int main( )
{
// Usage.
std::array<std::uint8_t, 5> bytes = { 1, 2, 3, 4, 5 };
const auto value{ hex_str( bytes ) };
}

Related

iostream equivalent to snprintf(NULL, 0, format_string, args...)

I want to find the number of characters that a stream formatting operation would produce without allocating memory from the heap. In C, it can be done with
int nchars = snprintf(NULL, 0, format_string, args...);
How can it be done within the ostream framework in C++?
An implementation with std::ostringstream may allocate memory from the heap:
template <class T>
int find_nchar(const T& value) {
std::ostringstream os; // may allocate memory from the heap
os << value;
return os.str().size(); // may allocate memory from the heap
}
I think I need to make a custom ostream class to achieve this. The custom ostream should respect all the formatting flags one can set for the normal std::ostream.
I am searching for a solution that only uses the C++ standard library, not boost::iostreams, for example.
Rather than a custom std::ostream it might be easier -- and perhaps more flexible -- to implement a custom std::streambuf that can then be used with any std::ostream.
#include <streambuf>
template <class CharT, class Traits = std::char_traits<CharT>>
struct counting_streambuf: std::basic_streambuf<CharT, Traits> {
using base_t = std::basic_streambuf<CharT, Traits>;
using typename base_t::char_type;
using typename base_t::int_type;
std::streamsize count = 0;
std::streamsize xsputn(const char_type* /* unused */, std::streamsize n)
override
{
count += n;
return n;
}
int_type overflow(int_type ch)
override
{
++count;
return ch;
}
};
Then use as...
#include <iostream>
int
main (int argc, char **argv)
{
using char_type = decltype(std::cout)::char_type;
counting_streambuf<char_type> csb;
/*
* Associate the counting_streambuf with std::cout whilst
* retaining a pointer to the original std::streambuf.
*/
auto *oldbuf = std::cout.rdbuf(&csb);
std::cout << "Some text goes here...\n";
/*
* Restore the original std::streambuf.
*/
std::cout.rdbuf(oldbuf);
std::cout << "output length is " << csb.count << " characters\n";
}
Running the above results in...
output length is 23 characters
Edit: The original solution didn't overload overflow. This works on Linux but not on Windows. Thanks go to Peter Dimov from Boost, who found the solution.

Reading data with different types in C++

I want to a program to read strings like:
integer_value 1
double_value 1.0
string_value one
I implement the following functions in order to read these:
void read_val_int(
std::vector<std::string> str_vec,
std::string str,
int& val){
if(str_vec[0]==str) val= std::stoi(str_vec[1]);
}
void read_val_dbl(
std::vector<std::string> str_vec,
std::string str,
double& val){
if(str_vec[0]==str) val= std::stoi(str_vec[1]);
}
void read_val_str(
std::vector<std::string> str_vec,
std::string str,
std::string& val){
if(str_vec[0]==str) val= str_vec[1];
}
str_vec is a vector containing two string values, e.g. {"integer_value","1"}.
str contains a string I want to compare with str_vec[0]
val is an integer, double or string that corresponds to str_vec[1] in case str_vec[0]==str is true.
I use these functions as, e.g. read_val_int(my_str_vec,"integer_value",my_int_val).
My question is: Is there a way of using one single function in order to do this? I have tried using a template but since I need to reference val this seems impossible.
Note: I'm aware of this post but it is in C and seems kinda messy to me. Maybe there is a simpler way to achieve this in C++.
If you are before C++17 and so cannot use std::variant, you can use only one function by using templates.
You declare the function as follows:
template <typename T>
void read_val(const std::string & data, T & val);
Then you specialize it for your three types:
template <>
void read_val<int>(const std::string & data, int & val)
{
val = std::stoi(data);
}
template <>
void read_val<double>(const std::string & data, double & val)
{
val = std::stod(data);
}
template <>
void read_val<std::string>(const std::string & data, std::string & val)
{
val = data;
}
And the job is done, you can use the function for you three types by calling one and only one function: read_val().
You can use it as follows:
std::string data_int("5");
std::string data_double("2.5");
std::string data_string("Hello");
int int_val;
double double_val;
std::string string_val;
read_val(data_int, int_val);
read_val(data_double, double_val);
read_val(data_string, string_val);
std::cout << int_val << std::endl;
std::cout << double_val << std::endl;
std::cout << string_val << std::endl;
As you can see, by the use of template specialization, you can use the same function for different types.
Moreover, it will automatically assure you that an allowed type is passed. Indeed, if you give something else than an int, double or std::string to the function, the compilation will fail because there is no specialization for it.
I hope it helps.
As suggested in Dave's comment, you should check the type of your variable parsing the first element of the vector.
Inside the if-else chain you can what you need with the right type of your variable.
You could also have a single function to return your values using std::variant e then printing values (or do whatever you need) using c++17 std::visit.
It could be something like this:
#include <vector>
#include <string>
#include <variant>
#include <iostream>
using my_variant = std::variant<int, double, std::string>;
my_variant read_val(
const std::vector<std::string> &str_vec)
{
if(str_vec[0]=="integer_value")
{
return std::stoi(str_vec[1]);
}
else if(str_vec[0]=="double_value")
{
return std::stod(str_vec[1]);
}
else if(str_vec[0]=="string_value")
{
return str_vec[1];
}
//notify error in some way, maybe throw
}
void print_variant(const my_variant &v)
{
std::visit([](my_variant &&var)
{
if (std::holds_alternative<int>(var))
std::cout<<"int->"<<std::get<int>(var)<<"\n";
else if (std::holds_alternative<double>(var))
std::cout<<"double->"<<std::get<double>(var)<<"\n";
else if (std::holds_alternative<std::string>(var))
std::cout<<"string->"<<std::get<std::string>(var)<<"\n";
}, v);
}
int main()
{
std::vector<std::string> vec_int {"integer_value", "1"};
std::vector<std::string> vec_dbl {"double_value", "1.5"};
std::vector<std::string> vec_str {"string_value", "str"};
print_variant(read_val(vec_int));
print_variant(read_val(vec_dbl));
print_variant(read_val(vec_str));
return 0;
}

Append structs to a vector<uint8_t>

I'm writing a message comuniction lib, and need to read some data to a struct, and append this struct to a vector, and read again, append again.
If in C language, memcpy works perfect, but I want is to make all code in C++11 code style.
I tried to use std::copy, but it needs a begin and end interator, so how exactly I can use std::copy like std::copy(&a, &a + sizeof(A), back_inserter(buffer));?
You can do this:
struct MyStruct {
int a;
double b;
int c;
};
std::vector<uint8_t> buffer;
MyStruct data { 42, 3.14159, 123 };
uint8_t* ptr = reinterpret_cast<uint8_t*>(&data);
std::copy(ptr, ptr + sizeof(data), back_inserter(buffer));
Note that std::copy in this case just reverts to std::memcpy underneath, and reinterpret_cast throws away all type safety of the language. Alexander's suggestion of using a static_assert is a good one.
EDIT:
Mário is right, back_inserter would cause std::copy to not be equivalent to std::memcpy. An alternative could be to reallocate your buffer first, then copy:
size_t len = buffer.size();
buffer.resize(len+sizeof(data));
std::copy(ptr, ptr + sizeof(data), buffer.data() + len);
(or something to that extent).
Here is a clean C++ way to do it:
First a simple range type:
template<class It>
struct range_t {
It b, e;
It begin() const { return b; }
It end() const { return e; }
std::size_t size() const { return end()-begin(); }
};
template<class It>
range_t<It> range(It s, It f) { return {s,f}; }
it represents a range of some iterators.
Next, some functions to treat pod data as bytes:
template<class T>
range_t< unsigned char* > as_bytes( T* t ) {
static_assert( std::is_trivially_copyable<T>::value, "bad idea if not trivially copyable" );
auto* ptr = reinterpret_cast<unsigned char*>(t);
return range(ptr, ptr+sizeof(T));
}
template<class T>
range_t< unsigned char const* > as_bytes( T const* t ) {
static_assert( std::is_trivially_copyable<T>::value, "bad idea if not trivially copyable" );
auto* ptr = reinterpret_cast<unsigned char const*>(t);
return range(ptr, ptr+sizeof(T));
}
both read and write versions.
Next, functions that take a structure and stuff them into a vector, or pop them out:
template<class T>
void push_bytes_in( std::vector<std::uint8_t>& target, T const* data ) {
auto bytes = as_bytes(data);
target.insert( target.end(), bytes.begin(), bytes.end() );
}
template<class T>
bool pop_bytes_out( std::vector<std::uint8_t>& src, T* data ) {
auto bytes = as_bytes(data);
if (bytes.size() > src.size()) return false;
std::copy( src.end()-bytes.size(), src.end(), bytes.begin() );
src.resize( src.size()-bytes.size() );
return true;
}
Finally, test code:
struct some_data {
int x, y;
char buff[1024];
};
std::vector<std::uint8_t> bytes;
some_data data{1,2, "hello"};
push_bytes_in( bytes, &data );
some_data d2;
if (!pop_bytes_out( bytes, &d2)) {
std::cout << "failed\n";
return -1;
}
std::cout << d2.buff << "\n";
Live example.
We could optimize push bytes if they turn out to be too slow to pre-size the buffer, then shove the bytes in using std copy or memcpy. However, you should be careful to ensure exponential data reservation in that case.
template<class T>
void push_bytes_in( std::vector<std::uint8_t>& target, T const* data ) {
if (target.capacity() < target.size()+sizeof(T)) {
target.reserve( target.capacity()*3/2 +1 );
}
auto bytes = as_bytes(data);
target.resize( target.size() + sizeof(T) );
std::copy( bytes.begin(), bytes.end(), target.end()-sizeof(T) );
}
that may turn out to be a bit faster.
You can use vector insert member function.
This is better than copy since vector insert knows how to allocate memory(you do not need to use ugly back_inserter).
void append(std::vector<unsigned char>& v, const MyStruct& s){
v.insert(v.end(), (unsigned char*) &s, ((unsigned char*)&s)+sizeof s);
}
full code here
note that this is very simple code compared to Yakk answer but I think it may be easier for some people to read code without templates. Also I use C style cast that some people think should not be done in C++ but I find reinterpret cast too verbose for this use case.

Pass unknown type and quantity of args and concat to char array

Is there a way to pass unknown quantity of args which can be either char strings or integers into a function and then concatenate them to a char array buffer?
For example, to be able to call all the following functions:
bufcat("this", 1, 3, "that");
// buffer = "this13that"
bufcat(1, "this", "that", 3, 4000000, "other");
// buffer = "1thisthat34000000other"
bufcat(1000000,2,3,4,5,6,7,8,9,10,11,12,13,"onemillionandfiftytwo");
// buffer = "10000002345678910111213onemillionandfiftytwo"
You can use variadic template plus a string stream:
template<typename... Args>
std::string bufcat(Args&&... args) {
std::stringstream ss;
auto iteration = [&ss](auto&& item) { ss << std::forward<decltype(item)>(item); };
(void)std::initializer_list<int> {(
iteration(std::forward<Args>(args))
, 0)..., 0};
return ss.str();
}
This will concat anything that you pass in parameters into the string stream. It will call the iteration lambda for each arguments in Args.
Then, you can simply call your function like this:
bufcat(1000000,2,3,4,5,6,7,8,9,10,11,12,13,"onemillionandfiftytwo");
And it will yield 10000002345678910111213onemillionandfiftytwo
A simple solution is possible in c++11 using variadic templates.
If performance matters the boilerplate code needed for the classic printf idiom might be more acceptable than the memory allocations used here.
#include <string>
#include <iostream>
inline std::string bufcat() { return ""; }
template<typename value_type> std::string bufcat(const value_type &value) { return std::to_string(value); }
template<> std::string bufcat(const bool &b) { return b ? "true" : "false"; }
std::string bufcat(const std::string &str) { return str; }
std::string bufcat(const char *str) { return str; }
template <typename arg0_type, typename ...arg_types>
std::string bufcat(arg0_type arg0, arg_types ... args)
{ return bufcat(arg0).append(bufcat(args...)); }
int main()
{
std::cout << bufcat(1000000,2,3,4,5,6,7,8,9,10,11,12,13,"onemillionandfiftytwo") << "\n";
}

std::string not working with std::set

I'm doing a programming question from C++ Primer Plus which asks me to make a template
function that returns the number of unique elements in an array. I don't understand why
line 13 causes an error while compiling as to my knowledge, a std::string behaves like an array.
This is my code:
#include <iostream>
#include <set>
template <typename T>
int reduce(T ar[], int n);
int main()
{
long test[] = {1, 2, 1, 3, 3, 4, 1};
std::string testStr = "testing";
std::cout << reduce(test, 6) << std::endl;
std::cout << reduce(testStr, 7) << std::endl;
std::cin.get();
return 0;
}
template <typename T>
int reduce(T ar[], int n)
{
std::set<T> test;
for(int i = 0; i < n; i++)
{
test.insert(ar[i]);
}
return test.size();
}
Following up my immediate response that std::string is not an array, this is the way a C++ person might accomplish the task you're looking for.
#include <iterator>
#include <iostream>
#include <set>
// instead of taking an array and length, just take where you want to start and where
// you want to stop.
template <typename TForwardIterator>
int reduce(TForwardIterator iter, TForwardIterator end)
{
// This is hideous syntax to get the type of object the iterator is describing.
// For std::string, it is char...for T*, it is T.
// I apologize for C++, I'm not sure there is a better way to do this.
typedef typename std::iterator_traits<TForwardIterator>::value_type value_type;
std::set<value_type> set;
// instead of forcing the objects to be an array type, use iterators!
for (; iter != end; ++iter)
set.insert(*iter);
return set.size();
}
int main()
{
long test[] = {1, 2, 1, 3, 3, 4, 1};
std::string testStr = "testing";
// begin() and end() are iterators you'll find on all the container types
std::cout << reduce(testStr.begin(), testStr.end()) << std::endl;
// pointers are iterators, too!
std::cout << reduce(test, test + 7) << std::endl;
return 0;
}
The answer is quite simple: std::string is not an array.
It behaves like an array so far as you can access the elements using the [] operator, but it is simply not the same data type as char[]. As a matter of fact the standard doesn't even guarantee that it's stored like an array (meaning continously). T[] will only match to array of, not objects which can be used arraylike.
In order to solve this you have several options
you can call reduce(teststr.c_str(), 7), since c_str() will return an chararray with the contents of the string.
You could rewrite reduce as template <typename T, typename U> int reduce(U ar, int n) and call it as reduce<long>(test, 6) and reduce<char>(testStr, 7). The second template parameter is necessary, since there is no unified way to get from the container to the element (except in c++0x/using compiler extensions).
If you are using c++0x you can use decltype to get from a container to the contained element: template <typename T>int reduce(T ar, int n) and std::set<decltype(ar[0])> test; (rest of the code remains unchanged, and somehow I seem to have trouble with code block sections so just these two lines here.
Of course in c++ one would typically write such a function in terms of iterators (see Travis Gockels answer), since that's simply a more flexible and better supported way.
You may be confusing std::strings with built-in character arrays. std::strings are not arrays, though they behave similarly to arrays (the class has an overloaded [] operator) and contain arrays (which you can access through c_str()).
If you replace line 10 with
char testStr[] = "testing";
Your program will compile and run.
Or, you could try something like:
#include <iostream>
#include <set>
template <typename T>
int reduce(const T* ar, int n);
int main()
{
long test[] = {1, 2, 1, 3, 3, 4, 1};
std::string testStr = "testing";
std::cout << reduce(test, 7) << std::endl;
std::cout << reduce(testStr.c_str(), testStr.size()) << std::endl;
std::cin.get();
return 0;
}
template <typename T>
int reduce (const T* ar, int n)
{
std::set<T> test;
for(int i = 0; i < n; i++)
{
test.insert(ar[i]);
}
return test.size();
}