Boost Karma:使用 boost::optional<boost::variant<...>> 编写规则
Boost Karma: writing a rule with boost::optional<boost::variant<...>>
我正在尝试在 Karma 生成器的 boost::optional
中使用 boost::variant
。我已经能够将问题简化为:
using FooVariant = boost::variant<int>;
using FooOptional = boost::optional<FooVariant>;
template<typename OutputIt = boost::spirit::ostream_iterator>
struct FooGenerator
: boost::spirit::karma::grammar<OutputIt, FooOptional()>
{
FooGenerator()
: FooGenerator::base_type(start_)
{
namespace bsk = boost::spirit::karma;
start_ = '[' << ( bsk::int_ | '*' ) << ']';
}
boost::spirit::karma::rule<OutputIt, FooOptional()> start_;
};
int main()
{
FooOptional fop1;
std::cout << boost::spirit::karma::format(FooGenerator<>(), fop1) << std::endl;
FooOptional fop2 = FooVariant{123};
std::cout << boost::spirit::karma::format(FooGenerator<>(), fop2) << std::endl;
}
这里的输出是:
[*]
[*]
这不是我所希望的。
我更改的第一件事之一是通过将 FooVariant
更改为:using FooVariant = int;
或多或少地进行完整性检查。这输出:
[*]
[123]
而且是我想看到的!所以在我的第一个代码中,变体只有一种类型,所以我尝试添加第二种类型,只是为了看看:
using FooVariant = boost::variant<int, double>;
...
start_ = '[' << ( ( bsk::int_ | bsk::double_ ) | '*' ) << ']';
但我们又回到了:
[*]
[*]
然后我尝试添加一个专门针对变体的规则:
using FooVariant = boost::variant<int, double>;
using FooOptional = boost::optional<FooVariant>;
template<typename OutputIt = boost::spirit::ostream_iterator>
struct FooGenerator
: boost::spirit::karma::grammar<OutputIt, FooOptional()>
{
FooGenerator()
: FooGenerator::base_type(start_)
{
namespace bsk = boost::spirit::karma;
foovar_ = (bsk::int_ | bsk::double_);
start_ = '[' << ( foovar_ | '*' ) << ']';
}
boost::spirit::karma::rule<OutputIt, FooVariant()> foovar_;
boost::spirit::karma::rule<OutputIt, FooOptional()> start_;
};
int main()
{
FooOptional fop1;
std::cout << boost::spirit::karma::format(FooGenerator<>(), fop1) << std::endl;
FooOptional fop2 = FooVariant{123};
std::cout << boost::spirit::karma::format(FooGenerator<>(), fop2) << std::endl;
}
编译错误:
alternative_function.hpp:127:34: error: no member named 'is_compatible' in
'boost::spirit::traits::compute_compatible_component<boost::variant<int, double>, boost::optional<boost::variant<int, double> >, boost::spirit::karma::domain>'
if (!component_type::is_compatible(spirit::traits::which(attr_)))
~~~~~~~~~~~~~~~~^
看起来模板生成正在尝试确保 boost::variant
和 boost::optional
兼容,但对我来说问题是 "why is it trying to make sure they're compatible at all?"
我怎样才能使这个工作?
我在另一个答案中告诉过你我会如何处理这个问题:
这不仅回避了问题,还简化了 AST/data 类型。
现在,既然你在强迫我¹,我就坚持下去。问题出在 foovar_ | '|'
是一个 替代表达式 并且它以某种方式验证了这一点该属性必须兼容。在该检查期间,假设
- 如果
a
具有属性 attr(a)
且 b
具有属性 attr(b)
则
a | b
应该有 variant<attr(a), attr(b)
它确实做了一些逻辑检查(比如 attr(a) == attr(b)
时),但没有逻辑检查如果 attr(b)
是 unused_type
那么它也应该与 optional<attr(a)>
兼容而不仅仅是 variant<attr(a), unused_type>
甚至 variant<attr(a)>
.
现在,我对为什么不感兴趣²。所以,这里是你可以强制的方法。
foovar_ = bsk::int_ | bsk::double_;
fooopt_ = (bsk::eps(is_initialized_(bsk::_val)) | '*') << -foovar_;
start_ = '[' << fooopt_ << ']';
key 是将其编码为 alternative-expression 以外的东西。在这里,我们只是相信 -foovar_
会说或不说。此外,我们还做
bsk::eps(is_initialized_(bsk::_val)) | '*'
意思:如果variant被初始化了,就这样,否则生成'*'
.
现在你不需要第三条规则,如果你喜欢只写代码,你可以写:
start_ = '[' << (bsk::eps(is_initialized_(bsk::_val)) | '*') << -foovar_ << ']';
演示
哦,差点忘了:
struct is_initialized_f {
template<typename T>
bool operator()(boost::optional<T> const& opt) const { return opt.is_initialized(); }
};
boost::phoenix::function<is_initialized_f> is_initialized_;
实现帮助 Phoenix actor 检查初始化状态。
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix.hpp>
using FooVariant = boost::variant<int, double>;
using FooOptional = boost::optional<FooVariant>;
template<typename OutputIt = boost::spirit::ostream_iterator>
struct FooGenerator
: boost::spirit::karma::grammar<OutputIt, FooOptional()>
{
FooGenerator()
: FooGenerator::base_type(start_)
{
namespace bsk = boost::spirit::karma;
namespace phx = boost::phoenix;
foovar_ = bsk::int_ | bsk::double_;
//fooopt_ = (bsk::eps(is_initialized_(bsk::_val)) | '*') << -foovar_;
start_ = '[' << (bsk::eps(is_initialized_(bsk::_val)) | '*') << -foovar_ << ']';
}
private:
struct is_initialized_f {
template<typename T>
bool operator()(boost::optional<T> const& opt) const { return opt.is_initialized(); }
};
boost::phoenix::function<is_initialized_f> is_initialized_;
boost::spirit::karma::rule<OutputIt, FooVariant()> foovar_;
//boost::spirit::karma::rule<OutputIt, FooOptional()> fooopt_;
boost::spirit::karma::rule<OutputIt, FooOptional()> start_;
};
int main()
{
for (FooOptional fop : { FooOptional{}, {FooVariant{123}}, {FooVariant{3.14}} }) {
if (std::cout << boost::spirit::karma::format(FooGenerator<>(), fop))
std::cout << "\n";
else
{
std::cout.clear();
std::cout << "#Error\n";
}
}
}
版画
[*]
[123]
[3.14]
¹ 笑话
² 我们会得到与 "someone forgot" 或 "it's a regression"
押韵的非权威答案
我正在尝试在 Karma 生成器的 boost::optional
中使用 boost::variant
。我已经能够将问题简化为:
using FooVariant = boost::variant<int>;
using FooOptional = boost::optional<FooVariant>;
template<typename OutputIt = boost::spirit::ostream_iterator>
struct FooGenerator
: boost::spirit::karma::grammar<OutputIt, FooOptional()>
{
FooGenerator()
: FooGenerator::base_type(start_)
{
namespace bsk = boost::spirit::karma;
start_ = '[' << ( bsk::int_ | '*' ) << ']';
}
boost::spirit::karma::rule<OutputIt, FooOptional()> start_;
};
int main()
{
FooOptional fop1;
std::cout << boost::spirit::karma::format(FooGenerator<>(), fop1) << std::endl;
FooOptional fop2 = FooVariant{123};
std::cout << boost::spirit::karma::format(FooGenerator<>(), fop2) << std::endl;
}
这里的输出是:
[*]
[*]
这不是我所希望的。
我更改的第一件事之一是通过将 FooVariant
更改为:using FooVariant = int;
或多或少地进行完整性检查。这输出:
[*]
[123]
而且是我想看到的!所以在我的第一个代码中,变体只有一种类型,所以我尝试添加第二种类型,只是为了看看:
using FooVariant = boost::variant<int, double>;
...
start_ = '[' << ( ( bsk::int_ | bsk::double_ ) | '*' ) << ']';
但我们又回到了:
[*]
[*]
然后我尝试添加一个专门针对变体的规则:
using FooVariant = boost::variant<int, double>;
using FooOptional = boost::optional<FooVariant>;
template<typename OutputIt = boost::spirit::ostream_iterator>
struct FooGenerator
: boost::spirit::karma::grammar<OutputIt, FooOptional()>
{
FooGenerator()
: FooGenerator::base_type(start_)
{
namespace bsk = boost::spirit::karma;
foovar_ = (bsk::int_ | bsk::double_);
start_ = '[' << ( foovar_ | '*' ) << ']';
}
boost::spirit::karma::rule<OutputIt, FooVariant()> foovar_;
boost::spirit::karma::rule<OutputIt, FooOptional()> start_;
};
int main()
{
FooOptional fop1;
std::cout << boost::spirit::karma::format(FooGenerator<>(), fop1) << std::endl;
FooOptional fop2 = FooVariant{123};
std::cout << boost::spirit::karma::format(FooGenerator<>(), fop2) << std::endl;
}
编译错误:
alternative_function.hpp:127:34: error: no member named 'is_compatible' in
'boost::spirit::traits::compute_compatible_component<boost::variant<int, double>, boost::optional<boost::variant<int, double> >, boost::spirit::karma::domain>'
if (!component_type::is_compatible(spirit::traits::which(attr_)))
~~~~~~~~~~~~~~~~^
看起来模板生成正在尝试确保 boost::variant
和 boost::optional
兼容,但对我来说问题是 "why is it trying to make sure they're compatible at all?"
我怎样才能使这个工作?
我在另一个答案中告诉过你我会如何处理这个问题:
这不仅回避了问题,还简化了 AST/data 类型。
现在,既然你在强迫我¹,我就坚持下去。问题出在 foovar_ | '|'
是一个 替代表达式 并且它以某种方式验证了这一点该属性必须兼容。在该检查期间,假设
- 如果
a
具有属性attr(a)
且b
具有属性attr(b)
则 a | b
应该有variant<attr(a), attr(b)
它确实做了一些逻辑检查(比如 attr(a) == attr(b)
时),但没有逻辑检查如果 attr(b)
是 unused_type
那么它也应该与 optional<attr(a)>
兼容而不仅仅是 variant<attr(a), unused_type>
甚至 variant<attr(a)>
.
现在,我对为什么不感兴趣²。所以,这里是你可以强制的方法。
foovar_ = bsk::int_ | bsk::double_;
fooopt_ = (bsk::eps(is_initialized_(bsk::_val)) | '*') << -foovar_;
start_ = '[' << fooopt_ << ']';
key 是将其编码为 alternative-expression 以外的东西。在这里,我们只是相信 -foovar_
会说或不说。此外,我们还做
bsk::eps(is_initialized_(bsk::_val)) | '*'
意思:如果variant被初始化了,就这样,否则生成'*'
.
现在你不需要第三条规则,如果你喜欢只写代码,你可以写:
start_ = '[' << (bsk::eps(is_initialized_(bsk::_val)) | '*') << -foovar_ << ']';
演示
哦,差点忘了:
struct is_initialized_f {
template<typename T>
bool operator()(boost::optional<T> const& opt) const { return opt.is_initialized(); }
};
boost::phoenix::function<is_initialized_f> is_initialized_;
实现帮助 Phoenix actor 检查初始化状态。
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix.hpp>
using FooVariant = boost::variant<int, double>;
using FooOptional = boost::optional<FooVariant>;
template<typename OutputIt = boost::spirit::ostream_iterator>
struct FooGenerator
: boost::spirit::karma::grammar<OutputIt, FooOptional()>
{
FooGenerator()
: FooGenerator::base_type(start_)
{
namespace bsk = boost::spirit::karma;
namespace phx = boost::phoenix;
foovar_ = bsk::int_ | bsk::double_;
//fooopt_ = (bsk::eps(is_initialized_(bsk::_val)) | '*') << -foovar_;
start_ = '[' << (bsk::eps(is_initialized_(bsk::_val)) | '*') << -foovar_ << ']';
}
private:
struct is_initialized_f {
template<typename T>
bool operator()(boost::optional<T> const& opt) const { return opt.is_initialized(); }
};
boost::phoenix::function<is_initialized_f> is_initialized_;
boost::spirit::karma::rule<OutputIt, FooVariant()> foovar_;
//boost::spirit::karma::rule<OutputIt, FooOptional()> fooopt_;
boost::spirit::karma::rule<OutputIt, FooOptional()> start_;
};
int main()
{
for (FooOptional fop : { FooOptional{}, {FooVariant{123}}, {FooVariant{3.14}} }) {
if (std::cout << boost::spirit::karma::format(FooGenerator<>(), fop))
std::cout << "\n";
else
{
std::cout.clear();
std::cout << "#Error\n";
}
}
}
版画
[*]
[123]
[3.14]
¹ 笑话
² 我们会得到与 "someone forgot" 或 "it's a regression"
押韵的非权威答案