【问题标题】:How to convert a Boost::make_recursive_variant Object into a string?如何将 Boost::make_recursive_variant 对象转换为字符串?
【发布时间】:2018-05-21 06:18:25
【问题描述】:

一直在玩 Boost:make_recursive_variant,我对如何从给定的 Variant 创建字符串并返回它感到很困惑。

我可以使用 cout 轻松输出,但我的目标是从 java 创建一个 C++ 版本的 Arrays.deeptoString 以返回一个字符串。尝试解决 recursive_variant 时不断遇到编译问题。这是我当前的 Arrays.deeptoString 代码。

typedef boost::make_recursive_variant<string, int, vector<boost::recursive_variant_ > >::type ObjectE;
class deepToString : public boost::static_visitor<std::string> {
public:
    string operator()(const int i) const {
        storedString += i;
        storedString += ",";
        return storedString;
    }
    string operator()(std::vector<ObjectE> const &v) const {
        storedString += "[";
        for (std::size_t i = 0; i < v.size() - 1; i++) {
            storedString += boost::apply_visitor(create_string(), v[i]);
            storedString += ",";
        }
        storedString += boost::apply_visitor(create_string(), v[v.size() - 1]);
        storedString += "]";
        return storedString;
    }
    string operator()(std::string const &s) const {
        storedString += s;
        storedString += ",";
    }
    mutable string storedString = "";
};

我希望我可以返回字符串,但它只是在我身上崩溃。我......既喜欢又讨厌递归变体atm。理想情况下,如果有一个给定的

ObjectE t = ["string" , [1, 2] , ["str2", "str3"], [1,3,6], [[1,2], [1,4]]]

我应该可以通过以下方式获取

string output = boost::apply_visitor(deepToString(), t);

输出获取

 "["string" , [1, 2] , ["str2", "str3"], [1,3,6], [[1,2], [1,4]]]"

一开始,我尝试不使用 const 方法,因为我们正在更改方法内部的值,但我得到了大量的编译错误。这种当前的风格为我编译,但会在我身上崩溃。我应该先调查一下它为什么会崩溃。

关于我应该如何解决这个问题的任何想法

【问题讨论】:

    标签: java c++ string boost boost-variant


    【解决方案1】:

    有很多问题:

    1. 执行storedString += i;(其中i 是int)并不像您认为的那样做
    2. 您未能从operator()(std::string const&amp;) const 返回值
    3. 你递归到create_string(),这是一个临时的;这就是你无法正确制作东西的原因const

    使用 mutable/const 解决所有问题并正确处理:

    Live On Coliru

    #include <boost/variant.hpp>
    #include <vector>
    #include <iostream>
    
    typedef boost::make_recursive_variant<std::string, int, std::vector<boost::recursive_variant_> >::type ObjectE;
    typedef std::vector<ObjectE> ObjectV;
    
    struct create_string : boost::static_visitor<std::string const&> {
        std::string const& operator()(const int i) {
            return storedString += std::to_string(i) + ", ";
        }
        std::string const& operator()(ObjectV const &v) {
            storedString += "[ ";
            for (std::size_t i = 0; i < v.size() - 1; i++) {
                create_string nest;
                storedString += boost::apply_visitor(nest, v[i]) + ", ";
            }
            create_string nest;
            storedString += boost::apply_visitor(nest, v[v.size() - 1]);
            return storedString += " ]";
        }
        std::string const& operator()(std::string const &s) {
            return storedString += s + ", ";
        }
    
        std::string storedString = "";
    };
    
    int main() {
        ObjectE obj = ObjectV { 
            1,
            "hello world",
            ObjectV {
                42, ObjectV { "some more" }, -42
            },
        };
    
        create_string vis;
    
        std::cout << "Result '" << boost::apply_visitor(vis, obj) << "'\n";
    }
    

    打印:

    Result '[ 1, , hello world, , [ 42, , [ some more,  ], -42,  ] ]'
    

    改进

    你可以做得更好。

    • 首先,您可以通过使用流而不是复制字符串来减少分配。这也可以使访问者无国籍。

    • 您还可以让访问者直接应用于变体,因此您不必再调用apply_visitor。

    • 您可以避免多余的逗号并使用std::quoted 正确转义字符串值(如果它们包含"、[、]、,)

      李>
    • 您可以使所有内容都通用,以便与更多变体一起使用

    以下是这些项目符号的实现:

    Live On Coliru

    #include <boost/variant.hpp>
    #include <vector>
    #include <iostream>
    #include <iomanip>
    
    typedef boost::make_recursive_variant<std::string, int, std::vector<boost::recursive_variant_> >::type ObjectE;
    typedef std::vector<ObjectE> ObjectV;
    
    struct printer {
        using result_type = void;
        std::ostream& _os;
    
        // forwards for `operator()`
        template <typename T> void call(T const& v) const { return operator()(v); }
    
        // dispatch for variants
        template <typename... Ts> void operator()(boost::variant<Ts...> const& v) const {
            return boost::apply_visitor(*this, v);
        }
    
        void operator()(int i) const                { _os << i;              } 
        void operator()(std::string const &s) const { _os << std::quoted(s); } 
    
        template <typename... Ts> void operator()(std::vector<Ts...> const& v) const {
            _os << "[ ";
            bool first = true;
            for (auto& el : v) {
                if (first) first = false;
                else       _os << ", ";
                call(el);
            }
            _os << " ]";
        }
    };
    
    int main() {
        ObjectE obj = ObjectV { 
            1,
            "hello world",
            ObjectV {
                42, ObjectV { "some more" }, -42
            },
        };
    
        printer print{std::cout};
        print(obj);
    }
    

    打印

    [ 1, "hello world", [ 42, [ "some more" ], -42 ] ]
    

    更多:漂亮的缩进

    现在我们已经丢失了字符串的包袱,我们可以再次为访问者添加一些状态。让我们用缩进让它打印出来。

    Live On Coliru

    struct printer {
        using result_type = void;
        std::ostream& _os;
        std::string _indent = "";
    
        template <typename... Ts> void operator()(boost::variant<Ts...> const& v) const {
            return boost::apply_visitor(*this, v);
        }
    
        void operator()(int i) const                { _os << i;              } 
        void operator()(std::string const &s) const { _os << std::quoted(s); } 
    
        template <typename... Ts> void operator()(std::vector<Ts...> const& v) const {
            _os << "[";
            auto indent = _indent + "    ";
    
            bool first = true;
            for (auto& el : v) {
                if (first) first = false;
                else _os << ",";
                _os << "\n" << indent;
                printer{_os, indent}(el);
            }
    
            if (!v.empty())
                _os << "\n" << _indent;
            _os << "]";
        }
    };
    

    打印

    [
        1,
        "hello world",
        [],
        [
            42,
            [
                "some more"
            ],
            -42
        ]
    ]
    

    为了胜利:没有递归变体,整洁的文字

    我注意到您编写的伪代码更像 JSON:

    ObjectE t = ["string" , [1, 2] , ["str2", "str3"], [1,3,6], [[1,2], [1,4]]]
    

    那不是 C++。但是,我可以让它像这样工作:

    ObjectE t = {"string" , {1, 2} , {"str2", "str3"}, {1,3,6}, {{1,2}, {1,4}}};
    

    你可以通过创建一个派生自变量的类型,并添加一个构造函数来获取向量元素的初始化列表:

    struct ObjectE : boost::variant<std::string, int, std::vector<ObjectE> > {
        using Base = boost::variant<std::string, int, std::vector<ObjectE> >;
    
        using Base::variant;
        using Base::operator=;
    
        ObjectE(std::initializer_list<ObjectE> init) : Base(std::vector<ObjectE>(init)) {}
    };
    

    就是这样。上面改进的访问者从一开始就是通用的足够,所以我们不需要更改另一行代码:

    Live On Coliru

    printer print{std::cout};
    print(ObjectE { 1, "hello world", {}, { 42, { "some more" }, -42 } });
    
    std::cout << "\nt: ";
    
    ObjectE t = {"string" , {1, 2} , {"str2", "str3"}, {1,3,6}, {{1,2}, {1,4}}};
    print(t);
    

    打印

    [
        1,
        "hello world",
        [],
        [
            42,
            [
                "some more"
            ],
            -42
        ]
    ]
    t: [
        "string",
        [
            1,
            2
        ],
        [
            "str2",
            "str3"
        ],
        [
            1,
            3,
            6
        ],
        [
            [
                1,
                2
            ],
            [
                1,
                4
            ]
        ]
    ]
    

    【讨论】:

    • 我.. 没想到会有如此深入的答案。将审查它们并将我从中学到的知识应用到另一个会话中。我之前试图使用 ostream 。一直遇到问题。现在我明白为什么了。谢谢你的花絮。
    • 为了获胜,我添加了最后一步,将您带到print(ObjectE {"string" , {1, 2} , {"str2", "str3"}, {1,3,6}, {{1,2}, {1,4}}});,就像您的梦想代码一样:) Live On Coliru
    • @Leruce “我……没想到会有这么深入的回答。”我们称之为sehe效应。
    • 嗨 Coliru,我在放置不同类型的元素时遇到错误。我的 C++ 版本有点旧。
    • @ChandanKSingh 我问科利鲁之神,但他也不明白你的意思。您是否忘记包含 coliru 链接?
    猜你喜欢
    • 2023-03-17
    • 2021-02-03
    • 2018-07-19
    • 1970-01-01
    • 2019-07-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多