Полиморфный шаблон преобразования / преобразования данных

Проблема заключается в следующем:

рассмотрим следующий класс

class data : public base_data
{
public:
int a;
std::string b;
double c;
... // many other members
};

Предположим, что имеет смысл выставлять данные членам этого класса.

Теперь рассмотрим, что существует много таких классов, каждый с разными членами, возможно, все они происходят от одного базового класса «base_data».

Теперь эти классы нужно экспортировать, импортировать, создавать, «устанавливать» и «получать» из других произвольных представлений данных.

Например:

using any_map = boost::unordered_map < std::string, boost::any > ;

является одним из таких представлений.

Кроме того, все эти операции должны выполняться массово, то есть полиморфно через коллекцию объектов base_data *.

Одним из решений этой проблемы является предоставление интерфейса в base_data следующим образом

class base_data
{
public:
virtual void set(const any_map&) = 0;
virtual any_map get() const = 0;
};

каждый производный класс знает своих членов, поэтому он знает, как сделать перевод. дополнительно производные классы могут предоставлять конструкторы вида

data(const any_map&) {...}

Позволить легко определить абстрактный шаблон фабрики.

Другим решением этой проблемы является предоставление статических функций перевода в некотором пространстве имен для каждого производного типа, например

static data convert(const any_map&);
static any_map convert(const data&);

Таким образом, мы избегаем загрязнения производных классов за счет решения с «меньшим количеством ОО» и, вероятно, возможности массового выполнения этих операций перевода.

Это также имеет больше смысла, если мы рассмотрим возможность поддержки многих представлений, отличных от any_map, например,

using boost::ptree;
using json_class;
using xml_class;

Но еще раз, это не полиморфно.

Большинство шаблонов проектирования «перевода», о которых я читал, имеют дело с интерфейсами, но я не нашел такого, который бы формально рассматривал перевод / преобразование данных в контексте полиморфизма.

Я ищу ссылку на шаблон проектирования, который формально решает эту проблему, совет о том, как приступить к реализации и / или указать на явные недостатки в моем подходе.

2

Решение

Как указано в комментариях, ниже приведен код, иллюстрирующий использование шаблона посетителя, как я описал. Просто добавьте дополнительных посетителей для ввода, JSON, CSV или любых других форматов, которые вам требуются. Обратите внимание, что посетители не нуждаются в модификации для работы с различными структурами записей — нижеприведенная реализация просто должна знать, как обрабатывать различные типы полей, задействованные посредством виртуальной диспетчеризации. Все это в конечном итоге похоже на библиотеку Boost-сериализации, которую я тоже рекомендую рассмотреть.

#include <iostream>
#include <string>
#include <vector>
#include <sstream>

struct Visitor
{
typedef const char* Identifier; // or string...

Visitor(std::ostream& os) : os_(os) { }

virtual Visitor& pre(Identifier) { return *this; }
template <typename T> Visitor& operator()(Identifier id, const T& t)
{
std::ostringstream oss;
oss << t;
return operator()(id, oss.str());
}
virtual Visitor& operator()(Identifier, double) = 0;
virtual Visitor& operator()(Identifier, const std::string&) = 0;
virtual Visitor& post() { return *this; }

std::ostream& os_;
};

struct Visitor__XML_Out : Visitor
{
using Visitor::Visitor;

Visitor& pre(Identifier i) override
{ os_ << '<' << i << '>'; i_ = i; return *this; }

Visitor& operator()(Identifier f, double x) override
{ return out(f, x); }

Visitor& operator()(Identifier f, const std::string& x) override
{ return out(f, x); }

Visitor& post() override
{ os_ << "</" << i_ << '>'; return *this; }

private:
template <typename T>
Visitor& out(Identifier f, const T& x)
{
os_ << '<' << f << '>' << x << "</" << f << '>';
return *this;
}

Identifier i_;
};

struct Base_Data
{
virtual void visit(Visitor& v) = 0;
};

struct Data : Base_Data
{
int a_;
std::string b_;
double c_;

Data(int a, const std::string& b, double c)
: a_(a), b_(b), c_(c)
{ }

void visit(Visitor& v) override
{
v.pre("Data")("a", a_)("b", b_)("c", c_).post();
}
};

int main()
{
Data d { 42, "hawk", 8.8 };
Visitor__XML_Out xml(std::cout);
d.visit(xml);
std::cout << '\n';
}

Выход:

<Data><a>42</a><b>hawk</b><c>8.8</c></Data>
1

Другие решения