ROS之pluginlib詳解

1. 寫在最前面

由於在一個源碼中, 關於在ROS平臺上的具體實現, 用到了Plugin. 爲了更加深入的理解到底是一個什麼鬼, 所以對pluginlib進行了一些學習, 下面的內容, 大部分在網站上是能夠找到的, 是綜合Wiki上的介紹, 以及從源碼中看到的內容和自己的嘗試. 希望對大家都一點點幫助.

pluginlib是一個使用C++實現的庫, 用於在ROS包裏面動態的加載或卸載plugin. plugin滿足一些條件的, 可以從運行庫(例如共享對象, 動態鏈接庫)中動態加載的類. Plugin在擴展或修改應用的行爲上很有優勢, 並不需要知道原始類的源碼, 也許你在編寫代碼時, 也並不知道你將會用到哪個plugin, 而是在運行時通過參數載入才確定具體的plugin, 在後面有一點相關的示例, 可以體會一下使用plugin的feeling(感覺很一般, 哈哈哈…).

2. 示例

想象一個場景, 假設現在有一個ROS包polygon_interface_package, 裏面包含一個polygon基類, 同時, 另外還有兩個不同的polygon, 一個是rectangle, 存在於rectangle_plugin包, 另外一個是triangle, 存在於triangle_plugin包. 我們希望rectangle和triangle都被系統支持.

2.1 registering/Exporting a Plugin

爲了能夠動態加載一個類, 那麼, 這個類必須是一個被標註的, 並且導入到系統的一個類. 這個過程, 需要使用宏PLUGINLIB_EXPORT_CLASS來完成. 一般情況下, 這個宏放在實現文件(.cpp)的末尾. 類似如下:

    #include <pluginlib/class_list_macros.h>
    #include <polygon_interface_package/polygon.h>
    #include <rectangle_package/rectangle.h>

    // 具體參數意義, 見下文
    PLUGINLIB_EXPORT_CLASS(rectangle_namespace::Rectangle, polygon_namespace::Polygon)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

2.2 The Plugin Description File

每個Plugin都需要由一個插件描述文件, 是一個XML格式機器可讀的文件. 包含一些必要的信息, 例如路徑, 插件類型, 插件名稱等. 類似如下:

    <library path="lib/librectangle">
      <class type="rectangle_namespace::Rectangle" base_class_type="polygon_namespace::Polygon">
      <description>
      This is a rectangle plugin
      </description>
      </class>
    </library>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

更多詳細的介紹, 可以查看: http://wiki.ros.org/pluginlib/PluginDescriptionFile

2.3 向ROS Package System中註冊插件

爲了使pluginlib能夠查詢到所有可用的插件, 需要在package.xml文件中添加export的tag塊. 類似如下:

    <export>
      <polygon_interface_package plugin="${prefix}/rectangle_plugin.xml" />
    </export>
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

一個值得注意的地方, 爲了使上述export命令正確執行, 還需要在build和run依賴項中添加如下信息:

    <build_depend>polygon_interface_package</build_depend>
    <run_depend>polygon_interface_package</run_depend>
  • 1
  • 2
  • 1
  • 2

2.4 Querying ROS Package System For Available Plugins

可以使用rospack命令來查詢, 就很簡單的一條命令, 類似如下:

    $ rospack plugins --attrib=plugin nav_core
  • 1
  • 1

將會返回所有nav_core包中導入的插件.

2.5 Step by Step

2.5.1 創建Plugin

首先, 當然是在自己的工作空間中創建一個用於嘗試的ROS Package. 依次輸入下述命令.

    $ roscd
    $ cd ../src
    $ catkin_create_pkg plugin_test roscpp pluginlib
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

OK, 現在可以開始寫代碼了. 在include/plugin_test文件夾下新建文件polygon_base.h, 將下述代碼拷貝進去, 申明我們的基類.

    #ifndef PLUGINLIB_TUTORIALS__POLYGON_BASE_H_
    #define PLUGINLIB_TUTORIALS__POLYGON_BASE_H_

    namespace polygon_base
    {
      class RegularPolygon
      {
        public:
          virtual void initialize(double side_length) = 0;
          virtual double area() = 0;
          virtual ~RegularPolygon(){}

        protected:
          RegularPolygon(){}
      };
    };
    #endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

在include/plugin_test文件夾下新建文件polygon_plugins.h, 將下述代碼拷貝進去, 申明我們的插件:

    #ifndef PLUGINLIB_TUTORIALS__POLYGON_PLUGINS_H_
    #define PLUGINLIB_TUTORIALS__POLYGON_PLUGINS_H_
    #include <plugin_test/polygon_base.h>
    #include <cmath>

    namespace polygon_plugins
    {
      class Triangle : public polygon_base::RegularPolygon
      {
        public:
          Triangle(){}

          void initialize(double side_length)
          {
            side_length_ = side_length;
          }

          double area()
          {
            return 0.5 * side_length_ * getHeight();
          }

          double getHeight()
          {
            return sqrt((side_length_ * side_length_) - ((side_length_ / 2) * (side_length_ / 2)));
          }

        private:
          double side_length_;
      };

      class Square : public polygon_base::RegularPolygon
      {
        public:
          Square(){}

          void initialize(double side_length)
          {
            side_length_ = side_length;
          }

          double area()
          {
            return side_length_ * side_length_;
          }

        private:
          double side_length_;

      };
    };
    #endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52

在src文件夾下創建文件polygon_plugins.cpp, 並拷貝下述代碼進去, 註冊我們的插件.

    #include <pluginlib/class_list_macros.h>
    #include <plugin_test/polygon_base.h>
    #include <plugin_test/polygon_plugins.h>

    PLUGINLIB_EXPORT_CLASS(polygon_plugins::Triangle, polygon_base::RegularPolygon)
    PLUGINLIB_EXPORT_CLASS(polygon_plugins::Square, polygon_base::RegularPolygon)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

在CMakeLists.txt文件中, 加入下述add_library申明. 值得注意的是, 在CMakeLists.txt文件中, 需要在include_directories中添加include目錄, 否則我們前面寫的兩個頭文件將會找不到.

    include_directories(
      ${catkin_INCLUDE_DIRS}
      include
    )

    ... ...

    add_library(polygon_plugins src/polygon_plugins.cpp)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

如前所述, 咱還需要編輯關於插件的信息內容, 在plugin_test主目錄下, 創建一個polygon_plugins.xml文件, 複製下述內容進入:

    <library path="lib/libpolygon_plugins">
      <class type="polygon_plugins::Triangle" base_class_type="polygon_base::RegularPolygon">
        <description>This is a triangle plugin.</description>
      </class>
      <class type="polygon_plugins::Square" base_class_type="polygon_base::RegularPolygon">
        <description>This is a square plugin.</description>
      </class>
    </library>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

在同目錄下的package.xml文件中export tag塊中添加下述內容:

      <plugin_test plugin="${prefix}/polygon_plugins.xml" />
  • 1
  • 1

在命令行中運行下述指令, 對應的輸出信息如下所示:

    $ rospack plugins --attrib=plugin plugin_test
    plugin_test /home/silence/WorkSpace/catkin_ws/src/plugin_test/polygon_plugins.xml
  • 1
  • 2
  • 1
  • 2

如果得到類似的輸出, 則表示所有都是沒問題的.

2.5.2 使用Plugin

2.5.2.1 基本使用體驗

在src目錄下, 新建polygon_loader.cpp文件, 用於測試, 複製下述內容:

    #include <pluginlib/class_loader.h>
    #include <plugin_test/polygon_base.h>

    int main(int argc, char** argv)
    {
      pluginlib::ClassLoader<polygon_base::RegularPolygon> poly_loader("plugin_test", "polygon_base::RegularPolygon");

      try
      {
        boost::shared_ptr<polygon_base::RegularPolygon> triangle = poly_loader.createInstance("polygon_plugins::Triangle");
        triangle->initialize(10.0);

        boost::shared_ptr<polygon_base::RegularPolygon> square = poly_loader.createInstance("polygon_plugins::Square");
        square->initialize(10.0);

        ROS_INFO("Triangle area: %.2f", triangle->area());
        ROS_INFO("Square area: %.2f", square->area());
      }
      catch(pluginlib::PluginlibException& ex)
      {
        ROS_ERROR("The plugin failed to load for some reason. Error: %s", ex.what());
      }

      return 0;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

並在CMakeLists.txt中加入下述申明, 然後$ catkin_make.

    add_executable(polygon_loader src/polygon_loader.cpp)
    target_link_libraries(polygon_loader ${catkin_LIBRARIES})
  • 1
  • 2
  • 1
  • 2

編譯成功後, 運行節點, 應該會得到下述類似的輸出.

    $ rosrun plugin_test polygon_loader
    [ INFO] [1477584281.637794959]: Triangle area: 43.30
    [ INFO] [1477584281.637923253]: Square area: 100.00
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

上述代碼都比較簡單, 就不做過多說明了.

2.5.2.2 另一種體驗

在src目錄下, 新建polygon_loader_v1.cpp文件, 用於測試, 複製下述內容:

    #include <pluginlib/class_loader.h>
    #include <plugin_test/polygon_base.h>

    #include <ros/ros.h>
    #include <sstream>
    #include <string>
    #include <vector>

    template <typename T>
    std::string to_string(T value) {
        std::ostringstream os ;
        os << value ;
        return os.str() ;
    }

    int main(int argc, char** argv)
    {
      ros::init(argc, argv, "polygon_loader_v1");
      ros::NodeHandle n_;

      std::vector<std::string> class_names;
      int class_index = 1;
      while (true)
      {
        std::string class_name;
        std::string param_name = std::string("polygon_loader_v1/derive_class_" + to_string(class_index++));
        if(!n_.getParam(param_name.c_str(), class_name))
            break;
        class_names.push_back(class_name);
      }

      if (class_names.empty()) {
        ROS_ERROR("ros parameter error");
        return 0;
      }

      pluginlib::ClassLoader<polygon_base::RegularPolygon> poly_loader("plugin_test", "polygon_base::RegularPolygon");

      try
      {

        for (class_index = 0; class_index < class_names.size(); ++class_index) {
          std::string class_name = class_names[class_index];
          boost::shared_ptr<polygon_base::RegularPolygon> plugin = poly_loader.createInstance(class_name);
          plugin->initialize(10.0);
          ROS_INFO("The polygon (%d / %d) area: %.2f", class_index + 1, class_names.size(), plugin->area());
        }
      }
      catch(pluginlib::PluginlibException& ex)
      {
        ROS_ERROR("The plugin failed to load for some reason. Error: %s", ex.what());
      }

      ROS_INFO("Waiting \"Ctrl + C\"");
      while (ros::ok()) {
        ;
      }

      return 0;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60

並在CMakeLists.txt中加入下述申明, 然後$ catkin_make.

    add_executable(polygon_loader_v1 src/polygon_loader_v1.cpp)
    target_link_libraries(polygon_loader_v1 ${catkin_LIBRARIES})
  • 1
  • 2
  • 1
  • 2

在plugin_test目錄下新建launch文件夾, 創建class_loader.launch文件, 複製下述內容.

    <launch>

        <!-- plugin params -->
        <param name="polygon_loader_v1/derive_class_1" value="polygon_plugins::Triangle" />
        <param name="polygon_loader_v1/derive_class_2" value="polygon_plugins::Square" />

        <node name="polygon_loader_v1"
             pkg="plugin_test" type="polygon_loader_v1" output="screen" />

    </launch>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

編譯成功後, 運行launch文件, 因爲在代碼中, 我們初始化了ros節點, 並且在launch文件中我們添加了ros parameter, 發起launch文件後, 會自動開啓roscore, 輸出會比較混亂, 應該會得到下述類似的輸出.

    $ roslaunch plugin_test class_loader.launch

    ... ...

    auto-starting new master
    process[master]: started with pid [16890]
    ROS_MASTER_URI=http://localhost:11311

    setting /run_id to bfd33a96-9cf8-11e6-a0bb-78acc03c5a93
    process[rosout-1]: started with pid [16903]
    started core service [/rosout]
    process[polygon_loader_v1-2]: started with pid [16906]
    [ INFO] [1477650287.294727014]: The polygon (1 / 2) area: 43.30
    [ INFO] [1477650287.294864723]: The polygon (2 / 2) area: 100.00
    [ INFO] [1477650287.294899464]: Waiting "Ctrl + C"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

上述代碼都比較簡單, 就不做過多說明了.

3. 進階

pluginlib的源碼見鏈接: https://github.com/ros/pluginlib . 打開include文件夾, 可以看得到, 其中僅包含少量的文件. 截圖如下:


include

3.1 class_list_macros.h

其結構很清晰, 其中幾個重要的宏申明於class_list_macros.h中, 定義如下:

    #include <class_loader/class_loader.h>
    /** 
     * @macro This version was deprecated in favor of PLUGINLIB_DECLARE_CLASS
     * @param - class_name - An alias for the class (no special characters allowed)  (IGNORED AS OF PLUGINLIB 1.9)
     * @param - class_type - The real class name with namespace qualifier (e.g. Animals::Lion)
     * @param - base_class_type - The real base class type from which class_type inherits
     */
    #define PLUGINLIB_REGISTER_CLASS(class_name, class_type, base_class_type) \
      CLASS_LOADER_REGISTER_CLASS_WITH_MESSAGE(class_type, base_class_type, "In file " __FILE__ " pluginlib WARNING: PLUGINLIB_REGISTER_CLASS is deprecated, please use PLUGINLIB_EXPORT_CLASS instead. You can run the script 'plugin_macro_update' provided with pluginlib in your package source folder to automatically and recursively update legacy macros. Base = base_class_type, Derived = derived_class_type")

    /** 
     * @macro This version is the most in use and requires package name in addition to fields in PLUGINLIB_REGISTER_CLASS 
     * @param - pkg - The package that exports the plugin (IGNORED AS OF PLUGINLIB 1.9)
     * @param - class_name - An alias for the class (no special characters allowed)  (IGNORED AS OF PLUGINLIB 1.9)
     * @param - class_type - The real class name with namespace qualifier (e.g. Animals::Lion)
     * @param - base_class_type - The real base class type from which class_type inherits
     */
    #define PLUGINLIB_DECLARE_CLASS(pkg, class_name, class_type, base_class_type) \
      CLASS_LOADER_REGISTER_CLASS_WITH_MESSAGE(class_type, base_class_type, "pluginlib WARNING: In file " __FILE__ " PLUGINLIB_DECLARE_CLASS is deprecated, please use PLUGINLIB_EXPORT_CLASS instead. You can run the script 'plugin_macro_update' provided with pluginlib in your package source folder to automatically and recursively update legacy macros.  Base = base_class_type, Derived = derived_class_type")

    /** 
     * @macro This version was only made possible with pluginlib 1.9 series. It's the easiest to use and now the official way of exporting classes.
     * @param - class_type - The real class name with namespace qualifier (e.g. Animals::Lion)
     * @param - base_class_type - The real base class type from which class_type inherits
     */
    #define PLUGINLIB_EXPORT_CLASS(class_type, base_class_type) \
      CLASS_LOADER_REGISTER_CLASS(class_type, base_class_type);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

3.2 class_desc.h

class_desc.h文件的定義如下, 其主要是用於保存前述xml文件中所編輯的關於plugin的內容.

      class ClassDesc
      {
        public:
          /**
           * @brief  Constructor for a ClassDesc
           * @param lookup_name The lookup name of the class 
           * @param derived_class The type of the derived class of the class
           * @param base_class The type of the class, corresponds to the type of the base class
           * @param package The package the class lives in
           * @param description A description for the class
           * @param library_name The name of the containing library for the class (not a full path!)
           * @param plugin_manifest_path The path to the plugin manifest file
           */
          ClassDesc(const std::string& lookup_name, const std::string& derived_class, const std::string& base_class, const std::string& package, 
              const std::string& description, const std::string& library_name, const std::string& plugin_manifest_path):
            lookup_name_(lookup_name), 
            derived_class_(derived_class),
            base_class_(base_class),
            package_(package),
            description_(description), 
            library_name_(library_name),
            resolved_library_path_("UNRESOLVED"),
            plugin_manifest_path_(plugin_manifest_path){}

          std::string lookup_name_;
          std::string derived_class_;
          std::string base_class_;
          std::string package_;
          std::string description_;
          std::string library_name_;
          std::string resolved_library_path_; //This is set by pluginlib::ClassLoader at load time
          std::string plugin_manifest_path_;
      };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

3.2 class_loader

除了上述兩部分外, 另外一個是與違例相關的類申明文件, pluginlib_exceptions.h. 當然, 其中最重要的部分, 是class loader. class_loader是pluginlib中最基礎的部分. 完成在運行時從運行庫中動態的加載已導入的C++類, 即插件, 以及創建這些類的對象. 一般而言, 當插件是爲了non-ROS package所創建的時, 應用使用class loader; 而將插件導入到ROS package時, 應該使用pluginlib. class loader主要提供兩個接口, 分別是class_loader::ClassLoader 和 class_loader::MultiLibraryClassLoader. 都提供了類似的接口, 不同之處在於, 前者僅能綁定單個庫, 而後者可以綁定多個庫. 基本應用示例如下:

    #include <class_loader/class_loader.h> // 包含頭文件
    #include "MyBase.h" // 引入已聲明的基類

    int main()
    {
      // 實例化一個class_loader::ClassLoader對象, 傳入參數是需要導入庫的完整路徑以及名稱
      class_loader::ClassLoader loader("libMyLibrary.so");
      // 獲取由基類MyBase所定義接口的類
      std::vector<std::string> classes = loader.getAvailableClasses<MyBase>();
      for(unsigned int c = 0; c < classes.size(); ++c)
      {
        boost::shared_ptr<MyBase> plugin = loader.createInstance<MyBase>(classes[c]);
        plugin->someMethod();
        // plugin是局部變量, 離開花括弧作用域後將會自動析構
      }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

值得說明的幾點:

  1. ClassLoader可以查詢到具有某個基類的插件(或類), 並且實例化這些插件, 前提條件是這些插件提前註冊並導入了, 即在實現文件末尾加入下述宏定義.

        CLASS_LOADER_REGISTER_CLASS(Derived, Base)
    • 1
    • 1
  2. 從上述示例用法中可以看到, 客戶端的代碼, 雖然不需要子類的具體定義, 但不可避免的是需要共同基類(即MyBase)的定義. 上述代碼中可以看到, 查詢可用插件和創建實例的兩個接口, 均是模板函數. 如果你所給定的基類不對, 是不能夠成功獲取到任何東西.

  3. 允許某個類多次申明, 並具有不同的基類申明

  4. class_loader::ClassLoader 和 class_loader::MultiLibraryClassLoader的所有接口都是線程安全的.

  5. 模板函數中的模板, 都是靜態判定的, 保證獲取到的接口是類型準確的. 不會出現加載到具有不兼容接口的無效插件.

最後, 如果對class loader的具體實現感興趣的朋友, 可以參看下述鏈接, http://wiki.ros.org/class_loader/Design , 並配合前面給出的Github上的源碼進行理解.


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章