1
0
Fork 0

decouple address matching from message dispatching

This commit is contained in:
Kevin Matz 2023-05-16 09:52:18 -04:00
parent 0398b63907
commit 7e679f3016
3 changed files with 104 additions and 101 deletions

View File

@ -59,18 +59,6 @@ void Method::addChild(Method *child)
}
/**
* @brief Method::address
* @return
*/
std::string Method::address() const
{
if (!parent_)
return name_; // root container
return parent_->address() + "/" + name_;
}
/**
* @brief Method::name
* @return
@ -96,87 +84,6 @@ bool Method::setName(std::string name)
}
/**
* @brief Method::dispatch
* @param patterns
* @param msg
* @return
*/
bool Method::dispatch(std::list<std::string> patterns, const std::shared_ptr<Message> msg) const
{
if (patterns.empty())
return false; // NO pattern to match
auto name = patterns.front();
if (!isRoot())
patterns.pop_front(); // take the first pattern, if not the root method
if (patterns.empty()) // LEAF pattern
{
if (isContainer()) // containters cannot be leafs
return false;
if (matchName(name)) // accept the message
{
trigger(msg); // activate method
return true;
}
}
else // BRANCH pattern
{
if (!isContainer()) // no children to match to
return false;
if (matchName(name)) // accept pattern
{
for (const auto &child: children_) // offer the remaining patterns to children
child->dispatch(std::list<std::string>(patterns), msg);
return true;
}
else
{
if (name.empty()) // empty patterns, ie "//", apply to all (depth search)
{
if (!dispatch(std::list<std::string>(patterns), msg)) // try again with the remaining patterns
{
patterns.push_front(name); // return the empty pattern to the list
for (const auto &child: children_) // offer full search depth to children
child->dispatch(std::list<std::string>(patterns), msg);
return true;
}
}
}
}
return false; // reject the message
}
/**
* @brief Method::onTrigger
* @param cb
* @return
*/
std::shared_ptr<void> Method::onTrigger(const std::function<void(std::shared_ptr<Message>)> cb)
{
// wrap the callback with a shared pointer
auto sp = std::make_shared<std::function<void(std::shared_ptr<Message>)>>(std::move(cb));
// add callback to list (as a weak pointer)
cb_trigger.push_back(sp);
// return token that caller must keep throughout it's scope
return sp;
}
/**
* @brief Method::trigger
* @param msg
*/
void Method::trigger(std::shared_ptr<Message> msg) const
{
for (const auto &wp: cb_trigger)
if (auto sp = wp.lock()) // the owner is still holding the token
(*sp)(msg);
}
/**
* @brief Method::matchName
* @param pattern
@ -199,6 +106,99 @@ bool Method::matchName(const std::string &pattern) const
}
/**
* @brief Method::address
* @return
*/
std::string Method::address() const
{
if (!parent_)
return name_; // root container
return parent_->address() + "/" + name_;
}
/**
* @brief Method::matchAddress
* @param hits
* @param pattern
* @return
*/
bool Method::matchAddress(std::vector<const Method *> &hits, std::list<std::string> pattern) const
{
if (pattern.empty()) // NO pattern to match
return false;
auto name = pattern.front(); // pattern token to consider
if (!isRoot())
pattern.pop_front(); // take the token, if not the root method
if (pattern.empty()) // LEAF pattern
{
if (isContainer()) // containters cannot be leafs
return false;
if (matchName(name)) // token matches this method
{
hits.push_back(this); // claim pattern
return true;
}
} // end LEAF pattern
else // BRANCH pattern
{
if (!isContainer()) // no children to match to
return false;
if (matchName(name)) // token matches this method
{
for (const auto &child: children_) // offer the remaining pattern to children
child->matchAddress(hits, pattern);
return true;
}
else // token does not match this method
{
if (name.empty()) // empty patterns, ie "//", apply to all (depth search)
{
if (!matchAddress(hits, pattern)) // try again with the remaining pattern (depth reached?)
{
pattern.push_front(name); // return the empty pattern to the list
for (const auto &child: children_) // offer full search depth to children
child->matchAddress(hits, pattern);
return true;
}
}
}
} // end BRANCH pattern
return false; // pattern does not match this method
}
/**
* @brief Method::trigger
* @param msg
*/
void Method::trigger(std::shared_ptr<Message> msg) const
{
for (const auto &wp: cb_trigger)
if (auto sp = wp.lock()) // the owner is still holding the token
(*sp)(msg);
}
/**
* @brief Method::onTrigger
* @param cb
* @return
*/
std::shared_ptr<void> Method::onTrigger(const std::function<void(std::shared_ptr<Message>)> cb)
{
// wrap the callback with a shared pointer
auto sp = std::make_shared<std::function<void(std::shared_ptr<Message>)>>(std::move(cb));
// add callback to list (as a weak pointer)
cb_trigger.push_back(sp);
// return token that caller must keep throughout it's scope
return sp;
}
/**
* @brief Method::isRoot
* @return

View File

@ -44,17 +44,17 @@ public:
void addChild(Method *child);
std::string address() const;
std::string name() const;
bool setName(std::string name);
bool matchName(const std::string &pattern) const;
bool dispatch(std::list<std::string> patterns, const std::shared_ptr<Message> msg) const;
std::string address() const;
bool matchAddress(std::vector<const Method *> &hits, std::list<std::string> pattern) const;
void trigger(std::shared_ptr<Message> msg) const;
std::shared_ptr<void> onTrigger(const std::function<void(std::shared_ptr<Message>)>);
protected:
void trigger(std::shared_ptr<Message>) const;
bool matchName(const std::string &pattern) const;
bool isRoot() const;
bool isContainer() const;
@ -62,7 +62,6 @@ private:
Method *parent_;
std::vector<Method*> children_;
std::string name_;
std::vector<std::weak_ptr<const std::function<void(std::shared_ptr<Message>)>>> cb_trigger;
};

View File

@ -49,12 +49,16 @@ void Receiver::dispatch(const std::shared_ptr<Message> msg) const
switch (msg->address_pattern.at(0)) {
case '/':
{
std::list<std::string> patterns;
std::list<std::string> pattern;
std::istringstream strm(msg->address_pattern);
strm.seekg(1); // skip leading '/'
for (std::string name; std::getline(strm, name, '/');)
patterns.push_back(name);
address_space_->dispatch(patterns, msg);
pattern.push_back(name);
std::vector<const Method *> hits;
address_space_->matchAddress(hits, pattern);
for (const auto hit: hits)
hit->trigger(msg);
}
break;
case '#':