【问题标题】:Using template function for accessing the raw bytes of POD data type and strings使用模板函数访问 POD 数据类型和字符串的原始字节
【发布时间】:2021-12-30 09:17:32
【问题描述】:

我正在尝试制作一个函数模板,让我可以处理 POD 数据类型 字符串的原始字节。

我想出了这个有点幼稚的方法(我没有经常使用模板,而且我能够编写它们仅用于非常简单的情况):

#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("%02x ", *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
}

输出

0a 00 00 00
0c 00
a4 ac 5b 8c f6 7f 00 00
a4 fb 7f 07 b7 00 00 00

最后两种情况的期望输出

41 42 43
41 42 43

显然,前两种情况有效,而后两种情况无效,因为foobar 已经是指针,而unsigned char* p = reinterpret_cast&lt;unsigned char*&gt;(&amp;object) 应该是unsigned char* p = objectsizeof object 应该是strlen(object)

如何使用 C++ 模板来做到这一点?我可以使用 c++17。

【问题讨论】:

    标签: c++ templates


    【解决方案1】:

    你可以使用C++17的if constexpr根据T的类型来执行对应的地址和大小的方法,大概是这样:

    #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("%02x ", *p++);
      }
      printf("\n");
    }
    

    Demo.

    【讨论】:

    • 这真的很优雅。您应该提到#include &lt;utility&gt; 是必需的。 OTOH,我不确定是否需要 #include &lt;type_traits&gt;
    • 标准不保证&lt;utility&gt;包含&lt;type_traits&gt;,所以#include &lt;type_traits&gt;是必须的。
    【解决方案2】:

    让我们扩展编译器在使用字符数组或指针调用 PrintBytes 时的作用。

    你打电话:

    const char* foo = "ABC";
    PrintBytes(foo);  // works as expected, but doesn't do what I want
    

    然后编译器必须为给定的参数实例化函数 PrintBytes,一个 const char*,它给出:

    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("%02x ", *p++); // this will print the value of object, NOT what it points to.
      }
    
      printf("\n");
    }
    

    要强制 PrintBytes(const char*) 打印字符串的 cntexnts,而不是其地址,您应该为字符串创建特定类型的覆盖,如下所示:

    void PrintBytes(const char* sz)
    {
      // you may want to check for sz itself being NULL before proceding
      if (sz)
      {
        do
        {
          printf("%02x ", *sz); // this will print the value of object, NOT its address
        } while (*sz++ != 0);
      }
    
      printf("\n");
    }
    

    【讨论】:

      【解决方案3】:

      我终于想到了这个:

      #include <cstdio>
      #include <cstring>
      
      void PrintBytes(const unsigned char *p, size_t len)
      {
        for (int i = 0; i < len; i++)
        {
          printf("%02x ", *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);
      }
      

      【讨论】:

        【解决方案4】:
        #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("%02x ", 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"
        }
        
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2010-09-15
          • 2021-11-06
          • 2020-07-20
          • 1970-01-01
          • 2016-10-24
          • 2021-05-18
          • 1970-01-01
          相关资源
          最近更新 更多