c++ - 不是 lambda 函数中的常量表达式

标签 c++ boost c++17 boost-hana

我有以下代码:

#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());
}

Live demo

我尝试在与变量 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被声明为常量表达式以使其工作?

最佳答案

hana::integral_c<std::uint8_t, frameHeader>
integral_c是一种编码静态已知值的类型。但是,您尝试使用 frameHeader 实例化它这不是静态已知的。

要将运行时值映射到 compile-tume 值上,您可以做的最好的事情是映射(有时使用二进制搜索)。但是,您也可以检查是否需要评估此编译时间。

解决方法

在您的特定示例中,您可以通过使用 constexpr lambda 来解决问题(给定一个足够新的编译器!)。

在实践中,我怀疑这是否适合您的需求,但只是让您知道诀窍:

Live On Wandbox
#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);
}

更新

Technically, the header is the first byte of the buffer array, i.e. uint8_t header = (uint8_t)buffer[0]. Would it be possible to omit the header argument and extract the header from the buffer as a constexpr directly?



不。

返回类型是固定的。输入是动态的。没有办法铺平(有用/有效)。

By the way, what would be a solution if I don't need it compile time evaluated?



由于您正在解析协议(protocol)消息,因此您自然会打开类型 ID(因为这就是它们在线上存在的方式)。作为一个认真的 C++ 程序员,你自然希望将抽象层跳到 proper type-switching。尽快地。
  • 老式技术是动态多态性(虚拟接口(interface)和继承)
  • 现代机制包括std::variant<...>与探视。

  • 根据您的使用模式和处理需求,可能更适用。 std::variant有一个很好的特性,它以可切换的方式对类型进行编码,但访问保留了静态类型信息。这意味着:从技术上讲,您可以利用静态类型信息、内联和所有优化优点。

    看来这就是你所追求的 .所以我建议:

    Live On Coliru
    #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
    

    使用 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)
    );
    

    Note how I dropped the integral_c because we don't need it



    让我们制作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;
    }
    

    请注意简化:我们根据元组元素的静态类型 (pair) 保留所有内容,在多态 lambda 中,我们始终拥有可用的帧类型。

    完整演示

    Live On Coliru
    #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
    

    关于c++ - 不是 lambda 函数中的常量表达式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62264698/

    相关文章:

    C++语法题,class关键字的使用

    c++ - 在 vf2_sub_graph_iso 中使用属性映射进行等价

    c++ - 将函数从结构传递给函数

    c++ - 打印附加到 float 的%字符

    c++ - 如何将所有 3rdparty 运行时依赖项包含到 linux 上的 cmake/cpack 生成的包中?

    c++ - 不稳定的行为

    在 boost::shared_ptr operator bool() 上旋转时需要 C++ volatile?

    c++ - 无法使用 packaged_task 在 thread_group 中创建线程

    c++ - 推导出具有默认模板参数的模板函数指针的模板参数

    c++ - Constexpr,如果测试一个模板参数