더 많은 정신 광기-파서 유형 (규칙 대 int_parser <>) 및 메타 프로그래밍 기술


80

질문은 맨 아래에 굵게 표시되어 있으며 문제는 끝 부분의 증류 코드 조각으로 요약됩니다.

내 유형 시스템 (유형 시스템이 유형에서 문자열로, 유형에서 문자열로 수행)을 단일 구성 요소 (Lakos에서 정의한대로)로 통합하려고합니다. 내가 사용하고 boost::array, boost::variant하고, boost::mpl이를 달성하기 위해. 내 유형에 대한 파서 및 생성기 규칙을 변형으로 통합하고 싶습니다. 정의되지 않은 유형, int4 (아래 참조) 유형 및 int8 유형이 있습니다. 변형은 variant<undefined, int4,int8>.

int4 특성 :

struct rbl_int4_parser_rule_definition
{
  typedef boost::spirit::qi::rule<std::string::iterator, rbl_int4()> rule_type;

  boost::spirit::qi::int_parser<rbl_int4> parser_int32_t;

  rule_type rule;

  rbl_int4_parser_rule_definition()
  {
    rule.name("rbl int4 rule");
    rule = parser_int32_t;  
  }
};

template<>
struct rbl_type_parser_rule<rbl_int4>
{
  typedef rbl_int4_parser_rule_definition string_parser;
};

위의 변형은 정의되지 않은 것으로 시작한 다음 규칙을 초기화합니다. 문제가 발생하여 50 페이지의 오류가 발생했고 마침내 추적 할 수있었습니다. Variant는 operator=할당 중에 사용 하고 a boost::spirit::qi::int_parser<>는 다른 사람에게 할당 할 수 없습니다 (operator =).

대조적으로, 정의되지 않은 유형에는 문제가 없습니다.

struct rbl_undefined_parser_rule_definition
{
  typedef boost::spirit::qi::rule<std::string::iterator, void()> rule_type;
  rule_type rule;

  rbl_undefined_parser_rule_definition()
  {
    rule.name("undefined parse rule");
    rule = boost::spirit::qi::eps;
  }
};

template<>
struct rbl_type_parser_rule<rbl_undefined>
{
  typedef rbl_undefined_parser_rule_definition string_parser;
};

문제의 증류 :

#include <string>
#include <boost/spirit/include/qi.hpp>
#include <boost/variant.hpp>
#include <boost/cstdint.hpp>

typedef boost::spirit::qi::rule<std::string::iterator,void()> r1;
typedef boost::spirit::qi::rule<std::string::iterator,int()> r2;

typedef boost::variant<r1,r2> v;

int main()
{
  /*
  problematic
  boost::spirit::qi::int_parser<int32_t> t2;
  boost::spirit::qi::int_parser<int32_t> t1;


  t1 = t2;
  */

  //unproblematic
  r1 r1_;
  r2 r2_;
  r1_ = r2_;

  v v_;
  // THIS is what I need to do.
  v_ = r2();
}

구체적인 파서와 규칙 사이에는 의미 론적 차이가 있습니다. 내 뇌는 지금 담배를 피우고 있으므로 실용주의에 대해 생각하지 않을 것입니다. 제 질문은이 문제를 어떻게 해결해야 하는가입니다. 문제를 해결하기위한 세 가지 접근 방식을 생각할 수 있습니다.

1 : 정적 함수 멤버 :

struct rbl_int4_parser_rule_definition
{
  typedef boost::spirit::qi::rule<std::string::iterator, rbl_int4()> rule_type;

  //boost::spirit::qi::int_parser<rbl_int4> parser_int32_t;

  rule_type rule;

  rbl_int4_parser_rule_definition()
  {
    static boost::spirit::qi::int_parser<rbl_int4> parser_int32_t;

    rule.name("rbl int4 rule");
    rule = parser_int32_t;  
  }
};

접근 방식 1이 스레드 안전 코드를 방지한다고 생각합니까? ?

2 : 정수 파서가 shared_ptr에 래핑됩니다. 타이핑 시스템에 대해 TMP를 사용하는 데는 두 가지 이유가 있습니다. 하나는 효율성이고 두 가지는 구성 요소에 대한 집중화입니다. 포인터를 사용하면 첫 번째 이유가 실패합니다.

세 : operator =는 no-op으로 정의됩니다. Variant lhs는 할당 전에 기본적으로 생성 되도록 보장 합니다.

편집 : 나는 옵션 3이 가장 합리적이라고 생각합니다 (연산자 = 작동하지 않음). 규칙 컨테이너가 생성되면 변경되지 않으며 유형의 규칙 특성을 오프셋에 강제로 할당 할뿐입니다.


1
옵션 1은 다음 경우에만 스레드가 안전하지 않습니다. parser_int32_t상태가 있고 참조가 사용됩니다. 상태 비 저장 또는 복사본이 만들어지면 안전합니다. 의미론에서 나는 사본이 만들어 졌다고 말할 것입니다.
Matthieu M.

상당히 혼란스러운 문제입니다. 파서 객체에 상태가 없는지 확신 할 수 없습니다. 또한 규칙 메커니즘에는 참조 및 구체적인 의미가 있습니다. 즉, 규칙은 다른 규칙에 대한 참조를 보유 할 수 있지만 구체적인 파서 자체가 될 수도 있습니다 (내 생각에). 이러한 의미가 구체적인 파서에 어떻게 적용되는지 모르겠습니다. .
Hassan Syed

@MatthieuM : 맞아요 .alias(). 사용 하지 않는 한 복사본이 만들어집니다 .
ildjarn

@ildjarn 그러나 규칙은 구체적인 파서가 아닙니다. : D 규칙의 내용은 구문 분석 트리에 해당하는 표현식입니다.
Hassan Syed

1
# 1이 스레드로부터 안전한지 여부를 평가할 수는 없지만 잊기 쉬운 조언을 제공 할 수 있습니다. 정적 할당은 컴파일러에서 한 번만 평가됩니다. 코드에서 약간의 확인을 상상해보십시오 (if (! evaluated_yet) evaluated () else noop ()). rbl_int4_parser_rule_definition의 관련 멤버 개체가 어디에서나 처음 호출 될 때 한 번 생성됩니다. 전역 싱글 톤을 사용하는 것과 거의 절대적으로 동일합니다. 동일한 문제를 해결하기 위해 해당 유형의 글로벌 싱글 톤을 사용할 수 있습니까? (초기 순서 무시 등) 그렇다면 스레드로부터 안전해야합니다.
std''OrgnlDave

답변:


11

질문의 전체 범위를 파악할 수 있을지 모르겠지만 여기에 몇 가지 힌트가 있습니다.

  • 로 주석 처리 된 줄은 나와 함께 // THIS is what I need to do.잘 컴파일됩니다 (문제가 해결 되었습니까? 실제로 규칙이 아닌 파서를 할당하는 것을 의미 했습니까?)

  • function-local의 초기화는 static최신 표준 (C ++ 11)에서 스레드로부터 안전하도록 정의되었습니다. C ++ 0x 스레딩에 대한 컴파일러 지원을 확인하십시오. (이니셜 라이저가 throw되면 초기화 명령문의 패스가 다시 초기화를 시도합니다.)

  • 규칙 alias()

    http://boost-spirit.com/home/articles/doc-addendum/faq/#aliases에 설명 된대로

    proto 표현식을 실제로 값 복사하지 않고도 규칙의 '논리적 사본'을 만들 수 있습니다. FAQ에서 말했듯이 이것은 주로 지연 바인딩을 허용하기위한 것입니다.

  • Nabialek 트릭은 당신이 기본적으로는 느리게 이후의 분석을위한 파서를 선택하고, 필요한 것을 정확하게 수 있습니다

    one = id;
    two = id >> ',' >> id;
    
    keyword.add
        ("one", &one)
        ("two", &two)
        ;
    
    start = *(keyword[_a = _1] >> lazy(*_a));
    

    귀하의 맥락에서 나는 keyword다음과 같이 정의 될 수 있습니다.

    qi::symbols<char, qi::rule<Iterator>*> keyword;
    

    시맨틱 액션의 속성으로 모든 작업을 수행합니다. 또는

    qi::symbols<char, qi::rule<Iterator, std::variant<std::string,int>() >*> keyword;
    
  • 동일한 유형으로 규칙을 가져옵니다 (기본적으로 이전 줄에 표시됨).

    이것이 제가 혼란스러워하는 부분입니다. 당신은 당신의 타입 시스템을 통합하고 싶다고 말합니다. 강력한 유형의 파서 (고유 속성 서명)가 필요하지 않을 수 있습니다.

    typedef boost::variant<std::string,int> unified_type;
    typedef qi::rule<std::string::iterator, unified_type() > unified_rule;
    
    unified_rule rstring = +(qi::char_ - '.');
    unified_rule rint    = qi::int_;
    
    unified_rule combine = rstring | rint;
    
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.