同一个程序的静态堆栈空间(static、全局变量所存放的位置)只有一个,所以我们可以利用这块空间进行不同so中的操作。比如,一个so负责声明,另一个so负责实现,而且不需要直接相互包含。
下面给出一个例子,我会给出文件目录、所有文件内容以及编译脚本。其中clang++可以换成g++,版本需要支持c++17。
目录结构
-- helloworld
-- libFuncImpl
-- FuncImpl.h
-- FuncImpl.cpp
-- libFuncDecl
-- FuncDecl.h
-- FuncDecl.cpp
-- helloworld.cpp
-- message.h
-- build.sh
-- run.sh
libFuncImpl
两个文件写一起了,自己拆一下。
// FuncImpl.h
class FuncImpl {
public:
int Add(int a, int b);
void Print(int value);
};
// FuncImpl.cpp
#include "FuncImpl.h"
#include "message.h"
int FuncImpl::Add(int a, int b) {
auto func = Messager::get_server_func<int(int, int)>("add");
if (func)
return func(4,5);
return 0;
}
void FuncImpl::Print(int value) {
Messager::publish<int>("print", value);
}
libFuncDecl
// FuncDecl.h
class FuncDecl {
public:
void init();
};
// FuncDecl.cpp
#include "FuncDecl.h"
#include "message.h"
#include <iostream>
void FuncDecl::init() {
Messager::add_server_func<int(int, int)>("add", [](int a, int b) {
return a + b;
});
Messager::register_data_map<int>();
Messager::subcribe<int>("print", [](int value){
std::cout << value << std::endl;
});
}
message.h
这个文件才是关键。
#pragma once
#include <map>
#include <functional>
#include <string>
#include <vector>
class Messager {
public:
Messager() = delete;
template<typename T>
static void subcribe(const std::string &key, std::function<void(const T &)> func) {
auto &messager_map = get_messager_map<T>();
auto &funcs = messager_map[key];
funcs.push_back(func);
}
template<typename T>
static void publish(const std::string &key, const T &value) {
auto &messager_map = get_messager_map<T>();
auto &funcs = messager_map[key];
for (const auto &func : funcs) {
func(value);
}
}
template<typename T>
static void add_server_func(const std::string &key, std::function<T> func) {
auto &server_func = get_server_func<T>(key);
if (server_func){
publish("log_fatal", "server_func is already exists, key: " + key);
throw std::bad_exception();
}
server_func = func;
}
template<typename T>
static bool has_server(const std::string &key) {
auto &server_func = get_server_func<T>(key);
if (server_func){
return true;
} else {
return false;
}
}
template<typename T>
static void remove_server_func(const std::string &key) {
auto &server_func = get_server_func<T>(key);
server_func = std::function<T>();
}
template<typename T>
static std::function<T> &get_server_func(const std::string &key) {
auto & server_func_map = get_server_map<T>();
return server_func_map[key];
}
public:
template<typename T>
static void register_server_map() {
get_server_map<T>();
}
template<typename T>
static void register_data_map() {
get_messager_map<T>();
}
private:
template<typename T>
static std::map<std::string, std::function<T>> &get_server_map() {
static std::map<std::string, std::function<T>> server_func_map;
return server_func_map;
}
template<typename T>
static std::map<std::string, std::vector<std::function<void(const T &)>>> &get_messager_map() {
static std::map<std::string, std::vector<std::function<void(const T &)>>> messager_map;
return messager_map;
}
};
helloworld
这是个测试例子。
#include "libFuncDecl/FuncDecl.h"
#include "libFuncImpl/FuncImpl.h"
int main()
{
FuncDecl decl;
FuncImpl impl;
decl.init();
impl.Print(impl.Add(4, 5));
return 0;
}
编译脚本加运行脚本
编译脚本中的clang++可以改成g++。注意需要支持c++17的版本,支持c++14的也可,但是要把脚本中的标准改一下。
build.sh
cd libFuncImpl && clang++ -shared -fPIC --std=c++17 -I../ FuncImpl.cpp -o libFuncImpl.so && cd ..
cd libFuncDecl && clang++ -shared -fPIC --std=c++17 -I../ FuncDecl.cpp -o libFuncDecl.so && cd ..
clang++ --std=c++17 helloworld.cpp -o helloworld -L./libFuncImpl -L./libFuncDecl -lFuncImpl -lFuncDecl
run.sh
export DYLD_LIBRARY_PATH=./libFuncImpl/:./libFuncDecl/ && ./helloworld
使用方式
- 打开终端,运行
clang++ --version
或g++ --version
,确定编译环境。 cd
到helloworld
目录下,执行chmod +x build.sh run.sh
,给脚本添加执行权限。- 执行
./build.sh
。 - 执行
./run.sh
,将输出9
特点
这种使用方式,可以很大程度上减少 libFuncDecl.so
和 libFuncImpl.so
之间的依赖关系,使用统一的函数接口模板类(message.h
),进行泛化处理。此类中的模板函数可以进行更多参数的扩充,便于更多信息的传递处理。