我有以下代码:
#include <boost/hana.hpp>
#include <array>
#include <iostream>
#include <utility>
namespace hana = boost::hana;
#define HEADER_CONNECT 0b00010000
#define HEADER_CONNACK 0b00001000
struct ConnectFrame
{
uint8_t header = 16;
uint8_t variable = 2;
};
struct ConnackFrame
{
uint8_t header = 8;
uint8_t variable = 3;
};
constexpr auto FramesMap = hana::make_tuple(
hana::make_pair(hana::type_c<ConnectFrame>, hana::integral_c<std::uint8_t, HEADER_CONNECT>),
hana::make_pair(hana::type_c<ConnackFrame>, hana::integral_c<std::uint8_t, HEADER_CONNACK>));
//! Runtime deserialization switch based on FramesMap
template <typename InputIterator>
auto deserializeByFrameHeader(const std::uint8_t frameHeader, const InputIterator buffer)
{
auto found = hana::index_if(FramesMap, [&frameHeader = std::as_const(frameHeader)](auto const &pair) {
return hana::second(pair) == hana::integral_c<std::uint8_t, frameHeader>;
});
auto FrameType = hana::first(hana::at(FramesMap, found.value()));
using T = typename decltype(FrameType)::type;
T var;
//deserialize(buffer, var);
return var;
}
int main()
{
std::array<std::byte, 128> buffer;
// for dummy purposes we assume that the first byte of the buffer array after serialization is 8
const uint8_t header = 8;
ConnackFrame frameOut = deserializeByFrameHeader(header, buffer.begin());
}
我尝试在与变量frameHeader匹配的元组中找到该对的索引。不幸的是,我收到一个编译错误:
../include/minimalMQTT.hpp:178:43: error: 'this' is not a constant expression
178 | return hana::second(pair) == hana::integral_c<std::uint8_t, frameHeader>;
| ~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
如何才能将变量frameHeader
声明为常量表达式以使其起作用?
答案 0 :(得分:1)
hana::integral_c<std::uint8_t, frameHeader>
integral_c
是一种编码静态已知值的类型。但是,您尝试使用frameHeader
实例化该变量,该变量不是静态的。
要在编译参数值上映射运行时值,最好的办法是进行映射(有时使用二进制搜索)。但是,您也可以检查是否需要评估此编译时间。
在您的 specific 示例中,您可以使用constexpr lambda(考虑到最近的编译器!)来解决问题。
在实践中,我怀疑这是否永远都能满足您的需求,但是只是您知道这个窍门:
#include <boost/hana.hpp>
#include <boost/core/ignore_unused.hpp>
#include <array>
#include <iostream>
#include <utility>
namespace hana = boost::hana;
#define HEADER_CONNECT 0b00010000
#define HEADER_CONNACK 0b00001000
struct ConnectFrame
{
uint8_t header = 16;
uint8_t variable = 2;
};
struct ConnackFrame
{
uint8_t header = 8;
uint8_t variable = 3;
};
constexpr auto FramesMap = hana::make_tuple(
hana::make_pair(hana::type_c<ConnectFrame>, hana::integral_c<std::uint8_t, HEADER_CONNECT>),
hana::make_pair(hana::type_c<ConnackFrame>, hana::integral_c<std::uint8_t, HEADER_CONNACK>));
//! Runtime deserialization switch based on FramesMap
template <typename FrameHeader, typename InputIterator>
auto deserializeByFrameHeader(FrameHeader const frameHeader, const InputIterator buffer)
{
auto found = hana::index_if(FramesMap, [=](auto const &pair) constexpr {
return hana::second(pair) == hana::integral_c<std::uint8_t, frameHeader()>;
});
auto FrameType = hana::first(hana::at(FramesMap, found.value()));
using T = typename decltype(FrameType)::type;
T var;
boost::ignore_unused(buffer);
//deserialize(buffer, var);
return var;
}
int main()
{
std::array<std::byte, 128> buffer;
// for dummy purposes we assume that the first byte of the buffer array after serialization is 8
ConnackFrame frameOut = deserializeByFrameHeader(
[]() constexpr { return 8; },
buffer.begin());
boost::ignore_unused(frameOut);
}
从技术上讲,标头是缓冲区数组的第一个字节,即uint8_t标头=(uint8_t)buffer [0]。是否可以省略header参数,并直接从缓冲区中将其标题提取为constexpr?
不。
返回类型是固定的。输入是动态的。无法(有效/有效地)铺平道路。
顺便说一句,如果我不需要评估编译时间,那将是一个解决方案?
由于要解析协议消息,因此自然会打开类型id(因为它们在网络上的存在方式)。作为认真的C ++程序员,您自然希望尽快将抽象层跳到proper type-switching。
std::variant<...>
和访问。取决于您的使用模式和处理需求,两者可能都更适用。 std::variant
具有很好的功能,它以可切换的方式编码类型,但是访问保留了静态类型信息。这意味着:从技术上讲,您也许可以利用静态类型信息,内联和所有优化优势。
这就是您要追求的。所以我建议:
#include <array>
#include <iostream>
#include <variant>
constexpr uint8_t HEADER_CONNECT = 0b00010000;
constexpr uint8_t HEADER_CONNACK = 0b00001000;
struct ConnectFrame {
uint8_t header = 16;
uint8_t variable = 2;
};
struct ConnackFrame {
uint8_t header = 8;
uint8_t variable = 3;
};
// Static typed land
void handler(ConnectFrame const&) { std::cout << "Handling ConnectFrame\n"; }
void handler(ConnackFrame const&) { std::cout << "Handling ConnackFrame\n"; }
template <typename InputIterator>
void deserialize(InputIterator&, ConnectFrame&) { /*TODO*/ }
template <typename InputIterator>
void deserialize(InputIterator&, ConnackFrame&) { /*TODO*/ }
template <typename Frame, typename InputIterator>
Frame deserialize(InputIterator& buffer) {
Frame frame;
deserialize(buffer, frame);
return frame;
}
// Type-swithcing land
template <typename InputIterator>
constexpr inline std::uint8_t frameHeader(InputIterator& buffer) {
return static_cast<std::uint8_t>(*buffer++);
}
using AnyFrame = std::variant<ConnectFrame, ConnackFrame>;
template <typename InputIterator>
AnyFrame deserializeByFrameHeader(InputIterator&& buffer) {
switch (uint8_t h = frameHeader(buffer)) {
case HEADER_CONNECT: return deserialize<ConnectFrame>(buffer);
case HEADER_CONNACK: return deserialize<ConnackFrame>(buffer);
}
throw std::range_error("frameHeader");
}
int main() {
constexpr auto process = [](auto const& frame) { handler(frame); };
using Buffer = std::array<std::byte, 128>;
for (auto buffer : { Buffer
{ std::byte(HEADER_CONNECT), std::byte(0x12), std::byte(0x34), },
{ std::byte(HEADER_CONNACK), std::byte(0xab), std::byte(0xcd), } })
{
auto frameOut = deserializeByFrameHeader(buffer.begin());
std::visit(process, frameOut);
}
}
哪些印刷品
Handling ConnectFrame
Handling ConnackFrame
如果您真的认为从映射表开始工作很重要,则可以使用更多的代码和编译器汗水:
constexpr auto FramesMap = hana::make_tuple(
hana::make_pair(hana::type_c<ConnectFrame>, HEADER_CONNECT),
hana::make_pair(hana::type_c<ConnackFrame>, HEADER_CONNACK)
);
请注意,由于我们不需要
integral_c
,我将其丢掉了
让AnyFrame
成为框架类型的变体:
constexpr auto FrameTypes = hana::transform(FramesMap, hana::first);
using AnyFrame = decltype(
hana::unpack(FrameTypes, hana::template_<std::variant>))
::type;
现在,让我们重新使用deserializeByFrameHeader
:
template <typename InputIterator>
AnyFrame deserializeByFrameHeader(InputIterator&& buffer) {
AnyFrame retval;
hana::for_each(FramesMap,
[&, frameHeader = frameHeader(buffer)](auto const &pair) {
auto first = hana::first(pair);
using T = typename decltype(first)::type;
if (hana::second(pair) == frameHeader) {
retval.emplace<T>();
deserialize(buffer, std::get<T>(retval));
}
});
return retval;
}
请注意简化:我们将所有内容都取决于多态lambda中的元组元素(pair
)的静态类型,在那里我们始终可以使用帧类型。
#include <cstdint>
constexpr uint8_t HEADER_CONNECT = 0b00010000;
constexpr uint8_t HEADER_CONNACK = 0b00001000;
struct ConnectFrame {
uint8_t header = 16;
uint8_t variable = 2;
};
struct ConnackFrame {
uint8_t header = 8;
uint8_t variable = 3;
};
#include <boost/hana.hpp>
#include <stdexcept>
#include <variant>
#include <iostream>
namespace {
namespace hana = boost::hana;
constexpr auto FramesMap = hana::make_tuple(
hana::make_pair(hana::type_c<ConnectFrame>, HEADER_CONNECT),
hana::make_pair(hana::type_c<ConnackFrame>, HEADER_CONNACK)
);
constexpr auto FrameTypes = hana::transform(FramesMap, hana::first);
using AnyFrame = decltype(
hana::unpack(FrameTypes, hana::template_<std::variant>))
::type;
}
// Static typed land
void handler(ConnectFrame const&) { std::cout << "Handling ConnectFrame\n"; }
void handler(ConnackFrame const&) { std::cout << "Handling ConnackFrame\n"; }
template <typename InputIterator>
void deserialize(InputIterator&, ConnectFrame&) { /*TODO*/ }
template <typename InputIterator>
void deserialize(InputIterator&, ConnackFrame&) { /*TODO*/ }
// Type-swithcing land
template <typename InputIterator>
constexpr inline std::uint8_t frameHeader(InputIterator& buffer) {
return static_cast<std::uint8_t>(*buffer++);
}
template <typename InputIterator>
AnyFrame deserializeByFrameHeader(InputIterator&& buffer) {
AnyFrame retval;
hana::for_each(FramesMap,
[&, frameHeader = frameHeader(buffer)](auto const &pair) {
auto first = hana::first(pair);
using T = typename decltype(first)::type;
if (hana::second(pair) == frameHeader) {
retval.emplace<T>();
deserialize(buffer, std::get<T>(retval));
}
});
return retval;
}
#include <array>
int main() {
constexpr auto process = [](auto const& frame) { handler(frame); };
using Buffer = std::array<std::byte, 128>;
for (auto buffer : { Buffer
{ std::byte(HEADER_CONNECT), std::byte(0x12), std::byte(0x34), },
{ std::byte(HEADER_CONNACK), std::byte(0xab), std::byte(0xcd), } })
{
auto frameOut = deserializeByFrameHeader(buffer.begin());
std::visit(process, frameOut);
}
}
打印
Handling ConnectFrame
Handling ConnackFrame