c++ - Boost:Spirit:Karma:如何获取输出的当前位置?

标签 c++ boost boost-spirit boost-spirit-karma

我想生成一些格式化的输出。为此需要一些缩进。因此,在生成过程中的某个时刻,我想获得当前位置,以缩进该数量的以下行。

这是一个最小的例子。请假设,我们不知道编译时 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/

相关文章:

c++ - 无法使用 boost spirit 将值添加到 vector

c++ - 您应该在头文件中包含源代码吗?

c++ - 原始类型的 emplace_back 与 push_back

c++ - 根据包含的数据对结构 vector 进行排序

c++ - 为什么会出现此错误 (C2582 : 'operator =' function is unavailable in 'B' ) when assigning to a reference?

c++ - Boost.Coroutine在 sleep 方面如何使用类似Unity3D Coroutine的方式?

c++ - 分解出精神规则的共同部分

c++ - 使用 Boost Spirit Qi 解析包含文字的字符串

c++ - 使用 pthreads 查看 stdin

c++ - 当你想实现一个可以返回 "nothing"的函数时,何时使用 boost::optional 以及何时使用 std::unique_ptr ?