Home > OS >  Using template function for accessing the raw bytes of POD data type and strings
Using template function for accessing the raw bytes of POD data type and strings

Time:11-20

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");
}

Demo.

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"
}

  • Related