转自:
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 的版本中。
——————