Сначала принципы:
Вам не нужно использовать виртуальные функции для имитации.
В местах, где вы не можете использовать runtime-polymorphi c типы, вы можете использовать stati c полиморфизм.
Но в этом случае еще лучше полностью отделить интерфейс конфигурации от реализации.
Реализуйте свой интерфейс, а не извлекайте из общей памяти контейнер ("источник конфигурации Is-A объект разделяемой памяти"). Вместо этого скажите «источник конфигурации имеет объект разделяемой памяти».
Другие важные вопросы:
Что делает YAML :: Node безопасным для разделяемой памяти? Скорее всего, это не так, потому что я не вижу указанного распределителя, и он, безусловно, включает в себя динамически выделяемую память, а также внутренние указатели.
Я думаю, что этот подход может легко потерпеть неудачу только для это.
Если фактическим источником является YAML, почему бы просто не поделиться файлом вместо очень сложной общей памяти? (Мы здесь только касаемся поверхности. Мы даже не упомянули о синхронизации).
Файловая система с незапамятных времен де-факто является «общей памятью» процессов на компьютере.
Пример интерфейса и реализации развязки
Интерфейсы делают так, что реализации могут быть развязаны, но, как вы заметили, наследование часто приводит к тому, что они не связаны 't.
Почему бы не написать что-то вроде:
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