Обычный подход - хранить указатель на условную функцию.
Вы реализуете набор условий как отдельные функции и прикрепляете указатель к нужному условию в таблице. Каждая функция тестирует для заданного набора условий. Вы перебираете список, пока одна из указанных функций не вернет true. Конечно, «текущее состояние» может использоваться как часть условий, что устраняет необходимость в 2d массиве.
struct {
bool(*test)(); //the condition
void(*onsuccess)(); //event
} condition;
Это может быть неэффективно, если условия многократно повторяются в цепочке, например:
ev1: (a && b && c)
ev2: (a && !b && c)
ev3: (a && b && d)
ev4: (a && !b && !c)
ev5: (!a)
(большинство событий тестируется для a
отдельно, если тестирование для a требует больших вычислительных ресурсов, это потребует гораздо больше времени процессора, чем необходимо).
В этом случае вам придется переназначить список condition_set -> event
в одно дерево решений - гораздо сложнее поддерживать, но более эффективно использовать процессор:
a:
b:
c: ev1
d: ev3
!b:
c: ev2
!c: ev4
!a: ev5
Это, конечно, больше не может быть простым одномерным списком, но вместо него требуется древовидная структура, скажем, разветвленный связанный список:
struct {
bool(*test)(); //the condition
void(*onsuccess)(); //event (if any)
condition* next_sibling; //other conditions dependent on parent but independent from this one,
condition* first_child; //other conditions dependent on this one,
} condition;
NULL на любом из указателей означает «нет такого элемента» (кроме «test», который не может быть нулевым). И, конечно, рекурсия необходима для обхода списка.