枚举值的组合爆炸(729 种组合...)
Combination explosion of an enum value (729 combinations...)
我面临一个问题,我必须生成大量代码,所有代码都非常相似,我想知道是否有任何模板化方法。
假设我有这种类型的结构
template <typename ...NodeKindTs>
struct Element{
std::tuple<NodeKindTs...> nodes;
}
我有一个整数向量,它将一个节点与另一个节点相关联,还有一个枚举向量,它说明每个节点是哪种类型。种类可以是A、B或C。
enum class Kind {A,B,C};
std::vector<int> relationShip;
std::vector<Kind> kind;
例如,如果我有
relationShip = {1,2,-1};
kind = {A,B,A}
表示第一个节点属于 A 类并且与第二个节点 B 类相关。你明白了。
现在,我必须创建元素并将它们插入到一个向量中,具体取决于每个节点和关系传送的 NodeKind。此元素由最多 6 个 NodeKind 模板化。为了解决这个问题,我需要一个巨大的 if 来检查每个节点的种类,然后调用 Element ctor。
对于 2 个 NodeKinds 的情况,这意味着做类似
的事情
if (node1.type == A && node2.type == A) {
auto &simNode1 = containerOfAs(node1.id);
auto &smiNode2 = containerOfAs(node2.id);
insertElement(elementFactory(simNode1, simNode2));
}
if (node1.type == A && node2.type == C)
{
auto &simNode1 = containerOfAs(node1.id);
auto &smiNode2 = containerOfCs(node2.id);
insertElement(elementFactory(simNode1, simNode2));
}
if (node1.type == B && node2.type == C)
{
auto &simNode1 = containerOfBs(node1.id);
auto &smiNode2 = containerOfCs(node2.id);
insertElement(elementFactory(simNode1, simNode2));
}
...
inserElement 是我创建的一个元函数,如果适合容器列表,它会将元素插入到容器中。
对于这2个案例,这最多需要9个if。对于 3 种情况,它需要 27 个 ifs,而对于 6 种情况,它需要 729 个 ifs。我真的不想对它们进行编码。
知道如何解决这个问题吗?
谢谢!
也许这样的事情可以作为一个起点(我可能误解了这个问题,可能有办法让它更短):
#include <iostream>
enum class Kind { A, B, C };
std::ostream& operator<<(std::ostream& os, Kind k) {
switch (k) {
case Kind::A: return os << "A";
case Kind::B: return os << "B";
case Kind::C: return os << "C";
}
return os << "Unknown";
}
template<typename F>
void dispatch(Kind k, F f) {
switch (k) {
case Kind::A: f.template call<Kind::A>(); return;
case Kind::B: f.template call<Kind::B>(); return;
case Kind::C: f.template call<Kind::C>(); return;
}
abort();
}
template<Kind k1>
struct handle2 {
template<Kind k2>
void call() {
std::cout << "k1=" << k1 << " k2=" << k2 << "\n";
// Do your thing with k1 and k2 here
}
};
struct handle1 {
Kind k2;
template<Kind k1>
void call() {
dispatch(k2, handle2<k1>{});
}
};
void handle(Kind k1, Kind k2) {
dispatch(k1, handle1{k2});
}
int main() {
handle(Kind::C, Kind::B);
}
使用 std::variant
,您可能会得到类似的东西:
std::variant<std::reference_wrapper<NodeA>,
std::reference_wrapper<NodeB>,
std::reference_wrapper<NodeC>> getNodeAsVariant(Kind kind, int id)
{
switch (kind) {
case Kind::A: return containerOfAs(id);
case Kind::B: return containerOfBs(id);
case Kind::C: return containerOfCs(id);
}
throw std::runtime_error("Invalid kind");
}
然后
auto v1 = getNodeAsVariant(node1.type, node1.id);
auto v2 = getNodeAsVariant(node2.type, node2.id);
auto v3 = getNodeAsVariant(node3.type, node3.id);
auto v4 = getNodeAsVariant(node4.type, node4.id);
auto v5 = getNodeAsVariant(node5.type, node5.id);
auto v6 = getNodeAsVariant(node6.type, node6.id);
// r1, .., r6 would be the correct reference_wrapper<T>
std::visit([](auto r1, auto r2, auto r3, auto r4, auto r5, auto r6) {
insertElement(elementFactory(r1/*.get()*/, r2, r3, r4, r5, r6));
}, v1, v2, v3, v4, v5, v6);
所以 std::visit
会为您生成 729 个重载。
我面临一个问题,我必须生成大量代码,所有代码都非常相似,我想知道是否有任何模板化方法。
假设我有这种类型的结构
template <typename ...NodeKindTs>
struct Element{
std::tuple<NodeKindTs...> nodes;
}
我有一个整数向量,它将一个节点与另一个节点相关联,还有一个枚举向量,它说明每个节点是哪种类型。种类可以是A、B或C。
enum class Kind {A,B,C};
std::vector<int> relationShip;
std::vector<Kind> kind;
例如,如果我有
relationShip = {1,2,-1};
kind = {A,B,A}
表示第一个节点属于 A 类并且与第二个节点 B 类相关。你明白了。
现在,我必须创建元素并将它们插入到一个向量中,具体取决于每个节点和关系传送的 NodeKind。此元素由最多 6 个 NodeKind 模板化。为了解决这个问题,我需要一个巨大的 if 来检查每个节点的种类,然后调用 Element ctor。
对于 2 个 NodeKinds 的情况,这意味着做类似
的事情if (node1.type == A && node2.type == A) {
auto &simNode1 = containerOfAs(node1.id);
auto &smiNode2 = containerOfAs(node2.id);
insertElement(elementFactory(simNode1, simNode2));
}
if (node1.type == A && node2.type == C)
{
auto &simNode1 = containerOfAs(node1.id);
auto &smiNode2 = containerOfCs(node2.id);
insertElement(elementFactory(simNode1, simNode2));
}
if (node1.type == B && node2.type == C)
{
auto &simNode1 = containerOfBs(node1.id);
auto &smiNode2 = containerOfCs(node2.id);
insertElement(elementFactory(simNode1, simNode2));
}
...
inserElement 是我创建的一个元函数,如果适合容器列表,它会将元素插入到容器中。
对于这2个案例,这最多需要9个if。对于 3 种情况,它需要 27 个 ifs,而对于 6 种情况,它需要 729 个 ifs。我真的不想对它们进行编码。
知道如何解决这个问题吗?
谢谢!
也许这样的事情可以作为一个起点(我可能误解了这个问题,可能有办法让它更短):
#include <iostream>
enum class Kind { A, B, C };
std::ostream& operator<<(std::ostream& os, Kind k) {
switch (k) {
case Kind::A: return os << "A";
case Kind::B: return os << "B";
case Kind::C: return os << "C";
}
return os << "Unknown";
}
template<typename F>
void dispatch(Kind k, F f) {
switch (k) {
case Kind::A: f.template call<Kind::A>(); return;
case Kind::B: f.template call<Kind::B>(); return;
case Kind::C: f.template call<Kind::C>(); return;
}
abort();
}
template<Kind k1>
struct handle2 {
template<Kind k2>
void call() {
std::cout << "k1=" << k1 << " k2=" << k2 << "\n";
// Do your thing with k1 and k2 here
}
};
struct handle1 {
Kind k2;
template<Kind k1>
void call() {
dispatch(k2, handle2<k1>{});
}
};
void handle(Kind k1, Kind k2) {
dispatch(k1, handle1{k2});
}
int main() {
handle(Kind::C, Kind::B);
}
使用 std::variant
,您可能会得到类似的东西:
std::variant<std::reference_wrapper<NodeA>,
std::reference_wrapper<NodeB>,
std::reference_wrapper<NodeC>> getNodeAsVariant(Kind kind, int id)
{
switch (kind) {
case Kind::A: return containerOfAs(id);
case Kind::B: return containerOfBs(id);
case Kind::C: return containerOfCs(id);
}
throw std::runtime_error("Invalid kind");
}
然后
auto v1 = getNodeAsVariant(node1.type, node1.id);
auto v2 = getNodeAsVariant(node2.type, node2.id);
auto v3 = getNodeAsVariant(node3.type, node3.id);
auto v4 = getNodeAsVariant(node4.type, node4.id);
auto v5 = getNodeAsVariant(node5.type, node5.id);
auto v6 = getNodeAsVariant(node6.type, node6.id);
// r1, .., r6 would be the correct reference_wrapper<T>
std::visit([](auto r1, auto r2, auto r3, auto r4, auto r5, auto r6) {
insertElement(elementFactory(r1/*.get()*/, r2, r3, r4, r5, r6));
}, v1, v2, v3, v4, v5, v6);
所以 std::visit
会为您生成 729 个重载。