如何将Boost :: make_recursive_variant对象转换为字符串?

时间:2017-12-07 07:45:22

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

一直在玩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方法,因为我们正在更改方法内部的值,但是我遇到了一大堆编译错误。这个当前的风格为我编译,但会崩溃我。我应该先了解为什么它会先崩溃。

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

1 个答案:

答案 0 :(得分:2)

有很多问题:

  1. storedString += i;(其中iint)并不能做你认为的事情
  2. 您无法从operator()(std::string const&) const
  3. 返回值
  4. 你递归create_string()这是暂时的;这就是你无法正确制作const
  5. 的原因

    使用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正确转义字符串值(如果它们包含"[],{{ 1}})

    • 您可以将所有内容设为通用,以便它可以使用更多变体

    以下是这些子弹的实施:

    <强> 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);
    }
    

    更多:相当缩进

    现在我们已经丢失了字符串的行李,我们可以再次向访客添加一些状态。让我们用缩进来打印它。

    <强> Live On Coliru

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

    打印

    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 << "]";
        }
    };
    

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

    我注意到你写的伪代码更像是JSON:

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

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

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

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

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

    这就是全部。上面改进的访问者从一开始就是通用的 ,因此我们不需要更改另一行代码:

    <强> Live On Coliru

    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)) {}
    };
    

    打印

    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);