2014-04-24 9 views
1

일부 출력은 입력 값의 중요한 속성에 의존하는 boost :: spirit :: karma 생성기를 작성하려고합니다.boost :: spirit :: karma 입력 속성에 기반한 대체 선택

실제 문제는 더 큰 문법의 일부이지만,이 예제는 다른 번거로운 규칙 중 일부와 동일한 속성을 가지며 실제로 문제를 일으키는 문법 규칙 중 하나입니다.

나는 거의 내가 원하는 것부터 시작해서 거기서 일할 것이다.

#include <boost/spirit/include/karma.hpp> 
#include <boost/spirit/home/phoenix.hpp> 
#include <boost/fusion/adapted.hpp> 
#include <string> 
#include <vector> 

template<typename OutputIterator_T> 
struct Test_Grammar : 
    boost::spirit::karma::grammar<OutputIterator_T, std::vector<double>()> 
{ 
    Test_Grammar() : Test_Grammar::base_type(start), start(), value() 
    { 
     namespace karma = boost::spirit::karma; 

     start 
     = *(value % karma::lit(", ")) 
     ; 

     value 
     = (karma::double_) 
     ; 
    } 

    boost::spirit::karma::rule<OutputIterator_T, std::vector<double>()> start; 
    boost::spirit::karma::rule<OutputIterator_T, double()> value; 
}; 

template <typename OutputIterator_T> 
bool generate_output(OutputIterator_T& sink, std::vector<double> const& data) 
{ 
    Test_Grammar<OutputIterator_T> grammar; 
    return (boost::spirit::karma::generate(sink, grammar, data)); 
} 

int main (int, char**) 
{ 
    std::string generated; 
    std::back_insert_iterator<std::string> sink(generated); 

    std::vector<double> data{1.5, 0.0, -2.5, 
          std::numeric_limits<float>::quiet_NaN(), 
          std::numeric_limits<float>::infinity()}; 

    generate_output(sink, data); 

    std::cout << generated << std::endl; 

    return 0; 
} 

은, 상기 코드는 상기 테스트 데이터가 공급되면, 출력 단 1.5, 0.0, -2.5, nan, inf

를 생성하는 문법 제가

1.5, 0.0, -2.5, special, special

이며 원하는 결과를 규정

문법의 일부인 value

로 바꿉니다.
value 
    = (&karma::double_(std::numeric_limits<double>::quiet_NaN()) << 
     karma::lit("special")) 
    | (&karma::double_(std::numeric_limits<double>::infinity()) << 
     karma::lit("special")) 
    | (karma::double_) 
    ; 

무한대에 대해 원하는 동작을 얻습니다. 그러나 NaN은 비교에서 (NaN! = NaN) 속성을 갖기 때문에 NaN에 대해 원하는 결과를 얻지 못합니다. 그래서 isfinite()와 같은 fpclassify 매크로/함수를 사용하는 방법이 필요합니다.

내가 그러나

value 
    = (karma::eps(...) << karma::lit("special")) 
    | (karma::double_) 
    ; 

, 함수 호출, 함수 포인터의 모든 조합 문법의 value 부분을 대체하여 내가 원하는 것을 얻을 수있을, 나는이 재판을 한 주술을 결합한다 ... 부분에서 컴파일러 오류가 발생했습니다.

도움을 주시면 감사하겠습니다.

UPDATE :

는 Sehe (나는 수락) 우수한 일반적인 솔루션을 제공 하였다. 고맙습니다!

저는 특정 사용 사례에 대해 sehe의 대답을 더 단순화 할 수 있었고 다른 사람들을 위해 여기에 문서화하려고했습니다.

<boost/spirit/home/*>에서 <boost/spirit/include/*>에 포함 된 모든 변경과 그 포함 전에 BOOST_SPIRIT_USE_PHOENIX_V3를 형성 후, 다음 줄

BOOST_PHOENIX_ADAPT_FUNCTION(bool, isfinite_, std::isfinite, 1) 

을 첨가하고,이

value 
    %= karma::double_[karma::_pass = isfinite_(karma::_1)] 
    | karma::lit("special") 
    ; 
+0

우수 질문 박람회. 이런 식으로 모든 질문을 소개하는 것을 좋아합니다. 도움이 진행 중입니다! – sehe

답변

2

에 문법 value 일부 변경 의미 작업을 사용하여 double_ 생성기를 동적으로 "실패"시킵니다.

value 
    %= karma::double_ [ karma::_pass = !(isnan_(karma::_1) || isinf_(karma::_1)) ] 
    | karma::lit("special") 
    ; 

이제 isnan_isinf_을 어떻게 구현합니까?나는 Phoenix V3 (Boost의 모든 출시 버전에서 기본값이 될 것입니다)를 사용하는 것을 선호합니다 :

BOOST_PHOENIX_ADAPT_FUNCTION(bool, isnan_, std::isnan, 1) 
BOOST_PHOENIX_ADAPT_FUNCTION(bool, isinf_, std::isinf, 1) 

그게 전부입니다.

: 의미 론적 행동이 비록
  • include/*.hpp 대신 home/*.hpp
  • 전체 목록을 포함 자동 속성 전파를 얻기 위해 그것을 Live On Coliru

    노트

    • 사용 %=를 참조하십시오
      #define BOOST_SPIRIT_USE_PHOENIX_V3 
      #include <boost/spirit/include/karma.hpp> 
      #include <boost/spirit/include/phoenix.hpp> 
      #include <boost/spirit/include/phoenix_function.hpp> 
      #include <boost/fusion/adapted.hpp> 
      #include <string> 
      #include <vector> 
      #include <cmath> 
      
      BOOST_PHOENIX_ADAPT_FUNCTION(bool, isnan_, std::isnan, 1) 
      BOOST_PHOENIX_ADAPT_FUNCTION(bool, isinf_, std::isinf, 1) 
      
      template<typename OutputIterator_T> 
      struct Test_Grammar : 
          boost::spirit::karma::grammar<OutputIterator_T, std::vector<double>()> 
      { 
          Test_Grammar() : Test_Grammar::base_type(start), start(), value() 
          { 
           namespace karma = boost::spirit::karma; 
           namespace phx = boost::phoenix; 
      
           start 
           = *(value % karma::lit(", ")) 
           ; 
      
           value 
           %= karma::double_ [ karma::_pass = !(isnan_(karma::_1) || isinf_(karma::_1)) ] 
           | karma::lit("special") 
           ; 
          } 
      
          boost::spirit::karma::rule<OutputIterator_T, std::vector<double>()> start; 
          boost::spirit::karma::rule<OutputIterator_T, double()> value; 
      }; 
      
      template <typename OutputIterator_T> 
      bool generate_output(OutputIterator_T& sink, std::vector<double> const& data) 
      { 
          Test_Grammar<OutputIterator_T> grammar; 
          return (boost::spirit::karma::generate(sink, grammar, data)); 
      } 
      
      int main (int, char**) 
      { 
          std::string generated; 
          std::back_insert_iterator<std::string> sink(generated); 
      
          std::vector<double> data{1.5, 0.0, -2.5, 
                std::numeric_limits<float>::quiet_NaN(), 
                std::numeric_limits<float>::infinity()}; 
      
          generate_output(sink, data); 
      
          std::cout << generated << std::endl; 
      
          return 0; 
      } 
      

      출력

      1.5, 0.0, -2.5, special, special 
      
    +0

    당신이 흥미를 가질만한 또 다른 기술로 해결 한 farily 비슷한 질문이 나온다. ** [A : 부스트 카르마 - 소모적 인 술어] (http://stackoverflow.com/questions/23079143/boost-karma -non-consuming-predicate/23080311 # 23080311) ** – sehe