I'm trying to make a function template that lets me process the raw bytes of a POD data type and strings.
I came up with this somewhat naive approach (I'm not using template a lot, and I'm able to write them use them only for very simple cases):
#include <cstdio>
template<typename T>
void PrintBytes(T object)
{
unsigned char* p = reinterpret_cast<unsigned char*>(&object);
for (int i = 0; i < sizeof object; i )
{
printf("x ", *p );
}
printf("\n");
}
int main()
{
int a = 10;
PrintBytes(a); // works as expected
short b = 12;
PrintBytes(b); // works as expected
const char* foo = "ABC";
PrintBytes(foo); // works as expected, but doesn't do what I want
const char bar[] = "ABC";
PrintBytes(bar); // works as expected, but doesn't do what I want
}
Output
0a 00 00 00
0c 00
a4 ac 5b 8c f6 7f 00 00
a4 fb 7f 07 b7 00 00 00
Desired output for the last two cases
41 42 43
41 42 43
Obviously the first two cases work and the last two cases don't because foo
and bar
are already pointers and the unsigned char* p = reinterpret_cast<unsigned char*>(&object)
should rather be unsigned char* p = object
and the sizeof object
should be strlen(object)
.
How can this be done with C templates? I can use c 17.
CodePudding user response:
You can use C 17 if constexpr
to perform the corresponding address and size method according to the type of T
, something like this:
#include <cstdio>
#include <cstring>
#include <utility>
#include <type_traits>
template<typename T>
void PrintBytes(T object)
{
auto [p, size] = [&] {
if constexpr (std::is_same_v<T, const char*>)
return std::pair{object, strlen(object)};
else
return std::pair{reinterpret_cast<unsigned char*>(&object), sizeof(object)};
}();
for (int i = 0; i < size; i )
{
printf("x ", *p );
}
printf("\n");
}
CodePudding user response:
Let's expand what the compiler does when calling PrintBytes with a character array or pointer.
You call:
const char* foo = "ABC";
PrintBytes(foo); // works as expected, but doesn't do what I want
The compiler must then instantiate the function PrintBytes for the given argument, a const char*, which gives:
void PrintBytes(const char* object)
{
unsigned char* p = reinterpret_cast<unsigned char*>(&object);
for (int i = 0; i < sizeof (object); i ) // sizeof(const char*) is 8, for a 64 biut app.
{
printf("x ", *p ); // this will print the value of object, NOT what it points to.
}
printf("\n");
}
To force PrintBytes(const char*) to print the cntexnts of a string, and not its address, you should create a type specific override for strings, as in:
void PrintBytes(const char* sz)
{
// you may want to check for sz itself being NULL before proceding
if (sz)
{
do
{
printf("x ", *sz); // this will print the value of object, NOT its address
} while (*sz != 0);
}
printf("\n");
}
CodePudding user response:
I finally came up with this:
#include <cstdio>
#include <cstring>
void PrintBytes(const unsigned char *p, size_t len)
{
for (int i = 0; i < len; i )
{
printf("x ", *p );
}
printf("\n");
}
template<typename T>
void PrintBytes(T object)
{
const unsigned char* p = reinterpret_cast<const unsigned char*>(&object);
PrintBytes(p, sizeof object);
}
template<>
void PrintBytes(const char *object)
{
auto len = strlen(object);
const unsigned char* p = reinterpret_cast<const unsigned char*>(object);
PrintBytes(p, len);
}
template<>
void PrintBytes(char* object)
{
auto len = strlen(object);
const char* p = reinterpret_cast<const char*>(object);
PrintBytes(p);
}
int main()
{
int a = 10;
PrintBytes(a); // works as expected
short b = 12;
PrintBytes(b); // works as expected
float f = 1.2f;
PrintBytes(f); // works as expected
const char* foo = "ABC"; // works as expected
PrintBytes(foo);
char bar[] = "ABC"; // works as expected
PrintBytes(bar);
}
CodePudding user response:
#include <cstdio>
template<typename T>
void PrintBytes(T& object)
{
const unsigned char* p = reinterpret_cast<const unsigned char*>(&object);
for (int i = 0; i < sizeof object; i )
{
printf("x ", p[i]);
}
printf("\n");
}
int main()
{
int a = 10;
PrintBytes(a); // works as expected
short b = 12;
PrintBytes(b); // works as expected
const char* foo = "ABC";
PrintBytes(foo);
// Print the content of the pointer foo, which is an address;
// I think this work as expected as the "object" is just a pointer;
// if you want to print the content of the data it points to instead,
// you need another implementation logic of this "PrintBytes",
// which would be an function overload, like some other answers show.
const char bar[] = "ABC";
PrintBytes(bar); // print "41 42 43 00"
}