我正在编写一个源文件库,其中包含一系列以一对一关系相关的声明和宏。第一个是类别列表,作为枚举:
typedef enum {
CID_SYS, // Highest-priority devices. Reserved.
CID_CTRL, // Controlling unit, only one per segment
CID_SENSOR, // Data providers (temperature, speed, clock)
CID_BROADCAST, // Broadcast messages (system messages extension)
...
} category_id_t;
我使用此枚举来定义 16 位消息标识符,其中类别位为最高有效 3 位。这些标识符被分段为两个可变大小的位 block 中的较低有效位。这些 block 之一取决于上述类别。因此,我还定义了一个尺寸列表作为宏,每个类别一个,如下所示:
#define SYS_MESSAGES_MAX 256
#define CTRL_MESSAGES_MAX 64
#define SENSOR_MESSAGES_MAX 8
#define BROADCAST_MESSAGES_MAX 64
...
然后就可以轻松屏蔽类别并检索相关位,即函数 ID,它位于消息 ID 的最低有效位中。以CID_SYS
为例:
unsigned short function_id = message_id & (SYS_MESSAGES_MAX-1)
我需要一个以类别作为参数的类模板。后者暗示的类别中的消息数量应该由模板类在编译时以某种方式推断出来,而不需要求助于数组。类模板可能看起来类似:
template <category_id_t CAT>
class Manager
{
...
unsigned message_count() const { return /* a number depending on CAT */ }
};
使用-Os
,编译器可以在编译时尽可能多地解析,而无需添加代码或变量。所以我想充分利用它。我当前的尝试是使用函数模板和特化:
template<category_id_t CAT>
unsigned cat_size();
template<category_id_t CAT>
class Manager
{
public:
unsigned size() const { return cat_size<CAT>(); }
};
template<> unsigned cat_size<CID_SYS>() { return SYS_MESSAGES_MAX; }
template<> unsigned cat_size<CID_CTRL>() { return CTRL_MESSAGES_MAX; }
上面的例子将是:
unsigned short function_id = message_id & (size()-1) /* This should be a constant resolved at compile-time */
通用模板函数故意保留其定义,以便在添加类别时忘记特化的情况下生成链接器错误。然而我发现这不优雅且令人费解。
我怎样才能让它更优雅?
我绝对不想将消息计数作为模板参数传递,因为我仍然需要 C 样式宏:我的库应该由 C 和 C++ 应用程序使用。 p>
最佳答案
现在我们有了 constexpr,这实际上不需要模板就可以实现。 cat_size
可以使用 constexpr
来完成映射函数具有 switch
。您可以定义 *_MESSAGES_MAX
单独constexpr int
如果您不喜欢返回的值如下所示
constexpr int cat_size(category_id_t cat) {
switch (cat) {
case CID_SYS:
return 256; // SYS_MESSAGES_MAX
case CID_CTRL:
return 64; // CTRL_MESSAGES_MAX
case CID_SENSOR:
return 8; // SENSOR_MESSAGES_MAX
case CID_BROADCAST:
return 64; // BROADCAST_MESSAGES_MAX
}
}
计算函数 id 只是另一个 constexpr
调用通过交换机。我已经替换了unsigned short
与 std::uint16_t
为了确保您得到想要的东西,请注意,这需要您 #include <cstdint>
constexpr std::uint16_t function_id(category_id_t cat, std::uint16_t msg) {
return msg & (cat_size(cat)-1);
}
查看下面生成的程序集,我们可以看到它实际上是在编译时计算的
int main() {
constexpr std::uint16_t msg{0xCDEF};
constexpr auto fid = function_id(CID_SYS, msg);
std::cout << fid << '\n';
}
关于c++ - 如何优雅地编写使用模板参数隐含值的类模板?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40027750/