零零碎碎 -- 继承与代理
2013-03-06 15:34 robturtle 阅读(260) 评论(0) 编辑 收藏 举报继上次尝试用 boost::any 容纳窗口部件失败后,使用了类的继承特性实现,这是昨天初始版本的一个快照:
ifndef WINDOWCOMPONENT_HPP #define WINDOWCOMPONENT_HPP #include <string> #include <memory> #include <list> using std::string; using std::shared_ptr; using std::list; #include "cppcommon.hpp" using namespace cliout; namespace cvoo { // OpenCV with OO window classes // A abstract of Window's components (e.g., trackbar, scrollbar) // Window's handler will show it or not according to needShowed() class WindowComponent { public: // notify window's handler create its resources virtual void Create(int flag = 0) = 0; // Visibility operations (implemented already) void Visible(bool stat) { showed = stat; } bool needShowed() const { return showed; } void FlipVisible() { showed = !showed; } WindowComponent() {} virtual ~WindowComponent() {} protected: // It's weired if you create a component to a window // and it doesn't show up by default bool showed = true; }; // Named Window is identified by its name class NamedWindowComponent: public WindowComponent { public: // if window with this name existed, create the component on it // Otherwise, it may create a new window (at OpenCV2/highgui) // or DO NOTHING (other name-based GUI) virtual void Create(const string & windowName, int flag = 0) = 0; const string & GetName() const {} NamedWindowComponent() {} virtual ~NamedWindowComponent() {} }; } // namespace #endif /*WINDOWCOMPONENT_HPP*/
然后窗口就可以用一个容器储存和管理其上的部件。这个时候问题来了,目前的设计没有办法支持对组件的分组操作。比如在播放器加入高斯滤波功能,并提供3个滚动条调整滤波参数,那么很自然地,用户一定会希望打开/关闭滤波功能时,这三个滚动条也同时显示或消失。为了实现这个功能,我想到了《C++沉思录》里的“代理”模式:用被代理类实现“节点”语义,用代理类实现“边”语义。大致构思如下:
class ComponentHandler; class WindowComponent { public: WindowComponent() {} virtual ~WindowComponent() {} void foo() { foo_self(); } protected: virtual void foo_self() = 0; }; class ComponentGroup: public WindowComponent { public: ComponentGroup() // intialize sub_components_ {} virtual ~ComponentGroup() {} void foo() { foo_self(); sub_components_->foo(); } protected: shared_ptr<ComponentHandler> sub_components_; }; class ComponentHandler { public: using item_t = shared_ptr<WindowComponent>; using container_t = std::container<item_t>; // for Example: std::container = std::map<string, item_t>; void foo() { for (auto sp : components_) sp->foo(); // if container == map, than it will be sp.second->foo() } protected: container_t components_; }
如此这般,只要在窗口内声明一个 ComponentHandler, 就可以创建出无限复杂的部件包含关系了。
第一次尝试失败了,因为我想把没有 ID 参数的 WindowComponent 和以字符串名字为 ID 的 NamedWindowComponent 统一在一起。这在写它们的句柄类的时候遇到了麻烦,我无法用统一的容器装载它们。然后我又一次尝试使用 boost::any ,很可惜的,std::map<boost::any, content_t> 却无法通过编译,似乎any不是一个可以比较的类。
第二次尝试化繁为简,因为这个类是用在 cv::namedWindow 的包装上的,所以干脆只考虑以字符串为ID这一种情况。然后,很快就写出了如下的内容:
WindowComponent.hpp
#ifndef WINDOWCOMPONENT_HPP #define WINDOWCOMPONENT_HPP #include <string> #include <memory> #include <map> using std::string; using std::shared_ptr; using std::make_shared; namespace cvoo { class NamedWindowComponent { protected: // You created it, you showed it. bool showed = true; public: NamedWindowComponent() {} virtual ~NamedWindowComponent() {} virtual void Create(const string & windowName, int flag = 0) { CreateSelf(windowName, flag); } virtual const string & GetName() const = 0; // Visibility control void Visible(bool stat) { showed = stat; } void FlipVisibility() { showed = !showed; } bool needShowed() { return showed; } protected: virtual void CreateSelf(const string & windowName, int flag = 0) = 0; }; class NamedWindowHandler { public: using item_t = shared_ptr<NamedWindowComponent>; using container_t = std::map<string, item_t>; protected: container_t components_; public: void AddComponent(item_t comp) { components_[comp->GetName()] = comp; } virtual void Create(const string & windowName, int flag = 0) { if (components_.size() == 0) return; for (auto pr : components_) pr.second->Create(windowName, flag); } }; class NamedComponentGroup: public NamedWindowComponent { public: using item_t = shared_ptr<NamedWindowComponent>; protected: shared_ptr<NamedWindowHandler> sub_components_; public: NamedComponentGroup() : sub_components_(make_shared<NamedWindowHandler>()) {} virtual ~NamedComponentGroup() {} virtual void Create(const string & windowName, int flag = 0) { CreateSelf(windowName, flag); sub_components_->Create(windowName, flag); } void AddComponent(item_t comp) { sub_components_->AddComponent(comp); } }; } // namespace #endif /*WINDOWCOMPONENT_HPP*/
然后用上面的声明包装 cv::namedWindow 和 cv::trackbar:
NamedWindow.hpp
#ifndef NAMEDWINDOW_HPP #define NAMEDWINDOW_HPP #include "opencv2/highgui/highgui.hpp" #include "WindowComponent.hpp" namespace cvoo { class NamedWindow: public NamedComponentGroup { protected: string win_name_; int flag_; public: NamedWindow(const string & windowName = "", int flag = 0) : win_name_(windowName) , flag_(flag) {} virtual ~NamedWindow() { cv::destroyWindow(win_name_); } const string & GetName() const { return win_name_; } // Special methods inline void Show() { Visible(true); Create(win_name_, flag_); } inline void Redraw() { Create(win_name_, flag_); } inline void Hide() { Visible(false); cv::destroyWindow(win_name_); } void Rename(const string & newName) { if (newName != win_name_) { cv::destroyWindow(win_name_); win_name_ = newName; Create(win_name_, flag_); } } void SetFlags(int flag) { flag_ = flag; } // Image Displayer void Display(const cv::Mat & img) { cv::imshow(win_name_, img); } protected: void CreateSelf(const string & windowName, int flag = 0) { cv::destroyWindow(win_name_); win_name_ = windowName; flag_ = flag; if (needShowed()) { cv::namedWindow(windowName, flag); } } }; } // namespace #endif /*NAMEDWINDOW_HPP*/
Trackbar.hpp
#ifndef TRACKBAR_HPP #define TRACKBAR_HPP #include "WindowComponent.hpp" #include "opencv2/highgui/highgui.hpp" namespace cvoo { class Trackbar: public NamedWindowComponent { public: using callback_t = void (*)(int, void*); using callback_data_t = void *; private: string name_; string win_name_cache_ = ""; int * ptr_pos_; int max_pos_; callback_t callback_; callback_data_t data_; public: Trackbar(const string & trackbarName, int * pPos, int maxPos, callback_t callback = 0, callback_data_t callback_data = 0) : name_(trackbarName) , ptr_pos_(pPos) , max_pos_(maxPos) , callback_(callback) , data_(callback_data) {} ~Trackbar() {} // no derived const string & GetName() const { return name_; } // Special methods void SetCallback(callback_t callback, callback_data_t data) { callback_ = callback; data_ = data; if (!win_name_cache_.empty()) { // because flag can only influence sub components // so it's no need to store or pass flags CreateSelf(win_name_cache_); } } // Other Accessors const string & GetWindowName() const { return win_name_cache_; } int GetPos() const { return *ptr_pos_; } int GetMaxPos() const { return max_pos_; } callback_t GetCallback() const { return callback_; } callback_data_t GetCallbackData() const { return data_; } protected: void CreateSelf(const string & windowName, int flag = 0) { win_name_cache_ = windowName; if (needShowed()) { cv::createTrackbar(name_, windowName, ptr_pos_, max_pos_, callback_, data_); } if (callback_) callback_(GetPos(), data_); } }; } // namespace #endif /*TRACKBAR_HPP*/
最后赞美一下 Boost 的 unit test framework, 上面有好些缺陷都是在做单元测试时找到的,比如在重新创建滚动条后立刻调用回调函数等等。