美文网首页
C++11 适配器模式示例

C++11 适配器模式示例

作者: FredricZhu | 来源:发表于2022-04-24 15:06 被阅读0次

    本例来自于Design Pattern in Modern C++一书的适配器模式一节。但是作者的代码是基于MFC绘图的。MFC绑定Windows平台,在Mac和Linux平台上无法使用。本人使用OpenCV重写绘图部分。运行成功。
    但是有点值得注意的是,本文中Drawer的draw方法不是一个回调方法。所以本例中的LineToCacheAdapter类,其实作用不大,MFC在CDialog类或者CView类隐藏后再次显示时,可能会调用OnPaint回调方法重绘窗体。所以缓存效果很明显,就是减少无必要的Points计算。
    但是本例使用的OpenCV,没有找到重绘窗体的回调。所以不需要刷新窗体。基本上缓存意义不大,除非你画了两条一模一样的线。我的意思是起点和终点相同。
    例子的思路其实也很简单。
    把原先不能接收线对象的方法,改用LineToPointAdapter进行适配,适配成点集合,这样就可以画点了。
    程序目录结构如下,


    image.png

    test/CMakeLists.txt

    cmake_minimum_required(VERSION 2.6)
    project(adapter_caching)
    
    set(CMAKE_CXX_STANDARD 20)
    add_definitions(-g)
    
    
    find_package(Boost REQUIRED COMPONENTS
        system
        filesystem
        serialization
        program_options
        thread
        )
    
    find_package(OpenCV REQUIRED )
    find_package(glog REQUIRED)
    
    include_directories(${Boost_INCLUDE_DIRS} /usr/local/include /usr/local/include/opencv4 /usr/local/iODBC/include /opt/snowflake/snowflakeodbc/include/ ${CMAKE_CURRENT_SOURCE_DIR}/../../ ${CMAKE_CURRENT_SOURCE_DIR}/../include/ ${CMAKE_CURRENT_SOURCE_DIR}/../../../include/)
    
    LINK_DIRECTORIES(/usr/local/lib /usr/local/iODBC/lib /opt/snowflake/snowflakeodbc/lib/universal)
    
    file( GLOB APP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../impl/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/*.h ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../../include/death_handler/impl/*.cpp) 
    foreach( sourcefile ${APP_SOURCES} )
            file(RELATIVE_PATH filename ${CMAKE_CURRENT_SOURCE_DIR} ${sourcefile})
        
            string(FIND "${filename}"  "test.cpp" "TEMP")
        if( NOT "${TEMP}" STREQUAL "-1" )
            string(REPLACE ".cpp" "" file ${filename})
            add_executable(${file}  ${APP_SOURCES})
            target_link_libraries(${file} ${Boost_LIBRARIES} ${OpenCV_LIBS})
            target_link_libraries(${file} glog::glog ssl crypto libgtest.a libgmock.a iodbc iodbcinst libnanodbc.a pthread)
        endif()
    endforeach( sourcefile ${APP_SOURCES})
    

    test/rect_draw_test.cpp

    #include "death_handler/death_handler.h"
    #include <glog/logging.h>
    #include "rect_draw_cache.hpp"
    
    #include <utility>
    #include <gtest/gtest.h>
    
    
    
    int main(int argc, char** argv) {
        FLAGS_log_dir = "./";
        FLAGS_alsologtostderr = true;
        // 日志级别 INFO, WARNING, ERROR, FATAL 的值分别为0、1、2、3
        FLAGS_minloglevel = 0;
    
        Debug::DeathHandler dh;
    
        google::InitGoogleLogging("./logs.log");
        testing::InitGoogleTest(&argc, argv);
        int ret = RUN_ALL_TESTS();
        return ret;
    }
    
    GTEST_TEST(MultitonTests, Multiton) {
        DrawableObjects objs {
            std::make_shared<VectorRectangle>(10,10,100,100),
            std::make_shared<VectorRectangle>(30,30,60,60)
        };
        Drawer<LineToPointCacheAdapter> d{objs};
        d.draw();
        d.wait_to_dispose();
    }
    

    include/geometry.h

    #ifndef _FREDRIC_GEOMETRY_H_
    #define _FREDRIC_GEOMETRY_H_
    
    #include <boost/functional/hash.hpp>
    
    struct Point {
        int x, y;
    
        friend std::size_t hash_value(Point const& obj) {
            std::size_t seed = 0x725C686F;
            boost::hash_combine(seed, obj.x);
            boost::hash_combine(seed, obj.y);
            return seed;
        }
    };
    
    struct Line {
        Point start, end;
    
        friend std::size_t hash_value(Line const& obj) {
            std::size_t seed = 0x719E6B16;
            boost::hash_combine(seed, obj.start);
            boost::hash_combine(seed, obj.end);
            return seed;
        }
    };
    
    struct VectorObject {
        virtual std::vector<Line>::iterator begin() = 0;
        virtual std::vector<Line>::iterator end() = 0;
    };
    
    struct VectorRectangle: VectorObject {
        VectorRectangle(int x, int y, int width, int height) {
            lines.emplace_back(Line{Point{x, y}, Point{x+width, y}});
            lines.emplace_back(Line{Point{x+width, y}, Point{x+width, y+height}});
            lines.emplace_back(Line{Point{x, y}, Point{x, y+height}});
            lines.emplace_back(Line{Point{x, y+height}, Point{x+width, y+height}});
        }
    
        std::vector<Line>::iterator begin() {
            return lines.begin();
        }
    
        std::vector<Line>::iterator end() {
            return lines.end();
        }
    
    private:
        std::vector<Line> lines;
    };
    #endif
    

    include/rect_draw_cache.hpp

    #ifndef _FREDRIC_RECT_DRAW_CACHE_HPP_
    #define _FREDRIC_RECT_DRAW_CACHE_HPP_
    
    #include "geometry.h"
    #include <opencv2/opencv.hpp>
    
    #include <glog/logging.h>
    #include <iostream>
    #include <memory>
    #include <map>
    #include <string>
    #include <concepts>
    #include <vector>
    
    std::string const window_name = "Draw Points";
    using DrawableObjects = std::vector<std::shared_ptr<VectorObject>>;
    using Points = std::vector<Point>;
    int const EXIT_KEY = 27;
    
    struct LineToPointAdapter {
        LineToPointAdapter(Line& line){
            static int count = 0;
            LOG(INFO) << ++count << ": Generating points for line (no caching)\n";
             // no interpolation
            int left = std::min(line.start.x, line.end.x);
            int right = std::max(line.start.x, line.end.x);
            int top = std::min(line.start.y, line.end.y);
            int bottom = std::max(line.start.y, line.end.y);
            int dx = right - left;
            int dy = bottom - top;
    
            // only vertical or horizontal lines
            if (dx == 0){
                // vertical
                for (int y = top; y <= bottom; ++y){
                    points.emplace_back(Point{ left,y });
                }
                // horizontal
            } else if (dy == 0) {
                for (int x = left; x <= right; ++x) {
                    points.emplace_back(Point{ x, top });
                }
            }
        }
    
        Points::iterator begin() {
            return points.begin();
        }
    
        Points::iterator end() {
            return points.end();
        }
    
    private:
        Points points;
    };
    
    
    struct LineToPointCacheAdapter {
        LineToPointCacheAdapter(Line& line){
            // functional objects
            boost::hash<Line> hash;
            line_hash = hash(line);
            // find it, don't need to calculate it again
            if(cache.find(line_hash) != cache.end()) {
                return;
            }
    
            static int count = 0;
            LOG(INFO) << ++count << ": Generating points for line (caching)\n";
    
            Points points;
             // no interpolation
            int left = std::min(line.start.x, line.end.x);
            int right = std::max(line.start.x, line.end.x);
            int top = std::min(line.start.y, line.end.y);
            int bottom = std::max(line.start.y, line.end.y);
            int dx = right - left;
            int dy = bottom - top;
    
            // only vertical or horizontal lines
            if (dx == 0){
                // vertical
                for (int y = top; y <= bottom; ++y){
                    points.emplace_back(Point{ left,y });
                }
                // horizontal
            } else if (dy == 0) {
                for (int x = left; x <= right; ++x) {
                    points.emplace_back(Point{ x, top });
                }
            }
            // not find, cache it 
            cache[line_hash] = points;
        }
    
        Points::iterator begin() {
            return cache[line_hash].begin();
        }
    
        Points::iterator end() {
            return cache[line_hash].end();
        }
    
    private:
        std::size_t line_hash;
        static std::map<std::size_t, Points> cache;
    
    };
    
    std::map<std::size_t, Points> LineToPointCacheAdapter::cache {};
    
    template <typename AdapterType>
    struct Drawer {
        Drawer(DrawableObjects const& vector_objs_): vector_objs{vector_objs_} {
            cv::namedWindow(window_name, cv::WINDOW_NORMAL);
            cv::resizeWindow(window_name, 200, 150);
            img_ = cv::Mat::zeros(cv::Size(200, 150), CV_8UC1);
            img_.setTo(cv::Scalar(255));
            cv::imshow(window_name, img_);
            cv::setWindowProperty(window_name, cv::WND_PROP_TOPMOST, 1);
        }
    
        void wait_to_dispose() {
            while(EXIT_KEY != cv::waitKey(1000)) {
            }
        }
    
        void draw() {
            for(auto& o: vector_objs) {
                for(auto& l: *o) {
                    AdapterType lpo{l};
                    draw_points(lpo.begin(), lpo.end());
                }
            }
        }
    
    private:
        DrawableObjects vector_objs;
        cv::Mat img_;
    
        void draw_points(Points::iterator start, Points::iterator end) {
            for(auto i=start; i!=end; ++i) {
                cv::circle(img_, cv::Point(i->x, i->y), 3, cv::Scalar(0), cv::FILLED, cv::LINE_8);
            }
            cv::imshow(window_name, img_);
        }
    };
    #endif
    

    程序输出如下,


    image.png
    image.png

    相关文章

      网友评论

          本文标题:C++11 适配器模式示例

          本文链接:https://www.haomeiwen.com/subject/apptyrtx.html