在嵌入式环境中工作时,我反复编写代码,从协议层获取字节数组,并将这些字节转换为C++类表示。
表示uint32_t
的示例字节数组,后跟uint8_t
,再后跟uint16_t
,可能如下所示。
std::array<uint8_t, 7> bytes(0x01, 0x02, 0x03, 0x04, 0x10, 0x20, 0x30);
其中0x01020304
是我的uin32_t
,0x10
是我的uint8_t
,0x2030
是我的uint16_t
。
我还有一个变量函数func()
,我想用从有效负载中解析出来的值来调用它。
为此,我手动定义了一个中间对象:
// 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
。
这是我最初尝试的:
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)...);
}
};
我可以通过引入一个新类来解决这个问题:
template <typename T, std::size_t position>
struct Param
{
using type = T;
static constexpr std::size_t offset = position;
};
这样,我就可以在代码中指定每个参数的偏移量,而不是在运行时维护currentIndex
,如下所示:
GENERIC_CLASS<Param<uint32_t, 0>, Param<uint8_t, 4>, Param<uint16_t, 5>>
以上可能容易出错,因为偏移量可能是错误的。有没有办法通过累积大小从类型的参数包中生成我的Params
序列?
或者,对于我上面提到的序列点问题,有什么解决方法吗?
发布于 2018-06-27 23:18:24
看起来你想要这样的东西:
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
。
发布于 2018-06-28 01:46:10
我提出了以下解决方案
#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;
的令人讨厌的警告(当T
为std::uint8_t
时;警告"shift count >= width of type"),你可以为std::uint8_t
开发第二个getVal
(没有循环和shift),只在sizeof(T) == 1
时启用它,并且只在sizeof(T) > 1
时启用第一个。
否则,您可以用几个ret <<= 4;
替换ret <<= 8;
。
发布于 2018-06-27 23:08:28
这样怎么样..。
#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;
}
根据您的平台,您可能会发现get<I>
实现并不合适,因为访问可能不一致。get_v<I>
替代方案将把数组中的数据复制到正确类型的元素中,增加的好处是只有在数据被访问时才会进行复制。
https://stackoverflow.com/questions/51064910
复制相似问题