Комбинированный взрыв значения перечисления (729 комбинаций ...) - PullRequest
0 голосов
/ 04 марта 2019

Я сталкиваюсь с проблемой, в которой мне нужно сгенерировать огромное количество кода, все это довольно похоже, и я хочу знать, есть ли какой-нибудь способ шаблонизации.

Допустим, у меня есть структура этого типа

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 видов узлов.Для решения этой задачи мне нужно огромное значение, которое проверяет Kind каждого узла, а затем вызывает 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));
}
...

insertElementэто мета-функция, которую я создал, которая вставляет элемент в контейнер, если подходит из списка контейнеров.

Для этого 2 случая требуется до 9 ifs.Для случая 3 требуется 27 ifs, а для случая 6 - 729 if.Я действительно не хочу их кодировать.

Есть идеи, как мне это решить?

Спасибо!

Ответы [ 2 ]

0 голосов
/ 04 марта 2019

С 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 перегрузок.

0 голосов
/ 04 марта 2019

Может быть, что-то подобное можно использовать в качестве отправной точки (возможно, я неправильно понял вопрос, и, возможно, есть способы сделать его короче):

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