淘先锋技术网

首页 1 2 3 4 5 6 7

需求:给定一个参数,若它为1,则不输出;若为1,则输出且追加乘法运算符。

分析:这个需求是很容易满足的,一个if语句就可以了。即便是用karma,只需要eps + alternative。这里主要研究如何自定义directive来完成这个任务。自定义generator是类似的,可以参照karma中的源码。

实现步骤:

  1. 设计directive的名字,取名为coef_。希望的使用方法coef_[generator]或coef_("*")[generator]。
  2. 使用BOOST_SPIRIT_TERMINAL_NAME_EX宏定义标签
            BOOST_SPIRIT_TERMINAL_NAME_EX(coef_, coef_type);
    
  3. 然后特化use_directive定义使用方法
            template<>
            struct use_directive<karma::domain, tag::coef_> : mpl::true_ {};
    
            template<typename Arg>
            struct use_directive < karma::domain, 
                terminal_ex <tag::coef_, fusion::vector1<Arg>>
            > : traits::is_string<Arg> {};
                   
    
    其中的fusion::vector1<Arg> 表明()运算符的参数只有一个。
  4. 接下来定义directive的具体规则。规则由一个类实现,然后再将标签(coef_)和实现(coef_directive)关联起来。
    1. 因为要定义的是一个作用在单个generator上的directive(即[]运算符只有一个参数),所以从unary_generator类继承。
                  template <typename Subject>
                  struct coef_directive : unary_generator<coef_directive<Subject> > {};
      
    2. 接下来需要定义三个嵌套类型,subject_type,properties和attribute。
                      using subject_type = Subject;
                      using properties = typename Subject::properties;
      
                      template <typename Context, typename Iterator = unused_type>
                      struct attribute : traits::attribute_of<subject_type, Context, Iterator> {};
      
    3. 然后定义directive的构造函数。很明显,需要两个参数:generator和乘法运算符。
      				coef_directive(Subject subject, std::string string) :
      					subject{ std::move(subject) }, string{ std::move(string) }
      				{}
                      Subject subject;
                      std::string string;
      
    4. 最后是关键的generate函数,它决定了directive如何进行工作。
      				template <typename OutputIterator, typename Context, typename Delimiter, typename Attribute>
      				bool generate(OutputIterator& sink, Context& ctx, Delimiter const& d, Attribute const& attr) const {
      					using attribute_type = typename attribute<Context>::type;
      					if (!traits::has_optional_value(attr)) return false;
      					traits::IsOne<Attribute> IsOne;
      					if (IsOne(traits::extract_from<attribute_type>(attr, ctx))) return true;
      					return
      						subject.generate(sink, ctx, d, attr) &&
      						string_generate(sink, string) &&
      						delimit_out(sink, d);
      				}
      
    5. 然后是一个不太重要的函数what。
      				template <typename Context>
      				info what(Context& context) const {
      					return info("coefficient", subject.what(context));
      				}
      
  5. 最后需要将coef_和coef_directive关联起来。因为有两种用法,所以需要两次特化。
    			template <typename Subject, typename Modifiers>
    			struct make_directive<
    				tag::coef_, 
    				Subject, Modifiers
    			> {
    				using result_type = coef_directive<Subject>;
    				result_type operator()(unused_type, Subject const& subject, unused_type) const {
    					return result_type{ subject, "" };
    				}
    			};
    
    			template <typename Subject, typename Arg, typename Modifiers>
    			struct make_directive<
    				terminal_ex <tag::coef_, fusion::vector1<Arg>>, 
    				Subject, Modifiers,
    				typename enable_if<traits::is_string<Arg>>::type
    			> {
    				using result_type = coef_directive<Subject>;
    				template <typename Terminal>
    				result_type operator()(Terminal const& term, Subject const& subject, unused_type) const {
    					return result_type{ subject, fusion::at_c<0>(term.args) };
    				}
    			};
    
  6. 剩下一点点工作。其中IsOne是这里coef_directive用到的一个traits
    			template <typename Subject>
    			struct has_semantic_action<karma::coef_directive<Subject> > : unary_has_semantic_action<Subject> {};
    
    			template <typename Subject, typename Attribute, typename Context, typename Iterator>
    			struct handles_container<karma::coef_directive<Subject>, Attribute, Context, Iterator>
    				: unary_handles_container<Subject, Attribute, Context, Iterator> {};
    
    			template<typename T>
    			struct IsOne {
    				bool operator()(T const& t) const {
    					return t == 1;
    				}
    			};
    

完整代码:

#include <boost/spirit/home/support/terminal.hpp>
#include <boost/spirit/home/support/handles_container.hpp>
#include <boost/spirit/home/support/has_semantic_action.hpp>
#include <boost/spirit/home/karma/delimit_out.hpp>
#include <boost/spirit/home/karma/meta_compiler.hpp>
#include <boost/spirit/home/karma/detail/attributes.hpp>
#include <string>

namespace boost {
	namespace spirit {
		BOOST_SPIRIT_TERMINAL_NAME_EX(coef_, coef_type);

		template<>
		struct use_directive<karma::domain, tag::coef_> : mpl::true_ {};

		template<typename Arg>
		struct use_directive < karma::domain, 
			terminal_ex <tag::coef_, fusion::vector1<Arg>>
		> : traits::is_string<Arg> {};

		namespace karma {
			using spirit::coef_;
			using spirit::coef_type;

			template <typename Subject>
			struct coef_directive : unary_generator<coef_directive<Subject> > {
				using subject_type = Subject;
				using properties = typename Subject::properties;

				template <typename Context, typename Iterator = unused_type>
				struct attribute : traits::attribute_of<subject_type, Context, Iterator> {};

				coef_directive(Subject subject, std::string string) :
					subject{ std::move(subject) }, string{ std::move(string) }
				{}

				template <typename OutputIterator, typename Context, typename Delimiter, typename Attribute>
				bool generate(OutputIterator& sink, Context& ctx, Delimiter const& d, Attribute const& attr) const {
					using attribute_type = typename attribute<Context>::type;
					if (!traits::has_optional_value(attr)) return false;
					traits::IsOne<Attribute> IsOne;
					if (IsOne(traits::extract_from<attribute_type>(attr, ctx))) return true;
					return
						subject.generate(sink, ctx, d, attr) &&
						string_generate(sink, string) &&
						delimit_out(sink, d);
				}

				template <typename Context>
				info what(Context& context) const {
					return info("coefficient", subject.what(context));
				}

				Subject subject;
				std::string string;
			};

			template <typename Subject, typename Modifiers>
			struct make_directive<
				tag::coef_, 
				Subject, Modifiers
			> {
				using result_type = coef_directive<Subject>;
				result_type operator()(unused_type, Subject const& subject, unused_type) const {
					return result_type{ subject, "" };
				}
			};

			template <typename Subject, typename Arg, typename Modifiers>
			struct make_directive<
				terminal_ex <tag::coef_, fusion::vector1<Arg>>, 
				Subject, Modifiers,
				typename enable_if<traits::is_string<Arg>>::type
			> {
				using result_type = coef_directive<Subject>;
				template <typename Terminal>
				result_type operator()(Terminal const& term, Subject const& subject, unused_type) const {
					return result_type{ subject, fusion::at_c<0>(term.args) };
				}
			};
		}

		namespace traits {
			template <typename Subject>
			struct has_semantic_action<karma::coef_directive<Subject> > : unary_has_semantic_action<Subject> {};

			template <typename Subject, typename Attribute, typename Context, typename Iterator>
			struct handles_container<karma::coef_directive<Subject>, Attribute, Context, Iterator>
				: unary_handles_container<Subject, Attribute, Context, Iterator> {};

			template<typename T>
			struct IsOne {
				bool operator()(T const& t) const {
					return t == 1;
				}
			};
		}
	}
}
测试代码:
#include <boost/spirit/include/karma.hpp>
#include <boost/fusion/include/std_pair.hpp>
#include <iostream>
int main() {
	std::vector<std::pair<int, int>> v{ {1,1},{2,2},{3,3} };

	namespace karma = boost::spirit::karma;
	std::cout << karma::format((karma::coef_("*")[karma::auto_] << karma::auto_) % " + ", v) << "\n";


	return 0;
}
运行结果: