BT10:Logger类实现原理解析(单例与观察者模式)

编程入门 行业动态 更新时间:2024-10-24 14:28:14

BT10:Logger类实现原理解析(单例与<a href=https://www.elefans.com/category/jswz/34/1765125.html style=观察者模式)"/>

BT10:Logger类实现原理解析(单例与观察者模式)

大家好,欢迎大家关注我的知乎专栏慢慢悠悠小马车 


各种调试工具介绍、MinitraceLogger、PublisherZMQ,都只能创建1个实例。像是单例模式,却不是典型的令构造函数是private的实现方式,而是通过限定构造函数的执行次数实现的。

不同的logger都是监控node的状态变化,在恰当的时机执行对应的打印、发送、保存等任务,这是通过观察者模式实现的。

本文从这2个实现方法展开介绍。

基类

所有Logger都继承自StatusChangeLogger,该类定义在 BehaviorTree.CPP/include/behaviortree_cpp_v3/loggers/abstract_logger.h,是一个纯虚基类。

// 所有logger的基类,纯虚基类
class StatusChangeLogger {public:StatusChangeLogger(TreeNode* root_node);virtual ~StatusChangeLogger() = default;// 当node发生状态变化时要执行的操作virtual void callback(BT::Duration timestamp, const TreeNode& node,NodeStatus prev_status, NodeStatus status) = 0;// 保存或发送数据virtual void flush() = 0;...private:std::vector<TreeNode::StatusChangeSubscriber> subscribers_;...
};

在 StatusChangeLogger的构造函数中,遍历树的节点,为其绑定回调函数,即不同logger所实现的callback()函数,来实现具体的打印、发送、保存等操作。

inline StatusChangeLogger::StatusChangeLogger(TreeNode* root_node) {first_timestamp_ = std::chrono::high_resolution_clock::now();// 对回调函数callback()的封装,执行配置选项对应的callback()auto subscribeCallback = [this](TimePoint timestamp, const TreeNode& node,NodeStatus prev, NodeStatus status) {if (enabled_ && (status != NodeStatus::IDLE || show_transition_to_idle_)) {if (type_ == TimestampType::ABSOLUTE) {  // 真正的回调操作this->callback(timestamp.time_since_epoch(), node, prev, status);} else {this->callback(timestamp - first_timestamp_, node, prev, status);}}};// 增加订阅者,绑定回调函数auto visitor = [this, subscribeCallback](TreeNode* node) {subscribers_.push_back(node->subscribeToStatusChange(std::move(subscribeCallback)));};// 遍历树的所有节点applyRecursiveVisitor(root_node, visitor);
}

 StatusChangeSignal应用了观察者模式。

using StatusChangeSignal = Signal<TimePoint, const TreeNode&, NodeStatus, NodeStatus>;
/*** @brief subscribeToStatusChange is used to attach a callback to a status change.* When StatusChangeSubscriber goes out of scope (it is a shared_ptr) the callback* is unsubscribed automatically.     * @param callback The callback to be execute when status change.* @return the subscriber handle.*/
TreeNode::StatusChangeSubscriber
TreeNode::subscribeToStatusChange(TreeNode::StatusChangeCallback callback) {return state_change_signal_.subscribe(std::move(callback));
}//Call the visitor for each node of the tree, given a root.
void applyRecursiveVisitor(TreeNode* root_node, const std::function<void(TreeNode*)>& visitor);

子类

以StdCoutLogger为例,其他子类仅callback()实现的操作不同而已,调用逻辑是一致的。

class StdCoutLogger : public StatusChangeLogger {static std::atomic<bool> ref_count;  // 原子类型public:StdCoutLogger(const BT::Tree& tree);~StdCoutLogger() override;virtual void callback(Duration timestamp, const TreeNode& node,NodeStatus prev_status, NodeStatus status) override;virtual void flush() override;
};

 单例的重点在于利用原子变量ref_count的值变化,来监控调用构造函数的次数。这个语法知识可以参考:atomic变量-compare_exchange用法

std::atomic<bool> StdCoutLogger::ref_count(false);StdCoutLogger::StdCoutLogger(const BT::Tree& tree): StatusChangeLogger(tree.rootNode()) {bool expected = false;// 如果expected==ref_count,就令ref_count=true(第2个参数),并返回true;// 否则,将ref_count的值赋给expected,并返回false// 即,ref_count初始化为false。第1次执行构造函数时,ref_count会被置为true,并返回true// 后面再进入构造函数时,expected!=ref_count,会抛出异常。if (!ref_countpare_exchange_strong(expected, true)) {throw LogicError("Only one instance of StdCoutLogger shall be created");}
}
StdCoutLogger::~StdCoutLogger() { ref_count.store(false); }

观察者模式

TreeNode的状态变化,都要经由setStatus()函数实现。在该函数内,会在NodeStatus变化时,通知订阅了该消息的订阅者。

void TreeNode::setStatus(NodeStatus new_status) {NodeStatus prev_status;{std::unique_lock<std::mutex> UniqueLock(state_mutex_);prev_status = status_;status_ = new_status;}if (prev_status != new_status) {// 当状态变化时,条件变量通知其他等待者state_condition_variable_.notify_all();// 通知该Signal的订阅者state_change_signal_.notify(std::chrono::high_resolution_clock::now(),*this, prev_status, new_status);}
}

notify() 通知到位的同时,会执行订阅者绑定的callback()。

template <typename... CallableArgs>
class Signal {public:using CallableFunction = std::function<void(CallableArgs...)>;using Subscriber = std::shared_ptr<CallableFunction>;void notify(CallableArgs... args) {for (size_t i = 0; i < subscribers_.size();) {if (auto sub = subscribers_[i].lock()) {(*sub)(args...);  // 执行callbacki++;} else {subscribers_.erase(subscribers_.begin() + i);}}}// 添加一个订阅者Subscriber subscribe(CallableFunction func) {Subscriber sub = std::make_shared<CallableFunction>(std::move(func));subscribers_.emplace_back(sub);return sub;}private:std::vector<std::weak_ptr<CallableFunction>> subscribers_;
};

更多推荐

BT10:Logger类实现原理解析(单例与观察者模式)

本文发布于:2024-03-24 00:09:38,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1744476.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:观察者   原理   模式   Logger

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!