首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何在C++11中构建具有在编译时递增值的整数成员的结构序列

如何在C++11中构建具有在编译时递增值的整数成员的结构序列
EN

Stack Overflow用户
提问于 2018-06-27 22:06:01
回答 4查看 184关注 0票数 1

在嵌入式环境中工作时,我反复编写代码,从协议层获取字节数组,并将这些字节转换为C++类表示。

表示uint32_t的示例字节数组,后跟uint8_t,再后跟uint16_t,可能如下所示。

代码语言:javascript
复制
std::array<uint8_t, 7> bytes(0x01, 0x02, 0x03, 0x04, 0x10, 0x20, 0x30);

其中0x01020304是我的uin32_t0x10是我的uint8_t0x2030是我的uint16_t

我还有一个变量函数func(),我想用从有效负载中解析出来的值来调用它。

为此,我手动定义了一个中间对象:

代码语言:javascript
复制
// Declaring the Object
struct MY_TYPE
{
   uint32_t val1;
   uint8_t val2;
   uint16_t val3;
} __attribute__((__packed__));

// Processing the Bytes 
auto & object(reinterpret_cast<MY_TYPE *>(&bytes));

func(object.val1, object.val2, object.val3) 

我想要做的是实现一个可变类,这样我就不需要为每个类型组合重新实现MY_TYPE

这是我最初尝试的:

代码语言:javascript
复制
template <typename... Types>
struct GENERIC_CLASS
{
   template <typename ReturnType, std::size_t ArraySize>
   ReturnType getValueFromArray(std::array<uint8_t, ArraySize> const & array, 
                                uint32_t & index); 

   // Note, not valid c++ since the size of the array (N) isn't 
   // specified. This has been omitted for simplicity. 
   void process(std::array<uin8_t, N> const & array)
   {
      auto currentIndex(u0);

      // Assumes this class has a specialization 
      // for getValueFromArray for all of the types in Types. 

      // This code doesn't work because there is no sequence point 
      // between each call to getValueFromArray, so the 
      // currentIndex can be incremented in a non-deterministic way. 
      func(this->getValueFromArray<Types>(array, currentIndex)...);
   }
};

我可以通过引入一个新类来解决这个问题:

代码语言:javascript
复制
template <typename T, std::size_t position>
struct Param
{
   using type = T;
   static constexpr std::size_t offset = position;
};

这样,我就可以在代码中指定每个参数的偏移量,而不是在运行时维护currentIndex,如下所示:

代码语言:javascript
复制
GENERIC_CLASS<Param<uint32_t, 0>, Param<uint8_t, 4>, Param<uint16_t, 5>>

以上可能容易出错,因为偏移量可能是错误的。有没有办法通过累积大小从类型的参数包中生成我的Params序列?

或者,对于我上面提到的序列点问题,有什么解决方法吗?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2018-06-27 23:18:24

看起来你想要这样的东西:

代码语言:javascript
复制
template <typename ...Ts>
void f(Ts... args)
{
    const int dummy[] = {0, ((std::cout << std::hex << args << std::endl), 0)...};
    static_cast<void>(dummy); // Avoid warning for unused variable
}

template <std::size_t N, typename Tuple, std::size_t ...Is>
void process(std::array<std::uint8_t, N> const& array, Tuple tuple, std::index_sequence<Is...>)
{
    int i = 0;
    const int dummy[] = {((memcpy(&std::get<Is>(tuple), array.data() + i, sizeof(std::tuple_element_t<Is, Tuple>)), i += sizeof(std::tuple_element_t<Is, Tuple>)), 0)...};
    static_cast<void>(dummy); // Avoid warning for unused variable
    f(std::get<Is>(tuple)...);
}

template <std::size_t N, typename ... Ts>
void process(std::array<std::uint8_t, N> const& array, std::tuple<Ts...> tuple)
{
    process(array, tuple, std::index_sequence_for<Ts...>{});
}

可以在C++11中实现index_sequence

Demo

票数 1
EN

Stack Overflow用户

发布于 2018-06-28 01:46:10

我提出了以下解决方案

代码语言:javascript
复制
#include <array>
#include <tuple>
#include <iostream>

template <typename ... Ts>
class process
 {
   private:

      template <typename T>
      static T getVal (std::uint8_t const * a)
       {
         T ret { *a++ };

         for ( auto i = 1U ; i < sizeof(T) ; ++i )
          {
            ret <<= 8;
            ret  += *a++;
          }

         return ret;
       }

      static std::size_t postIncr (std::size_t & pos, std::size_t add)
       {
         std::size_t ret { pos };

         pos += add;

         return ret;
       }

   public: 
      template <std::size_t N>
      static std::tuple<Ts...> func (std::array<std::uint8_t, N> const & a)
       {
         std::size_t  pos { 0U };

         return { getVal<Ts>(a.data()+postIncr(pos, sizeof(Ts)))... };
       }
 };

int main ()
 { 
   std::array<std::uint8_t, 7U>
      bytes{{0x01U, 0x02U, 0x03U, 0x04U, 0x10U, 0x20U, 0x30U}};

   auto tpl
    { process<std::uint32_t, std::uint8_t, std::uint16_t>::func(bytes) };

   std::cout << "- 0x" << std::hex << std::get<0>(tpl) << std::endl;
   std::cout << "- 0x" << int(std::get<1>(tpl)) << std::endl;
   std::cout << "- 0x" << std::get<2>(tpl) << std::endl;
 }

如果(我得到的)来自getVal()的来自ret <<= 8;的令人讨厌的警告(当Tstd::uint8_t时;警告"shift count >= width of type"),你可以为std::uint8_t开发第二个getVal (没有循环和shift),只在sizeof(T) == 1时启用它,并且只在sizeof(T) > 1时启用第一个。

否则,您可以用几个ret <<= 4;替换ret <<= 8;

票数 2
EN

Stack Overflow用户

发布于 2018-06-27 23:08:28

这样怎么样..。

代码语言:javascript
复制
#include <iostream>
#include <type_traits>
#include <cstring>
#include <typeinfo>

using namespace std;

template <size_t O,size_t I,size_t C,typename...>
struct byte_offset_impl;

template <size_t O,size_t I,size_t C,typename T,typename... Ts>
struct byte_offset_impl< O, I, C,T,Ts...> : 
    std::conditional<I == C,
      std::integral_constant<size_t,O>,
      byte_offset_impl<O + sizeof(T),I,C+1,Ts...>    
    >::type
{};


template <typename T>
struct type_c 
{ using type = T; };

template <size_t I,size_t C,typename...>
struct type_at_impl;

template <size_t I,size_t C,typename T,typename... Ts>
struct type_at_impl<I, C,T,Ts...> : 
    std::conditional<I == C,
      type_c<T>,
      type_at_impl<I,C+1,Ts...>  
    >::type
{};


template <size_t I,typename... Ts>
constexpr size_t byte_offset = byte_offset_impl<0,I,0,Ts...>::value;

template <size_t I,typename... Ts>
using type_at = typename type_at_impl<I,0,Ts...>::type;

template <typename...Ts>
struct generic_class
{
    generic_class(char* byteptr) : byteptr_(byteptr)
    {};


    template <size_t I>
    auto get() -> type_at<I, Ts...>& // needed for c++11
    {
        using result_type = type_at<I, Ts...>;
        return *reinterpret_cast<result_type*>(&byteptr_[byte_offset<I, Ts...>]);

    }

    template <size_t I>
    auto get_v() -> type_at<I, Ts...> // needed for c++11
    {
        using result_type = type_at<I, Ts...>;
        result_type result;

        std::memcpy(&result, &byteptr_[byte_offset<I, Ts...>], sizeof(result_type));

        return result;
    }
    char* byteptr_;
};


int main() {

    char bytes[sizeof(uint32_t) + sizeof(uint8_t) + sizeof(uint16_t)];

    uint32_t u32 = 1561;
    uint8_t u8 = 22;
    uint16_t u16 = 99;

    char* bytesp = bytes;
    std::memcpy(bytesp, &u32, sizeof(uint32_t)); bytesp += sizeof(uint32_t);
    std::memcpy(bytesp, &u8, sizeof(uint8_t)); bytesp += sizeof(uint8_t);
    std::memcpy(bytesp, &u16, sizeof(uint16_t));

    generic_class<uint32_t, uint8_t, uint16_t> gen(bytes);
    std::cout << (uint32_t)gen.get<0>() << std::endl;
    std::cout << (uint32_t)gen.get<1>() << std::endl;
    std::cout << (uint32_t)gen.get<2>() << std::endl;

    std::cout << (uint32_t)gen.get_v<0>() << std::endl;
    std::cout << (uint32_t)gen.get_v<1>() << std::endl;
    std::cout << (uint32_t)gen.get_v<2>() << std::endl;
}

Demo

根据您的平台,您可能会发现get<I>实现并不合适,因为访问可能不一致。get_v<I>替代方案将把数组中的数据复制到正确类型的元素中,增加的好处是只有在数据被访问时才会进行复制。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51064910

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档