【发布时间】:2019-08-01 16:09:59
【问题描述】:
背景
我正在尝试对 C 风格的 API 进行现代化改造,该 API 从嵌入式应用程序“注册”到容器(数组)TU 静态变量,以将它们分组到功能相关的组中,并在整个过程中跟踪这些组的值应用程序的生命周期。此接口通过标记的 void 指针支持多种变量类型,需要条件逻辑在需要时转换回正确类型的值(例如用于日志记录)。
编译器:ARM GCC 8.2
完整代码:Compiler explorer
稳定的代码
以下是变量“包装器”和这些包装器的集合的示例实施例:
#include <cstdint>
#include <cstring>
#include <array>
enum class VarT {
kUndefined,
kBoolean,
kUinteger
};
/* "Wraps" the desired variable-to-register */
struct Var {
const VarT val_t;
const void * const val;
const char * const name; /* User-facing var name */
constexpr Var(const char * const name, void* value, VarT type)
: val_t{type}
, val{value}
, name{name}
{ }
};
/* A naive attempt at generalizing the templated Group objects */
struct Base { };
/* A group of `Var`s */
template<std::size_t TNumVars>
struct VarGroup : Base {
/* Placed first strategically -- shared data */
std::size_t num_vars = TNumVars;
/* Because this is "flexible" from VarGroup to VarGroup
* due to TNumVars template parameter */
std::array<Var, TNumVars> vars;
explicit constexpr VarGroup(std::array<Var, TNumVars> var_arr)
: vars{var_arr}
{ }
};
可疑代码
尝试创建 VarGroups 的集合:
/* What makes up the collection of VarGroups */
struct GroupHandle {
/* User-facing VarGroup name */
const char * name;
/* Pointer to statically allocated VarGroup in other TUs */
const Base * group;
};
const std::size_t max_groups = 5;
/* The array of VarGroups */
auto groups = std::array<GroupHandle, max_groups>{};
std::size_t groups_idx = 0;
void regGroup(const char * name, const Base * g) {
/* Ignore bounds checking for the example */
groups[groups_idx++] = GroupHandle{name, g};
}
/* THE QUESTIONABLE CAST */
/* | */
/* v */
const VarGroup<1>* getGroup(const char * name) {
for (auto& g : groups) {
if (std::strcmp(name, g.name) == 0) {
return static_cast<const VarGroup<1>*>(g.group);
}
}
return nullptr;
}
测试代码
/* Some variables to track */
std::uint32_t uint_var = 42;
bool bool_var = true;
/* Create a group */
constexpr auto group1 = []() {
std::array vars = {
Var("g1 uint var", &uint_var, VarT::kUinteger),
Var("g1 bool var", &bool_var, VarT::kBoolean)
};
return VarGroup(vars);
}();
/* Create another group */
constexpr auto group2 = []() {
std::array vars = {
Var("g2 uint var", &uint_var, VarT::kUinteger)
};
return VarGroup(vars);
}();
/* test */
int main() {
regGroup("group one", &group1);
regGroup("group two", &group2);
/* get group one and iterate over all of its vars */
auto g1 = getGroup("group one");
if (g1 != nullptr) {
printf("Group one vars: \n");
for (std::size_t i = 0; i < g1->num_vars; i++) {
auto var = g1->vars[i];
printf("%s | %d | %d\n", var.name, var.val_t, *((int*)var.val));
}
}
uint_var = 7;
/* get group two and iterate over all of its vars */
auto g2 = getGroup("group two");
if (g2 != nullptr) {
printf("Group two vars: \n");
for (std::size_t i = 0; i < g2->num_vars; i++) {
auto var = g2->vars[i];
printf("%s | %d | %d\n", var.name, var.val_t, *((int*)var.val));
}
}
}
输出:
Group one vars:
g1 uint var | 2 | 42
g1 bool var | 1 | 1
Group two vars:
g2 uint var | 2 | 7
这可以按要求工作,但肯定感觉很脏。这种类型强制的实例——虽然看起来无关紧要,但静态数组大小分配取决于非类型模板参数——是否会导致不安全
【问题讨论】:
-
你能把它压缩成一个更小的例子吗
-
你不能在
VarGroup中使用std::vector而不是std::array(因此删除模板),那么您将拥有相同的类型并且能够拥有std::vector<VarGroup>。 -
如果你的类没有共同点,它们就不应该有一个共同的基类。如果他们确实有一些共同点,那么这些东西应该在他们的共同基类中。空基类基本上是一个错误。
标签: c++ templates casting containers undefined-behavior