у меня есть while (!Queue.empty())
цикл, который обрабатывает очередь элементов. Существует ряд сопоставителей шаблонов, идущих от наивысшего приоритета к наименьшему приоритету. Когда образец сопоставляется, соответствующий элемент удаляется из очереди, и сопоставление перезапускается сверху (так что сопоставители с наивысшим приоритетом получают возможность действовать первыми).
Итак, сейчас это выглядит примерно так (упрощенная версия):
while (!Queue.empty())
{
auto & Element = *Queue.begin();
if (MatchesPatternA(Element)) { // Highest priority, since it's first
// Act on it
// Remove Element from queue
continue;
}
if (MatchesPatternB(Element)) {
// Act on it
// Remove Element from queue
continue;
}
if (MatchesPatternC(Element)) { // Lowest priority, since it's last
// Act on it
// Remove Element from queue
continue;
}
// If we got this far, that means no pattern was matched, so
// Remove Element from queue
}
Это работает, но я хочу каким-то образом реорганизовать этот цикл, чтобы исключить использование ключевого слова. continue
,
Зачем? Потому что, если я хочу передать шаблон, соответствующий внешней функции, он явно ломается. Например.
void ExternalMatching(...)
{
if (MatchesPatternB(Element)) {
// Act on it
// Remove Element from queue
continue; // This won't work here
}
}
while (!Queue.empty())
{
auto & Element = *Queue.begin();
if (MatchesPatternA(Element)) {
// Act on it
// Remove Element from queue
continue;
}
ExternalMatching(...);
if (MatchesPatternC(Element)) {
// Act on it
// Remove Element from queue
continue;
}
// If we got this far, that means no pattern was matched, so
// Remove Element from queue
}
Я не хочу писать повторяющиеся, если такие заявления, как if (ExternalMatching(...)) { ... continue; }
Я бы лучше нашел более чистый способ выразить эту логику.
Этот упрощенный пример может показаться хорошей идеей сделать сопоставление с образцом более общим, а не иметь MatchesPatternA
, MatchesPatternB
, MatchesPatternC
и др. функции. Но в моей ситуации закономерности довольно сложны, и я пока не готов их обобщать. Поэтому я хочу сохранить эту часть как есть, отдельные функции.
Какие-нибудь изящные идеи? Спасибо!
Если у вас есть доступ к C ++ 11, я хотел бы предложить другое решение. По сути, я создал контейнер обработчиков и действий, которые можно настроить во время выполнения. Это может быть за или против вашего дизайна в зависимости от ваших требований. Вот:
#include <functional>
typedef std::pair<std::function<bool(const ElementType &)>,
std::function<void(ElementType &)> > HandlerData;
typedef std::vector<HandlerData> HandlerList;HandlerList get_handlers()
{
HandlerList handlers;
handlers.emplace_back([](const ElementType &el){ return MatchesPatternA(el); },
[](ElementType &el){ /* Action */ });
handlers.emplace_back([](const ElementType &el){ return MatchesPatternB(el); },
[](ElementType &el){ /* Action */ });
handlers.emplace_back([](const ElementType &el){ return MatchesPatternC(el); },
[](ElementType &el){ /* Action */ });
return handlers;
}int main()
{
auto handlers = get_handlers();
while(!Queue.empty()) {
auto &Element = *Queue.begin();
for(auto &h : handlers) {
// check if handler matches the element
if(h.first(Element)) {
// act on element
h.second(Element);
break;
}
}
// remove element
Queue.pop_front();
}
}
Я бы порекомендовал использовать функцию, которая выполняет сопоставление с образцом (но не влияет на результат), а затем набор функций, которые действуют на различные параметры:
enum EventType {
A, B, C //, D, ...
};
while (!queue.empty()) {
auto & event = queue.front();
EventType e = eventType(event); // Internally does MatchesPattern*
// and returns the match
switch (e) {
case A:
processA(event);
break;
case B:
processB(event);
Таким образом, вы четко отделяете соответствие от обработки, цикл просто диспетчер
Рассмотрим интерфейс:
class IMatchPattern
{
public:
virtual bool MatchesPattern(const Element& e) = 0;
};
Затем вы можете организовать контейнер объектов, реализующих IMatchPattern, чтобы обеспечить итеративный доступ к каждому методу сопоставления с шаблоном.
Вы можете изменить свой ExternalMatching
возвращать bool
, указывая, что обработка была выполнена. Таким образом, вызывающая сторона сможет продолжить оценку в случае необходимости:
bool ExternalMatching(...)
{
if (MatchesPatternB(Element) {
// Act on it
// Remove Element from queue
return true;
}
return false;
}
Теперь вы можете назвать это так:
if (ExternalMatchin1(...)) continue;
if (ExternalMatchin2(...)) continue;
...
if (ExternalMatchingN(...)) continue;
Хорошо, я закончил тем, что переписал цикл более похожий на это.
Огромная благодарность и благодарность Юсуши, dasblinkenlight, Дэвиду Родригесу за их помощь; этот ответ основан на комбинации их ответов.
bool ExternalMatching(...)
{
bool Match;
if ((Match = MatchesPatternX(Element))) {
// Act on it
} else if ((Match = MatchesPatternY(Element))) {
// Act on it
}
return Match;
}
while (!Queue.empty())
{
auto & Element = Queue.front();
if (MatchesPatternA(Element)) { // Highest priority, since it's first
// Act on it
} else if (MatchesPatternB(Element)) {
// Act on it
} else if (ExternalMatching(...)) {
} else if (MatchesPatternC(Element)) { // Lowest priority, since it's last
// Act on it
}
// Remove Element from queue
}
Теперь я знаю, что есть еще возможности для совершенствования, см. Ответы Матеуш Пуш и Майкла Ш. Тем не менее, это достаточно хорошо, чтобы ответить на мой первоначальный вопрос, и пока подойдет. Я рассмотрю вопрос об улучшении этого в будущем.
Если вам интересно увидеть реальный код (не упрощенная версия), смотрите здесь:
Спасибо всем еще раз!
Я хотел бы предложить функцию Factory, которая бы взяла Element и создала соответствующий обработчик и вернула указатель интерфейса на обработчик.
while (!Queue.empty())
{
auto & Element = *Queue.begin();
// get the appropriate handler object pointer e.g.
IPatternHandler *handler = Factory.GetHandler(Element);
handler->handle();
// clean up handler appropriately
}