Создание объекта без его известного типа

#c #boost #reflection #stdmap

Вопрос:

Мне было интересно, как я могу создать объект, если тип этого объекта хранится в строке. Моя цель — оптимизировать его настолько хорошо, насколько это возможно, этот алгоритм будет повторяться много раз. Я знаю 3 возможных решения:

  1. Создание списка (векторного?) Возможных классов и перебор всех возможных вариантов (чертовски медленно, если у меня> 10 возможных классов)
  2. подход std ::map (создание карты и проверка всего этого) (производительность не проверяется)
  3. Использование 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)
 

Живая демонстрация

Жить на Coliru

 #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, спасибо за вашу помощь!