2012-07-12 4 views
2

약간 특이한 문제가 발생했습니다. 다음 코드를 고려하십시오데이터 멤버의 수명을 한 메서드로 제한

class parser 
{ 
    lexer lex; 

public: 

    node_ptr parse(const std::string& expression) 
    { 
     lex.init(expression.begin(), expression.end()); 
     // ... 
     // call some helper methods 
     // return the result 
    } 

private: 

    // lots of small helper methods, many of them accessing lex 
}; 

구문 분석 방법은 init 방법으로 렉서를 초기화합니다. 그 전에 렉서는 사용할 수없는 "기본"상태입니다. 일반적으로, 하나는 건설 기간 동안 멤버를 초기화하기한다, 왜 나는 단순히이 작업을 수행하지 않습니다

class parser 
{ 
    lexer lex; 

public: 

    parser(const std::string& expr) : lex(expr.begin(), expr.end()) {} 

    node_ptr parse() 
    { 
     // call some helper methods 
     // return the result 
    } 
    // ... 
}; 

첫째, 이것은 클라이언트가 구문 분석 방법 훨씬 이해가되지 것 여러 번 호출 할 수 있음을 의미합니다.

둘째, 더 중요한 것은 아주 쉽게 될 수 있습니다 심각한 수명 문제 : 위의 코드에서

parser my_parser("1 * 2 + 3 * 4"); 
auto root = my_parser.parse(); 

는 렉서는 말에 존재 중단 임시 문자열 객체로 초기화됩니다 그렇기 때문에 다음 줄에 parse 메서드를 호출하면 정의되지 않은 동작이 호출됩니다.

이러한 두 가지 이유로 저는 같은 방법으로 초기화하고 구문 분석하려고합니다. 불행히도 결과를 반환해야하고 생성자가 결과를 반환 할 수 없기 때문에 생성자에서이 작업을 수행 할 수 없습니다.

기술적으로는 나는 또한 생성자 따라 소멸자 변경하면 parse 방법 내부 렉서를 구성하고 나중에 그것을 파괴 할 수 있습니다 :

class parser 
{ 
    static std::string dummy; 
    lexer lex; 

public: 

    parser() : lex(dummy.begin(), dummy.end()) 
    { 
     lex.~lexer(); 
    } 

    node_ptr parse(const std::string& expression) 
    { 
     new(&lex) lexer(expression.begin(), expression.end()); 
     // call some helper methods 
     lex.~lexer(); 
     // return the result 
    } 

    ~parser() 
    { 
     new(&lex) lexer(dummy.begin(), dummy.end()); 
    } 
    // ... 
}; 

을하지만 이것은 지금까지 내가 작성했던 추한 코드 매우 긴 시간. 예외도 아닙니다. 도우미 메서드가 throw되면 어떻게됩니까? 실제로 구문 분석 오류가 발생하면 그 일이 발생합니다.

그럼이 문제를 어떻게 해결해야합니까? parse 안에있는 로컬 렉서를 사용하고 lexer* 회원 포인트가 있습니까? boost::optional<lexer> 회원을 사용 하시겠습니까? 아니면 방금 init 방법으로 살아야합니까? 또는 결국 생성자에서 구문 분석을 수행하고 원하는 결과가 포함 된 "expection"을 throw해야합니까?

+0

호기심에서 벗어나, 거기에 무엇을 구축하고 있습니까? :) –

+2

범위가 단일 메서드 인 경우 왜 멤버로 유지하려고합니까? –

+0

@David 코드 주석에 쓴 것처럼 작은 헬퍼 메소드가 많이 있으며 그 중 많은 수가 렉스에 액세스합니다. – fredoverflow

답변

3

두 번째 예는 확실히 수행하지 않습니다. lexerParse()에 생성하고 포인터 또는 boost::optional을 저장하는 것이 좋습니다. 그러나 이것을 허용하려면 도우미 함수가 계속하기 전에 렉서가 유효한지 여부를 확인해야합니다. 나에게 지저분 해 보인다.

더 나은 것은 단지 Parse을 독립 실행 형 기능으로 설정하는 것입니다.나는이 호출자에게 분별, 그리고 문제 해결 구상 :

void parser_helper(lexer& lex) 
{ 
    ... 
} 

node_ptr Parse(const std::string& inp) 
{ 
    lexer lex(inp); 
    ... 
    parser_helper(lex); 
    ... 
    return ret; 
} 

또는 당신이 주변에 통과하기 위해 더 많은 상태가 있다면 ...

class parser_helper 
{ 
    lexer lex; 
    ... other state here 

public: 
    parser_helper(const std::string& inp) : 
     lex(inp) 
    { 
    } 

    ... helper functions here. 
    void helper_function() { } 
} 

node_ptr Parse(const std::string& inp) 
{ 
    parser_helper helper(inp); 
    ... 
    helper.helper_function(); 
    ... 
    return ret; 
} 

어느 쪽이든을, 렉서 단지를해야한다 자동 변수는 Parse 함수에 있습니다.

발신자가 기대하는 인터페이스는 단일 기능입니다. Parse의 내부에는 상태/도우미 기능이 있기 때문에 호출자가 클래스를 처리하지 않아도됩니다.

2

parse (실제로는 lex)이 단순한 기능을 나타내지 않아도됩니다. 또한 parse에 대해 표현을 사용하거나 lexer으로 초기화 할 이유가 없습니다.

편집 :

또는 단순히 구문 분석의 스택에 람다로를 만들 수 있습니다. 그러나 필자가 말했듯이 파서가 필요하다는 것을 알 수 있습니다. 그러나 파싱 자체의 멤버 메서드 밖에 존재할 필요가없는 것 같습니다. 왜 클래스 외부로 나가는 방법을 리펙토링하지 않는지 질문합니다. 예 :

class parser { 
    lexer l; 
    // stuff 
}; 
node_ptr parse(...) { 
    parser p(...); 
    return p(); 
} 
+0

As I ' 코드 주석에 쓰여진 "작은 도우미 메소드가 많이 있는데, 그들 중 많은 사람들이 렉스에 접근"합니다. 그래서 수업이 필요합니다. 그리고 저는 평생 문제를'parse'가 생성자가 아닌 표현식을 사용하기를 원하는 이유로 썼습니다. – fredoverflow

+0

@FredOverflow : 유효한 이유는 아니며 매개 변수가있는 자유 함수를 사용할 수 있으므로 파서 클래스의 필요성을 완전히 제거 할 수 있습니다. –

+2

또는 단순히 렉서가 존재하는 두 번째 클래스를 생성하고'parse' 프리 함수를 위해 스택에 인스턴스화하면됩니다. – Puppy

0

위의 코드에서 렉서는 줄 끝 부분에 존재하지 않는 임시 문자열 개체로 초기화되므로 다음 줄에서 parse 메서드를 호출하면 정의되지 않은 동작이 호출됩니다.

그건 의미가 없습니다. 임시 문자열의 사본을 만들어 lexer에서 사용할 수 있습니다.

+0

"그건 이해가 안돼"라는게 무슨 소리 야?누군가가 생성자에 문자열 리터럴을 전달하면 정확히 무슨 일이 일어날 것입니다. – fredoverflow

+0

@FredOverflow :하지만 문제가되지는 않습니다. 렉스가 존재하는 한 존재할 문자열로 작업 할 수 있기 때문입니다. 그렇게하는 것을 멈추게하는 것은 무엇입니까? – Nawaz

+1

@FredO : 그러나 그는이 문자열을 단순히 복사 할 수 있다는 정확한 포인트를 절대적으로 제시합니다. – Puppy

0

parse 내부에 로컬 렉서를 사용하고 렉서 * 멤버를 가리키는 포인터가 있습니까?

내 투표가 있습니다. 그렇게하면 평생 동안 완전히 통제 할 수 있습니다.