2018-04-25

总结

  • 签名导出:使用extern "C"导出函数对函数名有约束,也可以使用struct包装所有函数签名
  • 版本控制:可以参考Android SDK,引入minVersionmaxVersion和targetVersion
  • 元数据:可以保存在插件代码中,也可以保存在元数据中比如xml
  • 插件管理器:利用前向声明提供类型信息,但是管理器失去了通用性,会与API的信息耦合

plugin.h

#ifndef PLUGIN_H
#define PLUGIN_H

typedef void (*PluginOpen)();
typedef void (*PluginClose)();

struct PluginSpec {
  PluginOpen Open;
  PluginClose Close;
};

#endif

core.cpp

#include <dlfcn.h>
#include <boost/property_tree/xml_parser.hpp>
#include <fstream>
#include <iostream>
#include <map>
#include <string>
#include "plugin.h"

const std::string PLUGIN_SPEC{"PLUGIN_SPEC"};
const int kCoreVersion{3};

class PluginManager {
 public:
  static constexpr PluginSpec kEmpty{nullptr, nullptr};

  int LoadFromXml(std::string name) {
    using namespace boost::property_tree;
    ptree root;
    read_xml(name, root);

    try {
      auto plugin_nodes = root.get_child("plugins");

      // 遍历plugin列表
      for (auto it = plugin_nodes.begin(); it != plugin_nodes.end(); ++it) {
        //
        auto name = it->second.get<std::string>("name");
        auto min_version = it->second.get<int32_t>("minVersion");
        auto max_version = it->second.get<int32_t>("maxVersion");

        // 校验版本
        if (min_version > kCoreVersion) {
          std::cerr << "LoadFromXml:Failed to validate min version:"
                    << "name=" << name << ",core_version=" << kCoreVersion
                    << ",min_version=" << min_version << std::endl;
        } else if (max_version < kCoreVersion) {
          std::cerr << "LoadFromXml:Failed to validate max version:"
                    << "name=" << name << ",core_version=" << kCoreVersion
                    << ",max_version=" << max_version << std::endl;
        } else if (min_version <= kCoreVersion && kCoreVersion <= max_version) {
          std::cout << "LoadFromXml:"
                    << "name=" << name << ",min_version=" << min_version
                    << ",max_version=" << max_version << std::endl;

          auto pathname = "./" + name + "_plugin.so";
          auto handle = dlopen(pathname.c_str(), RTLD_NOW);

          // 打开失败
          if (handle == nullptr) {
            std::cerr << "LoadFromXml:Failed to open plugin file:" << pathname
                      << std::endl;
            continue;
          }

          // 注册PluginSpec
          PluginSpec *plugin = reinterpret_cast<PluginSpec *>(
              dlsym(handle, PLUGIN_SPEC.c_str()));
          plugins[name] = *plugin;
        } else {
        }
      }
    } catch (std::exception &ex) {
      std::cerr << "LoadFromXml:Failed to parse file:" << ex.what()
                << std::endl;
      return -1;
    }
    return 0;
  }

  int GetPlugin(const std::string &name, PluginSpec *plugin) {
    if (plugins.find(name) != plugins.end()) {
      *plugin = plugins[name];
      return 0;
    }
    return -1;
  }

  std::map<std::string, PluginSpec> ListPlugins() const { return plugins; }

 private:
  std::map<std::string, PluginSpec> plugins;
};

int main() {
  PluginManager pm;
  pm.LoadFromXml("./plugin.xml");

  auto plugins = pm.ListPlugins();
  for (auto &plugin : plugins) {
    plugin.second.Open();
  }

  for (auto &plugin : plugins) {
    plugin.second.Close();
  }
  return 0;
}

plugin.xml

<plugins>
  <plugin>
    <name>hello</name>
    <minVersion>2</minVersion>
    <maxVersion>4</maxVersion>
  </plugin>
  <plugin>
    <name>world</name>
    <minVersion>2</minVersion>
    <maxVersion>3</maxVersion>
  </plugin>
</plugins>

hello_plugin.cpp && world_plugin.cpp

#include <iostream>
#include "plugin.h"

void Open() { std::cout << "HelloOpen" << std::endl; }

void Close() { std::cout << "HelloClose" << std::endl; }

PluginSpec PLUGIN_SPEC{Open, Close};
#include <iostream>
#include "plugin.h"

void Open() { std::cout << "WorldOpen" << std::endl; }

void Close() { std::cout << "WordClose" << std::endl; }

PluginSpec PLUGIN_SPEC{Open, Close};

Makefile

CXX_FLAGS=-g -std=c++11

all: main plugins

main:
    g++ $(CXX_FLAGS) core.cpp plugin.h -o core -ldl

plugins: 
    g++ $(CXX_FLAGS) -fPIC -shared hello_plugin.cpp plugin.h -o hello_plugin.so
    g++ $(CXX_FLAGS) -fPIC -shared world_plugin.cpp plugin.h -o world_plugin.so

.PHONY: all
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容