一直在玩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方法,因为我们正在更改方法内部的值,但是我遇到了一大堆编译错误。这个当前的风格为我编译,但会崩溃我。我应该先了解为什么它会先崩溃。
关于我应该如何解决这个问题的任何想法
答案 0 :(得分:2)
有很多问题:
storedString += i;
(其中i
是int
)并不能做你认为的事情operator()(std::string const&) const
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
正确转义字符串值(如果它们包含"
,[
,]
,{{ 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);