我想生成一些格式化的输出。为此需要一些缩进。因此,在生成过程中的某个时刻,我想获得当前位置,以缩进该数量的以下行。
这是一个最小的例子。请假设,我们不知道编译时 karma::lit("Some text: ")
的输出有多长。事实上,这个前导文本可能是由几个规则生成的。
#include <iostream>
#include <iterator>
#include <string>
#include <vector>
#include <boost/spirit/include/karma.hpp>
using namespace std;
int main() {
vector<int> v { 0, 1, 2, 3 };
{
namespace karma = boost::spirit::karma;
karma::rule<ostream_iterator<char>, std::vector<int>() > myRule =
karma::lit("Some text: ") << (karma::int_ % karma::eol);
karma::generate(ostream_iterator<char>(cout), myRule, v);
}
return 0;
}
这产生
Some text: 0
1
2
3
我想要的结果:
Some text: 0
1
2
3
为此,需要在生成 vector 之前知道当前位置。那么,类似于 qi::raw[]
的等价物?
更新:指向到目前为止生成的输出的指针也可以。
最佳答案
我相信这种方法类似于您在评论中描述的方法。它假定您可以从迭代器获得的唯一信息是写入的字符总数。如果您可以通过修改其他答案中提到的头文件来访问当前列,则可以进一步简化它。
编辑:使用 Mike M 在评论中建议的方法修改了代码。现在它有一个更好的界面。使用 boost 1.54.0 使用 g++ 4.8.1 和 clang 3.2 进行测试。
为了使用你需要先定义两个position_getter
类型的终端:
std::size_t start=0, end=0;
position_getter start_(start), end_(end);
然后您只需将 start_
放在一行的开头,将 end_
放在您想知道您在哪一列的位置。之后,您可以使用 end - start
来计算该列。由于此计算需要在解析时(而非编译时)完成,因此您需要使用 phx::ref(end) - phx::ref(start)
。
通过其他答案中提到的修改,您可以简单地定义一个终端:
std::size_t column=0;
position_getter column_(column);
然后像这样在规则中使用它:
myRule = karma::lit("Some text: ")
<< column_
<< karma::int_ %
(karma::eol << karma::repeat(phx::ref(column))[karma::char_(" ")]);
#include <iostream>
#include <string>
#include <vector>
#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix.hpp>
//START OF CURRENT_POS.HPP
#include <boost/spirit/include/karma_generate.hpp>
///////////////////////////////////////////////////////////////////////////////
// definition the place holder
namespace custom_generator {
BOOST_SPIRIT_TERMINAL_EX(current_pos);
struct position_getter: boost::spirit::terminal<
boost::spirit::tag::stateful_tag<std::size_t&, tag::current_pos> > {
typedef boost::spirit::tag::stateful_tag<std::size_t&, tag::current_pos> tag_type;
position_getter(std::size_t& p)
: boost::spirit::terminal<tag_type>(p) {
}
};
}
///////////////////////////////////////////////////////////////////////////////
// implementation the enabler
namespace boost {
namespace spirit {
// enables a terminal of type position_getter
template<>
struct use_terminal<karma::domain,
tag::stateful_tag<std::size_t&, custom_generator::tag::current_pos> > : mpl::true_ {
};
}
}
///////////////////////////////////////////////////////////////////////////////
// implementation of the generator
namespace custom_generator {
struct current_pos_generator: boost::spirit::karma::primitive_generator<
current_pos_generator> {
current_pos_generator(std::size_t& pos_)
: pos(pos_) {
}
// Define required output iterator properties
typedef typename boost::mpl::int_<
boost::spirit::karma::generator_properties::tracking> properties;
// Define the attribute type exposed by this parser component
template<typename Context, typename Unused>
struct attribute {
typedef boost::spirit::unused_type type;
};
// This function is called during the actual output generation process.
// It stores information about the position in the output stream in
// the variable you used to construct position_getter
template<typename OutputIterator, typename Context, typename Delimiter,
typename Attribute>
bool generate(OutputIterator& sink, Context& ctx,
Delimiter const& delimiter, Attribute const& attr) const {
std::size_t column = sink.get_out_count();
// This would only work if you comment "private:" in line 82 of
// boost/spirit/home/karma/detail/output_iterator.hpp
// std::size_t column = sink.track_position_data.get_column()-1;
pos = column;
return true;
}
// This function is called during error handling to create
// a human readable string for the error context.
template<typename Context>
boost::spirit::info what(Context& ctx) const {
return boost::spirit::info("current_pos");
}
std::size_t& pos;
};
}
///////////////////////////////////////////////////////////////////////////////
// instantiation of the generator
namespace boost {
namespace spirit {
namespace karma {
template<typename Modifiers>
struct make_primitive<
tag::stateful_tag<std::size_t&, custom_generator::tag::current_pos>,
Modifiers> {
typedef custom_generator::current_pos_generator result_type;
template<typename Terminal>
result_type operator()(Terminal& term, unused_type) const {
typedef tag::stateful_tag<std::size_t&,
custom_generator::tag::current_pos> tag_type;
using spirit::detail::get_stateful_data;
return result_type(get_stateful_data<tag_type>::call(term));
}
};
}
}
}
//END OF CURRENT_POS.HPP
int main() {
std::vector<int> v { 0, 1, 2, 3 };
{
namespace karma = boost::spirit::karma;
namespace phx = boost::phoenix;
using custom_generator::position_getter;
std::size_t start = 0, end = 0;
position_getter start_(start), end_(end);
karma::rule<std::ostream_iterator<char>, std::vector<int>()> myRule =
start_
<< karma::lit("Some text: ")
<< end_
<< karma::int_ % (karma::eol
<< karma::repeat(phx::ref(end) - phx::ref(start))[karma::char_(
" ")]);
karma::generate(std::ostream_iterator<char>(std::cout), myRule, v);
std::cout << std::endl;
karma::rule<std::ostream_iterator<char>, std::vector<int>()> myRuleThatAlsoWorks =
karma::lit(":)")
<< karma::eol
<< start_
<< karma::lit("Some text: ")
<< end_
<< karma::int_ % (karma::eol
<< karma::repeat(phx::ref(end) - phx::ref(start))[karma::char_(
" ")]);
karma::generate(std::ostream_iterator<char>(std::cout), myRuleThatAlsoWorks,
v);
}
return 0;
}
关于c++ - Boost:Spirit:Karma:如何获取输出的当前位置?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18085020/