如何将 Boost::make_recursive_variant 对象转换为字符串?
How to convert a Boost::make_recursive_variant Object into a string?
一直在玩 Boost:make_recursive_variant,我对如何从给定的 Variant 和 return 创建字符串感到很困惑。
我可以使用 cout 轻松输出,但我的目标是创建一个 C++ 版本的 Arrays.deeptoString 从 java 到 return 一个字符串。将 运行 纳入试图解决 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 = "";
};
我希望我可以 return 字符串,但它却让我崩溃了。我...既喜欢又讨厌递归变体 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 方法,因为我们正在改变方法内部的值,但我遇到了一大堆编译错误。当前的样式适合我,但会在我身上崩溃。我应该先调查一下它崩溃的原因。
关于我应该如何解决这个问题的任何想法
存在一些问题:
- 做
storedString += i;
(其中 i
是 int
)并没有按照您的想法去做
- 您未能 return 来自
operator()(std::string const&) const
的值
- 你递归到
create_string()
这是一个临时的;这就是你无法 const
正确 的原因
修复所有问题并使用 mutable/const:
#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
正确转义字符串值(以防它们包含 "
、[
、]
, ,
)
您可以使所有内容通用,以便它适用于更多变体
下面是这些项目符号的实现:
#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 ] ]
更多:漂亮的缩进
现在我们已经丢失了字符串的包袱,我们可以再次为访问者添加一些状态。让我们用缩进让它打印得漂亮。
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)) {}
};
就是这样。上面改进后的访问器从一开始就是通用的 enough,所以我们不需要再改一行代码:
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
]
]
]
一直在玩 Boost:make_recursive_variant,我对如何从给定的 Variant 和 return 创建字符串感到很困惑。
我可以使用 cout 轻松输出,但我的目标是创建一个 C++ 版本的 Arrays.deeptoString 从 java 到 return 一个字符串。将 运行 纳入试图解决 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 = "";
};
我希望我可以 return 字符串,但它却让我崩溃了。我...既喜欢又讨厌递归变体 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 方法,因为我们正在改变方法内部的值,但我遇到了一大堆编译错误。当前的样式适合我,但会在我身上崩溃。我应该先调查一下它崩溃的原因。
关于我应该如何解决这个问题的任何想法
存在一些问题:
- 做
storedString += i;
(其中i
是int
)并没有按照您的想法去做 - 您未能 return 来自
operator()(std::string const&) const
的值
- 你递归到
create_string()
这是一个临时的;这就是你无法const
正确 的原因
修复所有问题并使用 mutable/const:
#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
正确转义字符串值(以防它们包含"
、[
、]
,,
)您可以使所有内容通用,以便它适用于更多变体
下面是这些项目符号的实现:
#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 ] ]
更多:漂亮的缩进
现在我们已经丢失了字符串的包袱,我们可以再次为访问者添加一些状态。让我们用缩进让它打印得漂亮。
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)) {}
};
就是这样。上面改进后的访问器从一开始就是通用的 enough,所以我们不需要再改一行代码:
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
]
]
]