컴퓨터로 프로그램을 만들어보신 분이라면 컴파일러 혹은 인터프리터라는 단어를 한 번쯤은 들어보셨으리라 생각합니다. 그만큼 필수적인 단어이자 프로그램 이죠. 이 프로그램들이 없으면 실행이라는 것 자체가 불가능하니까요. 그럼 이 부분은 어떻게 인식을 하게 될까요? 아시는대로 컴퓨터는 자동적으로 알아서 프로그램을 생성하는 것이 아니라 코딩을 통한 작동명령을 제시해주어야 합니다. 그렇지 않으면 제대로 동작하지도 않을뿐더러 어떠한 역할을 수행하지도 못하겠지요. 그럼 어떻게 문법적 구문을 인식할까요? 그건 바로 오토마타를 통한 LALR(N), LR(N) 등의 문자열 파싱 방법등으로 처리하곤 합니다. 물론 학교에서만 배운 지식이기에 현재 자주 사용하는 gcc, C++ Builder, VS.net C++ 등의 컴파일러는 더 최신의 기술을 사용할지도 모릅니다. 하지만 기본은 아마도 비슷하겠죠? 그 문법적 문자열을 인식하는것은 일일히 코딩을 통하여 인식을 할 수도 있겠지만 그 것은 시간적으로도 키보드의 플라스틱으로도 낭비가 아닐 수 없습니다. 왜냐하면 yacc, lex 등의 좋은 툴이 많이 나와있기 때문이죠. 그 것과 더불어 boost에는 spirit 이라는 라이브러리가 존재합니다. 아마도 규모면에서는 약간 적은 프로그램에서 사용될 것 같습니다. 그럼 대충 어떻게 사용하는지 알아볼까요??
사용은 boost가 흔히 그렇듯이 boost에 속하는 hpp 파일을 include 시켜주어야 합니다. 목적 헤더파일은 core.hpp 파일입니다. 이 boost::spirit이 지원하는 부분은 문법을 분석하고 문법이 실행되는 것 까지 지원되나, 제가 거기까지는 사용을 못해봤으므로 일단 입력 문자열이 맞는지 검사하는 부분으로 진행하겠습니다. 일단 문자열이 파싱이 되느냐 되지 않느냐를 판단하기 위해서는 Rule 이라는 것이 필요합니다. 이 것이 무엇이냐 하면 오토마타때 배우는 EBNF( Extended Backus Naur Form )와 동일하게 생각하시면 됩니다. 즉 하나의 룰이 하나의 Non-Terminal을 표현한다고 보시면 됩니다. 위 코드는 제가 IPangYa 라이브러리에서 계산수식을 인식하여 정확한 것인지 파악하기 위하여 작성하였던 코드입니다. 나름 고생도 했죠. 룰을 만들기 위해서는 Rule 형태의 변수를 선언을 하게 되고 = 연산자 우측엔 >> 로 이루어진 룰의 조합이 존재하면 됩니다. 예를 들면 ch_p< wchar_t >( L'+') >> L'(' 이렇게 된다면 +( 이라는 문자열을 인식하는 룰이됩니다. C++ 에 존재하는 라이브러리 답게 *, | 연산자들을 오버로딩 하여 룰을 C++ 문법으로 간단히 표현할 수 있도록 제작이 되어있습니다. 그러므로 Expr에서 (abcc)*, (abcc)+ 와 같은 문자열 형식을 인식할 수 있습니다. 물론 ParseTree가 제작되기 위하여 *, /, +, - 등의 연산자 오버로딩을 정해주기 위하여 룰의 위치, 순서들을 먼저 조절 해주어야 합니다. 그게 약간의 사용법에 있어서 어려움이 될 수도 있습니다. 종이의 룰은 그냥 적으면 되지만 OOP라고는 하지만 내부 함수는 절차지향이니 그 순서를 생각한다면 반대로 생각해야할 부분도 있기 때문이죠. 만약 이러한 작업들을 통하여 Rule이 생성되었다면 boost의 parse 함수를 이용하여 최상위 룰과, 입력 문자열을 같이 넣어준다면 작업을 수행하고, parse_info의 변수에 에러 위치, 혹은 성공 여부를 채워주게 됩니다. 무척 간편하죠?^^ 필요한데 간단간단하게 제작을하여 사용하려면 이러한 것들을 사용하는 것은 어떻까요?
저도 STL에 있어서 많은 것을 알고 있는 사람이 아니고, 아직 미숙하지만 개중에 입출력과 관련해서 유용한 iterator가 있다는 것을 알아 유용하게 사용하고 있습니다. 제가 사용하는 부분은 std::copy 알고리즘에서 std::ostream_iterator를 이용한 개체 내용의 출력부분입니다. 이를 이용한 코드는 저번에도 포스팅이 되었던 내용입니다. 그런데 이 ostream_iterator, istream_iterator에 대하여 Effective STL에서 한 가지 주제가 존재하여서 그에 대한 내용을 보려고 합니다.
주로 사용은 이렇게 하더군요. 이게 가능한 까닭은 바로 다음과 같은 코드에 있다는 것이 중요합니다.
Copy 알고리즘에서는 iterator이기 때문에 *연산자를 통하여 값을 읽어오게 되고, ostreambuf_iterator에 = 대입 연산자로 값을 입력하기 때문에 그렇습니다. 실제로 대충 돌아가는 루틴이 눈에 보이시죠?^^. 이 istreambuf_iterator, ostreambuf_iterator와 istream_iterator, ostream_iterator가 다른 점은 기타 점도 있겠지만 출력에 있어서 값을 읽어오고 값을 출력하는 부분이 _Strbuf 를 이용한 직접 액세스 라는 점입니다.
buf_iterator와 다른 점은 <<, >> 연산자를 통하냐 통하지 않고 직접 액세스 하느냐에 따라 달렸습니다. 이 점 때문에 buf_iterator와 아닌 것의 속도 차이가 분명 존재하게 됩니다. 그래서 별 다른 문제가 존재하지 않고 buf_iterator로써 가능하다면 이 것을 사용하는 것이 속도상으로 좋다는 말이 여기서 나오는 말 일테구요. 하지만 장점이 있다면 단점도 존재하겠죠??
이 코드를 실행해 보면 에러가 발생합니다. 그 이유는 바로 rdbuf 개체를 통한 직접 액세스를 std::cout 에서 하기 때문에 그렇습니다. 대충 이러한 오류가 발생하는 까닭은 std::cout이 다음과 같이 정의 되어있기 때문인것 같습니다.
위와 같이 정의가 되어있는데 basic_ostream이 int 형으로 됨으로 인하여 생성되는 Class 는 typedef basic_ostream<_Elem, _Traits> ostream_type; 와 같은 형태인데. 이 형태를 int 형 buf_iterator 내부에 존재하는 클래스와 호환이 안되기 때문에 그런 것 같군요. 정확한 이유는 한 번 제대로 따라가봐야 할 것 같은데, 일단 결론만 얘기하자면 ostream_iterator는 int 형태로 잡아주어도 출력이 가능합니다. 위에 언급한 바와 같이 <<, >> 연산자로써 std::cout << 1; 을 실행하는 것과 동일하게 처리가 된다고 보시면 되니까요.
buf_iterator 와 아닌 것의 차이를 대충 보았는데, 간단한 몇 자의 타자의 차이임에도 불구하고 속도의 차이가 벌어질 수 있다는 점. 놓칠 수 없다고 생각되지 않나요?^^. 하나하나 살펴보다 보면 모든 부분을 정복할 수 있을 것이라 생각이 드는군요^^;
boost::shared_ptr 에 대해서는 이미 아시고 계실테고, 관심이 더욱 있으신 분, 혹은 이미 아시는 분들은 boost::scoped_ptr 에 대해서도 알고 계실겁니다. 그런데 C++ 에도 표준으로 원래 존재하는 Smart Pointer가 존재합니다. 그 이름은 auto_ptr<> 입니다. 어떤 것이냐구요??
대충 이렇게 사용합니다. 그럼 scope를 벗어 날때 auto_ptr의 ~소멸자가 호출됨으로 인하여 해당 보관 메모리를 해제하게 됩니다. 그런데 사용은 아주 간단하게 보이지만 신경 써주어야하는 점이 몇가지 있습니다. 무엇인가 하면 new와 new [], delete, delete []의 차이점 때문이죠.
소멸자에서 사용하는 부분이 delete [] 가 아닌 delete이기 때문에 실질적으로 생성자에서 new int 가 아닌 new int[ 100 ] 과 같은 구문을 사용하였을때, 하나의 개체를 제외하고는 소멸자가 호출되지 않습니다. 그리고 할당된 개체가 int, double 등의 기본 타입이 아닌 메모리를 또 개별적으로 관리하는 개체일 경우는 더 큰 문제가 발생하게 되겠죠. 또 하나 생길 수 있는 문제점이란 무엇일까요?? 바로 = operator overloading으로 인한 소유권 이전에 대한 문제점입니다.
이렇게 구현이 되어있기 때문에 _Right에 해당하는 개체는 _Myptr이 삭제 되고, 해당 개체가 소유권을 이전 받기 때문에 문제점이 존재할 여지가 있습니다. 어떤 부분에서냐고 물어보신다면 물론 이 것이 STL Container 과 같은 부분에서 사용이 되거나, 코드 도중 사용이 된다면 소유권이 넘어감으로 인하여 예상치 못한 점이 발생할 수 있다는 점이죠. 물론 코딩을 하는 사람치고 이런 것을 알아보지 않고 바로 사용하는 경우는 거의 없겠지만, 보통의 경우를 생각하고 코딩할 경우는 무척 문제가 될 수 있습니다. 실제로 STL Container의 경우 내부에서 대입문이 존재하는 알고리즘을 사용했을 때, 개체에 대한 메모리가 존재하지 않는 경우가 생깁니다. 이는 프로그램이 예상대로 동작하지 않는다는 소리가 됩니다. 그로인해 프로그램은 오작동 혹은 버그가 생길테구요. 이러한 점을 보았을 때엔, 편리하지만 문제점이 존재하는 소스 양날의 검이 되지 않기 위해선 사용하기 이 전에 파악을 먼저하고 사용해야 하지 않을까 하는 생각이듭니다.
예외 처리 개념 위키 백과 : http://ko.wikipedia.org/wiki/%EC%98%88%EC%99%B8_%EC%B2%98%EB%A6%AC 이번 예외처리는 중요한부분을 8 챕터로 More Effecitve C++ 에서 설명한다. 항목 9 : 리소스 누수를 피하는 방법의 정공은 소멸자이다. 항목 10 : 생성자에서는 리소스 누수가 일어나지 앟게 하자. 항목 11 : 소멸자에서는 예외가 탈출하지 못하게 하자. 항목 12 : 예외 발생이 매개변..
오늘 정말 희한한 현상을 겪게 되었습니다. 그게 뭐냐하면 바로 C#의 Main단에서 Try-Catch 구문을 걸었는데 제대로 Catch가 걸리지 않았던 것이죠. 그 때는 참 황당했습니다. 왜냐구요? Visual Studio.Net을 C++개발자 타입으로 설정하였을 때, 디버그 모드로 프로세스를 실행하면 Catch까지 잘 걸리는데, 단독 프로세스로 실행했을 경우엔 Catch에 제대로 걸리지 않더군요. Application.Run 내부적으로 처리하는 것 같았습니다.
코드는 대충 위와 같았습니다. Program.cs 에서 Application.Run으로 폼을 표시하고 내부적으로 메시지 펌핑(?)을 하게 될거라 생각을 한 후, Form1에서는 고의적으로 익셉션을 발생시키게 하였습니다. 여기서 바로 문제가 발생합니다. 어떤 거냐구요? Debug 모드로 실행을 한 번 해보시고, 단독 모드로 실행을 한 번 해보십시오. 단독으로 실행을 하면 C#의 기본 Exception MessageBox(?)가 뜰거에요. 절대 메시지 박스가 뜨지 않습니다. 그래서 결국 구글링을 하다가 찾게 된 페이지에서 하나의 해결책을 발견하고 적용시켜보니 제대로 작동이 되었습니다.
찾은 코드를 한 번 적용 시켜 보겠습니다. 그럼 Program.cs에 존재하는 코드에 한 줄이 추가됩니다.
Application.SetUnhandledExceptionMode를 ThrowException으로 설정하도록 하니 단독 모드에서도 테스트 Catch 메시지 박스 코드가 존재하는 Catch가 걸립니다. 아마도 추측하기론 Run 내부에서 Try-Catch를 설정하는 듯도 보이는 군요. 30분 정도 이 것때문에 삽질을 하긴 했지만 다음에 이런 일이 발생했을 때는 아주 유용할 것이라 생각합니다.^^
정신이 약간 오락가락 하네요. 너무 놀아서 그런가 봅니다. 오늘 12시쯤 회사로 출근을 하여 웹 서핑중 boost 의 1.35 버젼 릴리즈 소식이 보이더군요. 개인적으로 엄청 좋아하는 라이브러리이다 보니 반갑기 그지 없었습니다. 자자 어떤게 업데이트 되었냐구요??
새로 추가된 라이브러리
ASIO - 소켓과 소켓 iostream 지원과, Portable Networking
Bimap - Bidirectional Map 라이브러리( Associative Container 의 하나 )
Circula Buffer - ring 혹은 Cyclic 버퍼로 알려진 STL Container
Function - 함수 포인터, 함수 레퍼런스 등..
Fusion - tuple에 적용되어 작용하는 라이브러리, tr1에서 tuple이 정식으로 승격되었죠?
GIL - 일반적인 이미지 라이브러리
Interprocess - Shared Memory, Memory Mapped File, Process-Shared Mutex.. 등등 이게 가장 눈에 띄네요?^^
Intrusive, Math/ Special Function 등 여러가지가 추가되었습니다.
업데이트 된 라이브러리
Graph
Hash
Iostreams
multiarray
Multi-Index Containers
Serialization
Thread
Wave
Xpressive
대충 살펴보니 좋은게 많이 추가된 것 같습니다. 에구 있는 것도 다 파악 못했는데, 아주 주르르륵 쏟아져 나오는군요. 조금씩 살펴보면서 적재적소에 사용을 해봐야겠습니다. 회사에서 건네받은 다른회사가 진행하고 제가 유지보수 해야 될 프로젝트 부분을 보니 std::lexical_???? 쪽 함수를 사용하더군요. 마음이 설레어 옵니다^^;
댓글을 달아 주세요