compile-time variable-length objects 基于字符串

compile-time variable-length objects based on string

相关的 SO 问题:

遗憾的是,没有一个(或其他类似的)提供我正在寻找的解决方案。

背景

USB 描述符(通常)是 byte-array 结构。 “字符串描述符”定义为字节数组,以 2 个字节的标准“header”开头,后跟一串 UNICODE(16 位)字符。

例如,值为“AB”的 USB 字符串描述符将具有以下字节序列:

0x06 0x03 0x41 0x00 0x42 0x00

其中0x06是描述符的总大小(包括header),0x03是它的“类型”(由标准定义)

当前(不满意)方法:

// other types omitted for clarity
enum UsbDescriptorType: uint8_t { USB_DESCR_STRING = 0x03 };

struct UsbDescrStd {
    uint8_t bLength;
    UsbDescriptorType bDescriptorType;
};

template<size_t N>
struct UsbDescrString final: UsbDescrStd {
    char str[N * 2];

    constexpr UsbDescrString(const char s[N]) noexcept
        : UsbDescrStd{sizeof(*this), UsbDescriptorType::USB_DESCR_STRING}
        , str {}
    {
        for(size_t i = 0; i < N; ++i)
            str[i * 2] = s[i];
    }
};

下面是它的用法示例,以及为什么它们对我来说“不够好”的简短评论:

// requires size information
constexpr UsbDescrString<9> uds9{"Descr str"};

// string duplication
constexpr UsbDescrString<sizeof("Descr str")-1> udsa{"Descr str"};

// requires an explicit string storage
constexpr auto UsbDescrStrTxt{"Descr str"};
constexpr UsbDescrString<sizeof(UsbDescrStrTxt)-1> udsa2{UsbDescrStrTxt};

// ugly use of a macro
#define MAKE_UDS(name, s) UsbDescrString<sizeof(s)-1> name{s}
constexpr MAKE_UDS(udsm, "Descr str");

从 C++20 开始,“模板的字符串参数”被明确禁止,也切断了该解决方案。

我想要达到的目标

理想情况下,我希望能够编写如下代码:

constexpr UsbDescrString uds{"Descr str"}; // or a similar "terse" approach

它简单、简洁,error-resistant,而且切中要点。我需要帮助以一种允许我创建 compile-time objects 而没有不必要的代码膨胀的方式编写 UsbDescrString

UsbDescrString 中添加一个 CTAD 就足够了

template<size_t N>
struct UsbDescrString final: UsbDescrStd {
    char str[N * 2];

    constexpr UsbDescrString(const char (&s)[N+1]) noexcept
        : UsbDescrStd{sizeof(*this), UsbDescriptorType::USB_DESCR_STRING}
        , str {}
    {
        for(size_t i = 0; i < N; ++i)
            str[i * 2] = s[i];
    }
};

template<size_t N>
UsbDescrString(const char (&)[N]) -> UsbDescrString<N-1>;

注意,为了防止数组到指针衰减,需要使用const char (&)作为构造函数参数。

Demo

"String argument to template" is explicitly prohibited as of C++20, cutting that solution off as well.

但是,多亏了 P0732,借助一些帮助程序 类,例如 basic_fixed_string,现在在 C++20 中您可以

template<fixed_string>
struct UsbDescrString final: UsbDescrStd;

constexpr UsbDescrString<"Descr str"> uds9;