c++ - 使用 gtest 模拟 boost 共享内存派生类
问题描述
我有一个简单的 CPP 类存储我的项目的一些配置。
这个类是使用 boost 进程间共享内存存储的,因此可以从我的服务器上运行的不同进程访问。
现在,我想在我的程序上运行一些测试——所以我想模拟我的共享内存对象的功能。为了使用 gtest 做到这一点,我创建了一个基本配置类,我的模拟类和共享内存类将从该类派生。
为了正确使用 gtest,我想模拟的基类函数必须是虚拟的,但根据boost 文档,共享内存不能包含虚拟函数,所以这是一种死锁。
我的基类示例:
class Configuration {
protected:
YAML::Node YmlFile_;
public:
struct System {
private:
float num1;
float num2;
public:
virtual ~System(){}
virtual float GetNum1() const {
return num1;
}
virtual float GetNum2() const {
return num2;
}
struct Logs{
private:
float num3;
float num4;
public:
virtual ~Logs(){}
virtual float GetNum3() const {
return num3;
}
virtual float GetNum4() const {
return num4;
}
Logs logs;
System system;
virtual System* GetSystem(){}
virtual Logs* GetLogs(){}
在模拟类上,我希望模拟函数以获取结构(GetSystem,GetLogs),然后模拟它们的返回值,但仍然能够保存将保存在共享内存中的“真实”派生类配置.
有任何想法吗..?
解决方案
先说原则:
您不必使用虚函数来模拟。
在不能使用运行时多态类型的地方,可以使用静态多态。
但在这种情况下,将配置接口与实现完全分离似乎更好。
实现您的接口不是从共享内存容器派生的(“配置源是共享内存对象”)。而是说“配置源有-一个共享内存对象”。
其他关键问题:
是什么让 YAML::Node 对共享内存安全?可能不是,因为我没有看到指定的分配器,而且它肯定涉及动态分配的内存以及内部指针。
我认为这种方法很容易因此而死在水中。
如果实际来源是 YAML,为什么不直接共享文件,而不是高度复杂的共享内存?(我们在这里只是在摸索表面。我们甚至没有提到同步)。
从一开始,文件系统就是计算机上进程的事实上的“共享内存”。
示例解耦接口和实现
接口使得实现可以解耦,但正如您所注意到的,继承通常使得它们不是。
为什么不写这样的东西:
struct ConfigData {
struct System {
float num1;
float num2;
struct Logs {
float num3;
float num4;
} logs;
} system;
};
现在制作一个共享接口(我会为演示简化它):
struct IConfiguration {
virtual ConfigData const& getData() const = 0;
};
所以你可以有你的 YAML 后端:
class YAMLConfiguration : public IConfiguration {
public:
YAMLConfiguration(std::istream& is) : _node(YAML::Load(is)) {
parse(_node, _data);
}
virtual ConfigData const& getData() const override {
return _data;
}
private:
YAML::Node _node;
ConfigData _data;
};
或共享内存实现:
#include <boost/interprocess/managed_shared_memory.hpp>
namespace bip = boost::interprocess;
class SharedConfiguration : public IConfiguration {
public:
SharedConfiguration(std::string name)
: _shm(bip::open_or_create, name.c_str(), 10ul << 10),
_data(*_shm.find_or_construct<ConfigData>("ConfigData")())
{ }
virtual ConfigData const& getData() const override {
return _data;
}
private:
bip::managed_shared_memory _shm;
ConfigData& _data;
};
完整演示
struct ConfigData {
struct System {
float num1 = 77;
float num2 = 88;
struct Logs {
float num3 = 99;
float num4 = 1010;
} logs;
} system;
};
struct IConfiguration {
virtual ConfigData const& getData() const = 0;
};
///////// YAML Backend
#include <yaml-cpp/yaml.h>
static bool parse(YAML::Node const& node, ConfigData::System::Logs& data) {
data.num3 = node["num3"].as<float>();
data.num4 = node["num4"].as<float>();
return true;
}
static bool parse(YAML::Node const& node, ConfigData::System& data) {
data.num1 = node["num1"].as<float>();
data.num2 = node["num2"].as<float>();
parse(node["Logs"], data.logs);
return true;
}
static bool parse(YAML::Node const& node, ConfigData& data) {
parse(node["System"], data.system);
return true;
}
class YAMLConfiguration : public IConfiguration {
public:
YAMLConfiguration(std::istream& is) : _node(YAML::Load(is)) {
parse(_node, _data);
}
virtual ConfigData const& getData() const override {
return _data;
}
private:
YAML::Node _node;
ConfigData _data;
};
///////// Shared Memory Backend
#include <boost/interprocess/managed_shared_memory.hpp>
namespace bip = boost::interprocess;
class SharedConfiguration : public IConfiguration {
public:
SharedConfiguration(std::string name)
: _shm(bip::open_or_create, name.c_str(), 10ul << 10),
_data(*_shm.find_or_construct<ConfigData>("ConfigData")())
{ }
virtual ConfigData const& getData() const override {
return _data;
}
private:
bip::managed_shared_memory _shm;
ConfigData& _data;
};
#include <iostream>
void FooFunction(IConfiguration const& cfg) {
std::cout << "Logs.num3:" << cfg.getData().system.logs.num3 << "\n";
}
void FakeApplication() {
std::cout << "Hello from FakeApplication\n";
std::istringstream iss(R"(
System:
num1: 0.1
num2: 0.22
Logs:
num3: 0.333
num4: 0.4444
)");
YAMLConfiguration config(iss);
FooFunction(config);
}
void FakeTests() {
std::cout << "Hello from FakeTests\n";
SharedConfiguration config("shared_memory_name");
FooFunction(config);
}
int main() {
FakeApplication();
FakeTests();
}
印刷
Hello from FakeApplication
Logs.num3:0.333
Hello from FakeTests
Logs.num3:99
总结与注意
简而言之,在使用共享内存之前要三思而后行。它并不像你想的那么简单。
很可能,您的一些配置值将不是 POD 数据类型(您知道,可能是字符串),突然间您将不得不关心分配器:
- 看看我的这些答案,看看它是什么样子,看看它是否值得你的目的
此外,不要忘记访问共享内存的进程之间的同步。
¹ Coliru 没有 yaml-cpp,但您可以使用 managed_mapped_file 显示共享实现:Live On Coliru
推荐阅读
- java - 如何实例化具有带参数的私有构造函数的泛型类
- python - pydotplus 工作不正常,决策树可视化错误?
- android - (实例中缺少 Vulkan 1.0 API)通过 Android Studio 3.5 和 3.6 Canary 9 启动 Android Emulator(Q API 29)时
- apache-kafka - Spring Cloud @StreamListener 消费者未在消费者组中注册 CONSUMER-ID、HOST 和 CLIENT-ID
- c# - lambda中的初始化惰性字段未在多个线程之间重用
- amazon-web-services - 使用参数在 AWS SAM 中动态创建资源名称
- react-native - 为什么我的模态高度占据了屏幕的一半?
- hibernate - 使用它们的 Id 从两个表创建一个表
- python-3.x - Python list() 与 append()
- matlab - 我的卷积码在完全重叠后停止工作。有什么建议么?