用 Boost.extension 做C/C++插件

转自:

http://boost-extension.redshoelace.com/docs/boost/extension/boost_extension/tutorials.html#boost_extension.tutorials.tutorial01

http://remonstrate.wordpress.com/2011/01/16/%E5%88%A9%E7%94%A8-extension-%E4%B8%8E-reflection-%E6%9B%B4%E5%A5%BD%E7%9A%84%E4%BD%BF%E7%94%A8%E5%8A%A8%E6%80%81%E9%93%BE%E6%8E%A5%E5%BA%93/


boost 的 extension 是建立在 reflection 之上的对动态链接库的支持。

C++ 和动态链接库之间存在的最大问题是由于函数名重载需要引入函数名 mangling,比如 f(int) 与 f(float) 被 mangle 之后就成为了 f_i 和 f_f 两个不同的 symbol,动态链接程序提供的接口函数,只支持 C 类型的查询,即可以用 dlsym 使用字符串获得需要的函数指针。这样如果希望借助动态链接库创建需要的对象,必须要依赖于一些声明为 extern “C” 的函数,用于创建需要的对象。这也就是为人所称为 factory method 的 design pattern。这在插件技术中最为常见:主程序通过一个固定的接口与动态链接库进行交互,然后通过某种规则动态的载入需要的 plugin(即一个动态链接库),通过接口完成需要的任务。这样第三方开发者可以实现自己私有的插件(adobe 的不少产品中这种技术非常普遍,如 photoshop 的 filter,不同文件格式的支持)。

boost 的 extension 是基于 reflection 的一个库,其目的是方便用户创建 C++ 的动态链接库,特别的为我们提供了很好的 factory method 实现,通过 reflection 和 RTTI 让我们能够更加便捷的实现插件。下面我们通过 extension 提供的三个例子了解 extension 的各种功能。

从动态链接库导出一般函数

这是库函数

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
  * @file libhello.cpp
  */
#include <iostream>
#include <boost/extension/extension.hpp>
 
extern "C"
void BOOST_EXTENSION_EXPORT_DECL
boost_extension_hello_world( int repetitions) {
   for ( int i = 0; i < repetitions; ++i) {
     std::cout << "Hello World" << std::endl;
   }
}

注意这里提供的宏 BOOST_EXTENSION_EXPORT_DECL 会被替换成为不同平台下需要的东西。调用这个库函数的代码如下

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
/**
  * @file hello.cpp
  */
#include <iostream>
#include <boost/extension/shared_library.hpp>
#include <boost/function.hpp>
 
int
main ( int argc, char * argv[] ) {
   using namespace boost::extensions;
 
   std::string library_path = "libhello.so" ;
   shared_library lib(library_path);
 
   if (!lib.open()) {
     std::cerr << "Library failed to open: " << library_path << std::endl;
     return 1;
   }
 
   boost::function< void ( int )>
     f(lib.get< void , int >( "boost_extension_hello_world" ));
 
   if (!f) {
     std::cerr << "Function not found!" << std::endl;
     return 1;
   }
 
   f(4);
 
   return 0 ;
}

这里使用了 shared_library 类用于操作一般的动态链接库,其 get 方法可以获得类型安全的函数指针。

使用 factory method 从动态链接库中导出类

首先是公用的接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#ifndef _ANIMAL_HPP_
#define _ANIMAL_HPP_
/**
  * @file animal.hpp
  */
 
class animal {
public :
   animal( int age) : age_(age) {
   }
   virtual ~animal( void ) {
   }
   virtual std::string get_name( void ) {
     return "A generic animal" ;
   }
   int get_age( void ) {
     return age_;
   }
protected :
   int age_;
};
 
#endif

我们将通过 animal 接口操纵各种 animal。下面是库的源文件:

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
/**
  * @file libanimal.cpp
  */
 
#include <iostream>
#include <map>
 
#include <boost/extension/extension.hpp>
#include <boost/extension/factory.hpp>
#include <boost/extension/type_map.hpp>
 
#include "animal.hpp"
 
class puma : public animal {
  public :
   puma( int age) : animal(age) {}
   virtual std::string get_name() {
     return "puma" ;
   }
};
 
class leopard : public animal {
  public :
   leopard( int age) : animal(age) {}
   virtual std::string get_name() {
     return "leopard" ;
   }
};
 
class wildcat : public animal {
  public :
   wildcat( int age) : animal(age) {}
   virtual std::string get_name() {
     return "wildcat" ;
   }
};
 
class cougar : public animal {
  public :
   cougar( int age) : animal(age) {}
   virtual std::string get_name() {
     return "cougar" ;
   }
};
 
BOOST_EXTENSION_TYPE_MAP_FUNCTION {
   using namespace boost::extensions;
   std::map<std::string, factory<animal, int > >&
     animal_factories(types.get());
   animal_factories[ "Puma factory" ].set<puma>();
   animal_factories[ "Leopard factory" ].set<leopard>();
   animal_factories[ "Wildcat factory" ].set<wildcat>();
   animal_factories[ "Cougar factory" ].set<cougar>();
}

最后 BOOST_EXTENSION_TYPE_MAP_FUNCTION 产生的就是输出用于建立 factory 的函数,利用它可以获得一个 type_map,而 type_map 可以转换成为 std::map 给用户根据 factory 的名称(如 Puma Factory)产生需要的对象。下面是调用这个库的代码:

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
#include <iostream>
#include <map>
 
#include <boost/extension/factory.hpp>
#include <boost/extension/shared_library.hpp>
#include <boost/extension/type_map.hpp>
#include <boost/function.hpp>
#include <boost/scoped_ptr.hpp>
 
#include "animal.hpp"
 
int
main ( int argc, char * argv[] ) {
   using namespace boost::extensions;
   std::string library_path = "libanimal.so" ;
   shared_library lib(library_path);
   if (!lib.open()) {
     std::cerr << "Library failed to open: " << library_path << std::endl;
     return 1;
   }
 
   type_map types;
   if (!lib.call(types)) {
     std::cerr << "Function not found!" << std::endl;
     return 1;
   }
 
   std::map<std::string, factory<animal, int > >&
     factories( types.get() ) ;
   if (factories.empty()) {
     std::cerr << "Animals not found!" << std::endl;
     return 1;
   }
 
   int age = 1;
   for (std::map<std::string, factory<animal, int > >::iterator it
          = factories.begin();
        it != factories.end(); ++it) {
     ++age;
     std::cout << "Creating an animal using factory: " << it->first << std::endl;
     boost::scoped_ptr<animal> current_animal(it->second.create(age));
     std::cout << "Created an animal: " << current_animal->get_name()
               << " Age: " << current_animal->get_age() << std::endl;
   }
 
   return 0 ;
}

我们不难看到获得的 factories 对象的 pair 是字符串名表示 factory method name,而 factory 的 create 调用对应的构造函数产生我们需要的 animal 对象(这里用 boost::scoped_ptr 做内存管理),之后就可以利用 animal 提供的接口进行对应的操作。良好的设计下,应该不需要使用 dynamic_cast 进行转换。但是比如某些 animal 是某种别的 interface 的实现,这里就需要 dynamic_cast 了。下面是执行结果

$ ./animal
Creating an animal using factory: Cougar factory
Created an animal: cougar Age: 2
Creating an animal using factory: Leopard factory
Created an animal: leopard Age: 3
Creating an animal using factory: Puma factory
Created an animal: puma Age: 4
Creating an animal using factory: Wildcat factory
Created an animal: wildcat Age: 5

extension 支持更复杂的多重继承、virtual 继承,这里就不举例了。

extension 与 reflection

使用动态链接库与 Java 的 class 的另一个最大不同是 Java 的 reflection 能力强大,而 C++ 这方面却很欠缺,这导致很多 Java 中能实现的功能,C++ 都会比较吃力。boost.reflection 在这方面起到了一种补充作用。首先看看一个简单的 interface,虽然这个“不”是必须的:

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
#ifndef _CAR_HPP_
#define _CAR_HPP_
/**
  * @file car.hpp
  */
#include <iostream>
#include <string>
 
class car {
private :
   std::string name_;
   bool started_;
public :
   car(std::string car_name) : name_(car_name), started_( false ) {}
   bool start( void ) {
     std::cout << name_ << " started." << std::endl;
     started_ = true ;
     return true ;
   }
   bool turn( float angle)
   {
     if (started_) {
       std::cout << "Turning " << name_ << " " << angle << " degrees."
                 << std::endl;
       return true ;
     } else {
       std::cout << "Cannot turn before starting the engine of "
                 << name_ << "." << std::endl;
       return false ;
     }
   }
   void accelerate( float qty, bool direction)
   {
     if (started_) {
       std::cout << "Accelerating " << name_ << " " << qty << " m/h."
                 << std::endl;
     } else {
       std::cout << "Cannot accelerate before starting the engine of "
                 << name_ << "." << std::endl;
     }
   }
   virtual ~car( void ) {}
};
 
#endif

下面是我们的库,注意其中一个并没有继承 car,特别的如果我们需要使用 factory method 还是需要继承的。

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
/**
  * @file libcar.cpp
  */
 
#include "car.hpp"
#include <boost/extension/extension.hpp>
#include <boost/reflection/reflection.hpp>
using namespace boost::reflections;
 
class suv {
public :
   suv( const char * name) {}
   const char * get_type( void ) { return "It's an SUV." ; }
   void start() {}
   bool start( int target_speed) {}
   short year;
};
 
class compact : public car {
public :
   compact( const char * name) : car(name) {}
   virtual const char * get_type( void ) { return "It's a compact." ; }
   virtual ~compact( void ) {}
} ;
 
extern "C"
void BOOST_EXTENSION_EXPORT_DECL
extension_export_car(std::map<std::string, reflection> reflection_map) {
   reflection_map[ "suv" ].reflect<suv>()
     .constructor< const char *>()
     .function(&suv::get_type, "get_type" )
     .data(&suv::year, "year" )
     .function< void >(&suv::start, "start" )
     .function< bool , int >(&suv::start, "start" );
 
   reflection_map[ "compact" ].reflect<compact>()
     .constructor< const char *>()
     .function(&compact::get_type, "get_type" );
}

最后我们通过 reflection 来调用 get_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
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
61
62
63
/**
  * @file car.cpp
  */
#include <string>
#include <iostream>
#include <boost/extension/factory_map.hpp>
#include <boost/extension/shared_library.hpp>
#include <boost/extension/convenience.hpp>
#include <boost/reflection/reflection.hpp>
#include <boost/function.hpp>
 
int
main ( int argc, char * argv[] ) {
   using boost::reflections::instance;
   using boost::reflections::instance_constructor;
   using boost::reflections::reflection;
 
   std::map<std::string, reflection> reflection_map;
   boost::extensions::shared_library lib ( "libcar.so" ) ;
   lib.open();
   if (!lib.is_open())
     return 2 ;
   if (!lib.get< void , std::map<std::string,
       reflection> &>( "extension_export_car" ) ) {
     return 3;
   }
 
   lib.get< void , std::map<std::string, reflection> &>
               ( "extension_export_car" )(reflection_map);
   if (reflection_map.size() != size_t (2) ||
       reflection_map.find( "suv" ) == reflection_map.end() ||
       reflection_map.find( "compact" ) == reflection_map.end()) {
     std::cout << "Could not load reflections!" ;
     return 1;
   }
 
   reflection & first_reflection =
     reflection_map[ "suv" ];
   reflection & second_reflection =
     reflection_map[ "compact" ];
 
   instance_constructor< const char *> first_constructor =
     first_reflection.get_constructor< const char *>();
 
   instance first_instance =
     first_constructor( "First Instance" );
 
   boost::reflections::function< const char *> first_function =
     first_reflection.get_function< const char *>( "get_type" );
   std::cout << "First reflection: " << first_function(first_instance)
             << std::endl;
 
   instance_constructor< const char *> second_constructor =
     second_reflection.get_constructor< const char *>();
   instance second_instance =
     second_constructor( "Second Instance" );
   boost::reflections::function< const char *> second_function =
     second_reflection.get_function< const char *>( "get_type" );
   std::cout << "Second reflection: " << second_function(second_instance)
             << std::endl;
 
   return 0 ;
}

运行后的结果

$ ./car
First reflection: It's an SUV.
Second reflection: It's a compact.

其实 boost.extension 和 boost.reflection 的功能远远不止这些,我们后面会更仔细的研究这两个库。同时也希望他们能早日进入 boost release 的版本中。

——————

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值