#c #boost #reflection #stdmap
Вопрос:
Мне было интересно, как я могу создать объект, если тип этого объекта хранится в строке. Моя цель — оптимизировать его настолько хорошо, насколько это возможно, этот алгоритм будет повторяться много раз. Я знаю 3 возможных решения:
- Создание списка (векторного?) Возможных классов и перебор всех возможных вариантов (чертовски медленно, если у меня> 10 возможных классов)
- подход std ::map (создание карты и проверка всего этого) (производительность не проверяется)
- Использование Boost::PFR (своего рода библиотека «отражения» для C ) (производительность также неизвестна)
Знаете ли вы, какой подход был бы лучшим и самым быстрым вариантом? И как это реализовать? С уважением, Кароль
Комментарии:
1. Подумайте об этом: зачем вам нужно хранить типы? Как бы вы обработали созданные объекты?
2. вы можете зарегистрировать все свои типы и иметь фабрику, которая будет создавать элемент на основе заданного ключа (строки? лучше иметь промежуточный ключ)
3. Общий совет по любому вопросу производительности — протестировать его, чтобы выяснить, соответствует ли он вашим потребностям. Гораздо точнее, чем случайные интернет-пользователи, размышляющие о коде, который они не видят.
4. Ripi2, мне нужно сохранить типы, чтобы нарисовать все объекты в сцене. Черт возьми, я опубликую код после того, как выполню эти примеры идеально, теперь у меня есть некоторые проблемы с моим кодом. В любом случае, спасибо за ваши ответы!
5. Вы хотите сделать все это самостоятельно, или использование фреймворка приемлемо?
Ответ №1:
Похоже, ваш случай — десериализация.
Синтаксический анализ и создание экземпляра будут намного медленнее, чем отправка одной виртуальной функции для фабрики классов. Вот набросок, анализ:
Point(3, 4.5);
Circle(Point(0,0), 42e3);
Rectangle(Point(7, -0.87654), Point (77, 9e2));
В следующую иерархию:
struct Shape {
virtual ~Shape() = defau<
virtual void print(std::ostreamamp;) const {};
friend std::ostreamamp; operator<<(std::ostreamamp; os, Shape constamp; s) {
s.print(os);
return os;
}
};
struct Point : Shape {
double x, y;
virtual void print(std::ostreamamp; os) const override {
os << "Point(" << x << "," << y << ")";
}
};
struct Circle : Shape {
Point origin;
double radius;
virtual void print(std::ostreamamp; os) const override {
os << "Circle(" << origin << "," << radius << ")";
}
};
struct Rectangle : Shape {
Point topleft, bottomright;
virtual void print(std::ostreamamp; os) const override {
os << "Rectangle(" << topleft << "," << bottomright << ")";
}
};
Анализатор использует Spirit X3:
namespace Parser {
using namespace boost::spirit::x3;
template <typename T>
auto as = [](auto expr) { return rule<struct _, T>{"rule"} = expr; };
auto point = as<Point>(eps >> "Point" >> "(" >> double_ >> ',' >> double_ >> ')');
auto circle = as<Circle>(eps >> "Circle" >> "(" >> point >> ',' >> double_ >> ')');
auto rectangle = as<Rectangle>(eps >> "Rectangle" >> "(" >> point >> ',' >> point >> ')');
auto shape = point | circle | rectangle;
auto push = [](autoamp; ctx) {
auto vis = [amp;](autoamp; shape) { _val(ctx).insert(std::move(shape)); };
apply_visitor(vis, _attr(ctx));
};
auto shapes = rule<struct _, Shapes>{} //
= skip(space)[shape[push] % ';'];
} // namespace Parser
Давайте используем поликоллекцию для хранения полиморфных типов. Очевидно, вы могли бы использовать контейнер указателей, если хотите, но вы упомянули, что производительность вызывает беспокойство.
int main() {
std::string constamp; input = R"(Point(3, 4.5);
Circle(Point(0,0), 42e3);
Rectangle(Point(7, -0.87654), Point (77, 9e2)))";
Shapes parsed;
if (parse(begin(input), end(input), Parser::shapes, parsed)) {
for (autoamp; s : parsed) {
std::cout << "Instantiated: " << s << "n";
}
}
}
С принтами
Instantiated: Rectangle(Point(7,-0.87654),Point(77,900))
Instantiated: Circle(Point(0,0),42000)
Instantiated: Point(3,4.5)
Живая демонстрация
#include <iostream>
#include <typeinfo>
struct Shape {
virtual ~Shape() = defau<
virtual void print(std::ostreamamp;) const {};
friend std::ostreamamp; operator<<(std::ostreamamp; os, Shape constamp; s) {
s.print(os);
return os;
}
};
struct Point : Shape {
double x, y;
virtual void print(std::ostreamamp; os) const override {
os << "Point(" << x << "," << y << ")";
}
};
struct Circle : Shape {
Point origin;
double radius;
virtual void print(std::ostreamamp; os) const override {
os << "Circle(" << origin << "," << radius << ")";
}
};
struct Rectangle : Shape {
Point topleft, bottomright;
virtual void print(std::ostreamamp; os) const override {
os << "Rectangle(" << topleft << "," << bottomright << ")";
}
};
#include <boost/poly_collection/base_collection.hpp>
using Shapes = boost::poly_collection::base_collection<Shape>;
#include <boost/fusion/adapted.hpp>
BOOST_FUSION_ADAPT_STRUCT(Point, x, y)
BOOST_FUSION_ADAPT_STRUCT(Circle, origin, radius)
BOOST_FUSION_ADAPT_STRUCT(Rectangle, topleft, bottomright)
#include <boost/spirit/home/x3.hpp>
namespace Parser {
using namespace boost::spirit::x3;
template <typename T>
auto as = [](auto expr) { return rule<struct _, T>{"rule"} = expr; };
auto point = as<Point>(eps >> "Point" >> "(" >> double_ >> ',' >> double_ >> ')');
auto circle = as<Circle>(eps >> "Circle" >> "(" >> point >> ',' >> double_ >> ')');
auto rectangle = as<Rectangle>(eps >> "Rectangle" >> "(" >> point >> ',' >> point >> ')');
auto shape = point | circle | rectangle;
auto push = [](autoamp; ctx) {
auto vis = [amp;](autoamp; shape) { _val(ctx).insert(std::move(shape)); };
apply_visitor(vis, _attr(ctx));
};
auto shapes = rule<struct _, Shapes>{} //
= skip(space)[shape[push] % ';'];
} // namespace Parser
int main() {
std::string constamp; input = R"(Point(3, 4.5);
Circle(Point(0,0), 42e3);
Rectangle(Point(7, -0.87654), Point (77, 9e2)))";
Shapes parsed;
if (parse(begin(input), end(input), Parser::shapes, parsed)) {
for (autoamp; s : parsed) {
std::cout << "Instantiated: " << s << "n";
}
}
}
Комментарии:
1. Версия с использованием ptr_vector для сравнения (больше выделений кучи) coliru.stacked-crooked.com/a/d9550f34b38eaed8
2. У вас есть какие-нибудь хорошие статьи о десериализации? Я искал некоторое время, и я не нашел ничего ценного. Спасибо за вашу помощь!
3. Все статьи будут в контексте их соответствующих библиотек / фреймворков (boost serialization, protobuf, Cap’n Proto, msgpack, bson, Cereal и некоторые другие, о которых я бы больше не упоминал (MFC OMG)). Надеюсь, вы их найдете. По крайней мере, вы можете легко черпать вдохновение из их выбора набора функций / интерфейса. Подумайте о управлении версиями, переносимости, потоковой / непотоковой передаче, частичной / интегральной (де) сериализации, отслеживании указателей и т. Д.)
4. sehe, спасибо за вашу помощь!