これは、ここの説明に大きく基づいたカスタム ディレクティブです。
残念ながら、必要な情報がイテレータのプライベート メンバーに含まれているため、これは投稿した非常に単純な例でのみ機能します。すべてがずれてしまう前に何か他のものを出力すると。detail/output_iterator.hppのコードを少し変更する場合は、これを回避できます。"private:" をコメント化するか、position_policy
単にget_out_column
と同じようにメンバー関数を追加することができますget_out_count
。
それを使用するには、以下を変更する必要があります。
karma::int_ % karma::eol;
に:
custom_generator::align_list_to_current_position[karma::int_];
ご覧のとおり、カスタム ディレクティブには多くのボイラープレートが必要ですが、このコードの大部分はすべてのディレクティブに共通しています。実際、名前を変更する以外に、変更する必要があるのは次の 3 つだけです。
tracking
必要なプロパティのセットに含まれていることを確認してください。
typedef typename boost::mpl::int_<
Subject::properties::value | karma::generator_properties::tracking
> properties;
ディレクティブの属性を list(%) の属性と同じにします (ここを見てください):
template <typename Context, typename Iterator>
struct attribute
: boost::spirit::traits::build_std_vector<
typename boost::spirit::traits::attribute_of<Subject, Context, Iterator>::type
>
{};
そして最後にgenerate
関数を変更します。この関数では、左のメンバーとしてディレクティブに渡したものをすべて持ち、右のメンバーとして karma::eol と整列するために必要な数のスペースを連結したリストを作成するだけです。
#include <iostream>
#include <string>
#include <vector>
#include <boost/spirit/include/karma.hpp>
//START OF ALIGN_LIST_TO_CURRENT_POSITION.HPP
#include <boost/spirit/include/karma_generate.hpp>
///////////////////////////////////////////////////////////////////////////////
// definition the place holder
namespace custom_generator
{
BOOST_SPIRIT_TERMINAL(align_list_to_current_position);
}
///////////////////////////////////////////////////////////////////////////////
// implementation the enabler
namespace boost { namespace spirit
{
// We want custom_generator::align_list_to_current_position to be usable as a directive only,
// and only for generator expressions (karma::domain).
template <>
struct use_directive<karma::domain, custom_generator::tag::align_list_to_current_position>
: mpl::true_ {};
}}
///////////////////////////////////////////////////////////////////////////////
// implementation of the generator
namespace custom_generator
{
// That's the actual columns generator
template <typename Subject>
struct align_list_to_current_position_generator
: boost::spirit::karma::unary_generator<
align_list_to_current_position_generator<Subject> >
{
// Define required output iterator properties: take the properties needed by the subject and add tracking
typedef typename boost::mpl::int_<Subject::properties::value | boost::spirit::karma::generator_properties::tracking> properties;
// Define the attribute type exposed by this parser component
template <typename Context, typename Iterator>
struct attribute
: boost::spirit::traits::build_std_vector<
typename boost::spirit::traits::attribute_of<Subject, Context, Iterator>::type>
{};
align_list_to_current_position_generator(Subject const& s)
: subject(s)
{}
// This function is called during the actual output generation process.
// It dispatches to the embedded generator while supplying a new
// delimiter to use
template <typename OutputIterator, typename Context
, typename Delimiter, typename Attribute>
bool generate(OutputIterator& sink, Context& ctx
, Delimiter const& delimiter, Attribute const& attr) const
{
using boost::spirit::karma::repeat;
using boost::spirit::karma::char_;
using boost::spirit::karma::eol;
using boost::spirit::karma::domain;
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;
return boost::spirit::compile<domain>(subject%(eol << repeat(column)[char_(" ")])).generate(sink, ctx, delimiter, attr);
}
// 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("align_list_to_current_position", subject.what(ctx));
}
Subject subject;
};
}
///////////////////////////////////////////////////////////////////////////////
// instantiation of the generator
namespace boost { namespace spirit { namespace karma
{
// This is the factory function object invoked in order to create
// an instance of our align_list_to_current_position_generator.
template <typename Subject, typename Modifiers>
struct make_directive<custom_generator::tag::align_list_to_current_position, Subject, Modifiers>
{
typedef custom_generator::align_list_to_current_position_generator<Subject> result_type;
result_type operator()(unused_type, Subject const& s, unused_type) const
{
return result_type(s);
}
};
}}}
//END OF ALIGN_LIST_TO_CURRENT_POSITION.HPP
int main() {
std::vector<int> v { 0, 1, 2, 3 };
{
namespace karma = boost::spirit::karma;
using custom_generator::align_list_to_current_position;
karma::rule<std::ostream_iterator<char>, std::vector<int>() > myRule =
karma::lit("Some text: ") << align_list_to_current_position[karma::int_];
karma::generate(std::ostream_iterator<char>(std::cout), myRule, v);
std::cout << std::endl;
//This rule would work if you make the changes mentioned in align_list_to_current_position_generator::generate
karma::rule<std::ostream_iterator<char>, std::vector<int>() > myRuleThatFails =
karma::lit(":_(") << karma::eol << karma::lit("Some text: ") << align_list_to_current_position[karma::int_ << karma::int_];
karma::generate(std::ostream_iterator<char>(std::cout), myRuleThatFails, v);
}
return 0;
}