【问题标题】:Creating an object without its known type创建一个没有已知类型的对象
【发布时间】:2021-12-15 18:37:34
【问题描述】:

我想知道如果这个对象的类型存储在一个字符串中,我该如何创建一个对象。我的目标是尽可能地优化它,这个算法会被迭代很多次。我知道 3 种可能的解决方案:

  1. 创建可能类的列表(向量?)并迭代所有可能的变体(如果我有超过 10 个可能的类,速度会非常慢)
  2. std::map 方法(创建地图并检查所有地图)(未检查性能)
  3. 使用 Boost::PFR(一种用于 C++ 的“反射”库)(性能也未知)

您知道哪种方法是最好和最快的选择吗?以及如何实施? 真诚的,卡罗尔

【问题讨论】:

  • 想一想:为什么需要存储类型?您将如何处理创建的对象?
  • 您可以注册所有类型并拥有工厂,该工厂将根据给定键(字符串?最好有中间键)创建项目
  • 对任何性能问题的一般建议是对其进行测试,以确定它是否符合您的需求。比随机的互联网人推测他们看不到的代码要准确得多。
  • Ripi2,我需要存储类型来绘制场景中的所有对象。 TheUndeadFish,我会在完美完成这些示例后发布代码,现在我的代码有一些问题。不管怎样,谢谢你的回答!
  • 您想自己做,还是可以接受框架使用?

标签: c++ boost reflection stdmap


【解决方案1】:

看来你的情况是反序列化。

解析和实例化将比类工厂的一个虚函数调度慢得多。这是一个草图,正在解析:

Point(3, 4.5);
Circle(Point(0,0), 42e3);
Rectangle(Point(7, -0.87654), Point (77, 9e2));

进入以下层次结构:

struct Shape {
    virtual ~Shape() = default;
    virtual void print(std::ostream&) const {};

    friend std::ostream& operator<<(std::ostream& os, Shape const& s) {
        s.print(os);
        return os;
    }
};

struct Point : Shape {
    double x, y;
    virtual void print(std::ostream& os) const override {
        os << "Point(" << x << "," << y << ")";
    }
};

struct Circle : Shape {
    Point  origin;
    double radius;
    virtual void print(std::ostream& os) const override {
        os << "Circle(" << origin << "," << radius << ")";
    }
};

struct Rectangle : Shape {
    Point topleft, bottomright;
    virtual void print(std::ostream& 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 = [](auto& ctx) {
        auto vis = [&](auto& shape) { _val(ctx).insert(std::move(shape)); };
        apply_visitor(vis, _attr(ctx));
    };
    auto shapes = rule<struct _, Shapes>{}  //
                = skip(space)[shape[push] % ';'];
} // namespace Parser

让我们使用PolyCollection 来存储多态类型。如果您愿意,显然可以使用指针容器,但您提到性能是一个问题。

int main() {
    std::string const& 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 (auto& 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)

现场演示

Live On Coliru

#include <iostream>
#include <typeinfo>

struct Shape {
    virtual ~Shape() = default;
    virtual void print(std::ostream&) const {};

    friend std::ostream& operator<<(std::ostream& os, Shape const& s) {
        s.print(os);
        return os;
    }
};

struct Point : Shape {
    double x, y;
    virtual void print(std::ostream& os) const override {
        os << "Point(" << x << "," << y << ")";
    }
};

struct Circle : Shape {
    Point  origin;
    double radius;
    virtual void print(std::ostream& os) const override {
        os << "Circle(" << origin << "," << radius << ")";
    }
};

struct Rectangle : Shape {
    Point topleft, bottomright;
    virtual void print(std::ostream& 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 = [](auto& ctx) {
        auto vis = [&](auto& 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 const& 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 (auto& s : parsed) {
            std::cout << "Instantiated: " << s << "\n";
        }
    }
}

【讨论】:

  • 版本使用 ptr_vector 进行比较(更多的堆分配)coliru.stacked-crooked.com/a/d9550f34b38eaed8
  • 你有什么关于反序列化的好文章吗?找了好久,也没找到什么有价值的东西。感谢您的帮助!
  • 所有文章都将在它们各自的库/框架的上下文中(boost 序列化、protobuf、Cap'n Proto、msgpack、bson、Cereal 和其他一些我不会再提及的) (MFC OMG))。希望你能找到它们。至少您可以轻松地从他们的功能集/界面选择中获得灵感。考虑版本控制、可移植性、流/非流、部分/集成(反)序列化、指针跟踪等)
  • 咳咳,谢谢你的帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-22
  • 2013-08-27
  • 2011-09-29
  • 2022-10-05
  • 2019-07-10
  • 2021-08-10
相关资源
最近更新 更多