boost::spirit::karma 替代生成器 boost::variant 包含字符串和字符串别名
boost::spirit::karma alternative generator with boost::variant consisting a string and string alias
我有一个 boost::variant
由几种类型组成,包括字符串类型别名和字符串类型。字符串类型别名与 boost::spirit::qi
替代解析器一起工作,但 boost::spirit::karma
替代生成器不仅以不需要的方式工作,而且通过不使用所需的字符串类型别名生成器规则,但也出乎意料地工作,但是当变体包含字符串类型时,甚至不使用内置的字符串生成器:
#include <iostream>
#include <iterator>
#include <string>
#include <vector>
#include <boost/spirit/include/karma.hpp>
using mode = std::string;
using alt_variant = boost::variant<mode, std::string, unsigned>;
using alt_variant_without_string = boost::variant<mode, unsigned>;
template <typename OutputIterator>
boost::spirit::karma::rule<OutputIterator, mode()>
mode_gen{
boost::spirit::karma::lit("mode=\"") <<
boost::spirit::karma::string
<< boost::spirit::karma::lit("\"")
};
int main(int argc, char *argv[]) {
alt_variant foo1{mode{"bar"}};
alt_variant_without_string foo2{mode{"bar"}};
std::string output;
using namespace boost::spirit::karma;
const auto gen = mode_gen<std::back_insert_iterator<std::string>> | uint_ | string;
boost::spirit::karma::generate(std::back_inserter(output), gen, foo1);
std::cout << "Output\"" << output << "\"\n"; //Output""
output.clear();
boost::spirit::karma::generate(std::back_inserter(output), gen, foo2);
std::cout << "Output\"" << output << "\"\n";//Output"mode="bar""
return 0;
}
谁能解释一下这种行为,以及我是如何得到想要的行为的?
我想对于后一种情况,我必须摆脱所有字符串类型别名并使用显式结构作为类型,但后来我又陷入了丑陋的一个成员结构极端情况。 ( https://codereview.stackexchange.com/q/206259/95143
但是,第一个输出至少不只是 "bar" 即当模式生成器也不使用时不使用字符串生成器,对我来说看起来像是一个错误,即我无法理解。
从哪里开始。
一个。未指定的行为
This might actually be Undefined Behaviour but I didn't check the documentation.
类型别名不会创建新类型。因此 typeid(std::string) == typeid(mode)
变体无法区分这两种元素类型。
变体的行为未指定。比较:Live On Coliru
boost::variant<mode, std::string> v;
boost::variant<int, mode, std::string> v;
乙。未定义的行为
然后你做
const auto gen = mode_gen<std::back_insert_iterator<std::string> > | uint_ | string;
同样适用于 Qi:原型表达式通过引用保存规则操作数,这意味着 auto
是个坏主意:
- undefined behaviour somewhere in boost::spirit::qi::phrase_parse
运行 你的代码 UBSan/ASan 并使用 Valgring 来捕获这样的错误,在它们吃掉你客户的数据之前。
问题
你的问题是你想要可以打开的表达类型。我认为 Java-ists 喜欢称它为抽象数据类型。这是一个崇高的目标,您可以:
解决方案 1
将 mode
设为自定义类型:
#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
您可以使用 hack:
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
:
替换所有的业力语法
#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";
}
I LOVE IT when less and less code does more and more. But at this rate, one wonders why even use karma
?
奖励 #2 - ADL It,以及谁需要 Karma
要使其与 my_traits
方法和您的标签类型相得益彰,请将参数依赖查找最大化:
#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
我有一个 boost::variant
由几种类型组成,包括字符串类型别名和字符串类型。字符串类型别名与 boost::spirit::qi
替代解析器一起工作,但 boost::spirit::karma
替代生成器不仅以不需要的方式工作,而且通过不使用所需的字符串类型别名生成器规则,但也出乎意料地工作,但是当变体包含字符串类型时,甚至不使用内置的字符串生成器:
#include <iostream>
#include <iterator>
#include <string>
#include <vector>
#include <boost/spirit/include/karma.hpp>
using mode = std::string;
using alt_variant = boost::variant<mode, std::string, unsigned>;
using alt_variant_without_string = boost::variant<mode, unsigned>;
template <typename OutputIterator>
boost::spirit::karma::rule<OutputIterator, mode()>
mode_gen{
boost::spirit::karma::lit("mode=\"") <<
boost::spirit::karma::string
<< boost::spirit::karma::lit("\"")
};
int main(int argc, char *argv[]) {
alt_variant foo1{mode{"bar"}};
alt_variant_without_string foo2{mode{"bar"}};
std::string output;
using namespace boost::spirit::karma;
const auto gen = mode_gen<std::back_insert_iterator<std::string>> | uint_ | string;
boost::spirit::karma::generate(std::back_inserter(output), gen, foo1);
std::cout << "Output\"" << output << "\"\n"; //Output""
output.clear();
boost::spirit::karma::generate(std::back_inserter(output), gen, foo2);
std::cout << "Output\"" << output << "\"\n";//Output"mode="bar""
return 0;
}
谁能解释一下这种行为,以及我是如何得到想要的行为的?
我想对于后一种情况,我必须摆脱所有字符串类型别名并使用显式结构作为类型,但后来我又陷入了丑陋的一个成员结构极端情况。 ( https://codereview.stackexchange.com/q/206259/95143 但是,第一个输出至少不只是 "bar" 即当模式生成器也不使用时不使用字符串生成器,对我来说看起来像是一个错误,即我无法理解。
从哪里开始。
一个。未指定的行为
This might actually be Undefined Behaviour but I didn't check the documentation.
类型别名不会创建新类型。因此 typeid(std::string) == typeid(mode)
变体无法区分这两种元素类型。
变体的行为未指定。比较:Live On Coliru
boost::variant<mode, std::string> v;
boost::variant<int, mode, std::string> v;
乙。未定义的行为
然后你做
const auto gen = mode_gen<std::back_insert_iterator<std::string> > | uint_ | string;
同样适用于 Qi:原型表达式通过引用保存规则操作数,这意味着 auto
是个坏主意:
- undefined behaviour somewhere in boost::spirit::qi::phrase_parse
运行 你的代码 UBSan/ASan 并使用 Valgring 来捕获这样的错误,在它们吃掉你客户的数据之前。
问题
你的问题是你想要可以打开的表达类型。我认为 Java-ists 喜欢称它为抽象数据类型。这是一个崇高的目标,您可以:
解决方案 1
将 mode
设为自定义类型:
#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
您可以使用 hack:
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
:
#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";
}
I LOVE IT when less and less code does more and more. But at this rate, one wonders why even use
karma
?
奖励 #2 - ADL It,以及谁需要 Karma
要使其与 my_traits
方法和您的标签类型相得益彰,请将参数依赖查找最大化:
#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