原则第一:
你不必使用虚函数来模拟。
在你不能使用运行时多态类型的地方你可以使用静态多态。
但在这种情况下,最好将配置接口与实现完全分离。
实现您的接口,而不是派生来自共享内存容器(“配置源是-A 共享内存对象”)。而是说“配置源有共享内存对象”。
其他关键问题:
-
是什么让 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;
};
完整演示
Live On Coliru¹
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