从哪儿开始。
A. 未指明的行为
这实际上可能是未定义的行为,但我没有检查文档。
类型别名不会创建新类型。因此typeid(std::string) == typeid(mode)
,变体无法区分这两种元素类型。
Variant 的行为未指定。相比:Live On Coliru
boost::variant<mode, std::string> v;
和Live On Coliru
boost::variant<int, mode, std::string> v;
B. 未定义的行为
然后你做
const auto gen = mode_gen<std::back_insert_iterator<std::string> > | uint_ | string;
与 Qi 一样适用:原始表达式通过引用保存规则操作数,这意味着这auto
是一个坏主意:
使用 UBSan/ASan 运行您的代码,并使用 Valgring 在此类错误吃掉您的客户数据之前捕获它们。
问题
您的问题是您想要可以打开的表达类型。我认为 Java 主义者喜欢称它为抽象数据类型。这是一个崇高的目标,您可以:
解决方案 1
制作mode
自定义类型:
Live On Coliru
#include <boost/spirit/include/karma.hpp>
#include <iostream>
#include <iterator>
#include <string>
#include <vector>
struct mode : std::string {
using std::string::string;
};
namespace karma = boost::spirit::karma;
template <typename Out = boost::spirit::ostream_iterator>
karma::rule<Out, mode()> mode_gen = "mode=\"" << karma::string << "\"";
int main() {
using Variant = boost::variant<mode, std::string, unsigned>;
Variant foo = std::string("foo"),
bar = mode("bar"),
i = 42;
for (Variant v : { foo, bar, i })
std::cout << "Output: " << format(mode_gen<> | karma::uint_ | karma::string, v) << "\n";
}
印刷
Output: foo
Output: mode="bar"
Output: 42
解决方案#2:强类型定义
我无法立即完成这项工作,所以让我指出一个示例实现:
#include <boost/serialization/strong_typedef.hpp>
解决方案#3:区分std::string
您可以使用黑客:
namespace hack {
template <typename Char, typename Tag>
struct my_traits : std::char_traits<Char> {};
}
using mode = std::basic_string<char, hack::my_traits<char, struct ModeTag> >;
仍然打印相同Live On Coliru
Output: foo
Output: mode="bar"
Output: 42
奖金
你的发电机有问题。具体来说,如果您的mode
值包含报价,事情就会出错。您可以简单地利用ostream
:
struct mode : std::string {
using std::string::string;
friend std::ostream& operator<<(std::ostream& os, mode const& m) {
return os << "mode=" << std::quoted(m);
}
};
这种方式很简单
std::cout << mode("yo") << std::endl;
std::cout << mode("y\"!\"o") << std::endl;
会打印Live On Coliru
mode="yo"
mode="y\"!\"o"
这要优雅得多。这也意味着您可以将所有业力语法替换为karma::stream
:
Live On Coliru
#include <boost/spirit/include/karma.hpp>
#include <iostream>
#include <iomanip>
struct mode : std::string {
using std::string::string;
friend std::ostream& operator<<(std::ostream& os, mode const& m) {
return os << "mode=" << std::quoted(m);
}
};
int main() {
boost::variant<mode, std::string, unsigned>
foo = std::string("foo"),
bar = mode("bar"),
i = 42;
for (auto v : { foo, bar, i })
std::cout << "Output: " << karma::format(karma::stream, v) << "\n";
}
当越来越少的代码做越来越多的事情时,我喜欢它。但是以这种速度,人们想知道为什么还要使用karma
?
奖金 #2 - ADL It,谁需要 Karma
为了让它在my_traits
方法和标签类型上大放异彩,请将 Argument Dependent Lookup 发挥到最大:
Live On Coliru
#include <boost/variant.hpp>
#include <iostream>
#include <iomanip>
namespace hack {
template <typename Char, typename Tag>
struct my_traits : std::char_traits<Char> {};
}
namespace mylib {
struct ModeTag{};
struct ValueTag{};
static inline std::ostream& operator<<(std::ostream& os, ModeTag) { return os << "mode"; }
static inline std::ostream& operator<<(std::ostream& os, ValueTag) { return os << "value"; }
template <typename Char, typename Tag>
static inline std::ostream& operator<<(std::ostream& os, hack::my_traits<Char, Tag>)
{ return os << Tag{}; }
template <typename Char, typename CharT, typename Alloc>
std::ostream& operator<<(std::ostream& os, std::basic_string<Char, CharT, Alloc> const& s) {
return os << CharT{} << "=" << std::quoted(s);
}
}
using mode = std::basic_string<char, hack::my_traits<char, struct mylib::ModeTag> >;
using value = std::basic_string<char, hack::my_traits<char, struct mylib::ValueTag> >;
int main() {
boost::variant<mode, value, unsigned>
foo = value("foo"),
bar = mode("bar"),
i = 42;
std::cout << foo << std::endl;
std::cout << bar << std::endl;
std::cout << i << std::endl;
}
它的编译速度提高了 10 倍并打印:
value="foo"
mode="bar"
42