Skip to main content

Posts

Showing posts from July, 2006

C++ of the Day #18 - Returning local variable

이번 글도 역시 뉴스그룹에서 가져왔습니다. (( c.l.c.m:Returning local variable 그런데 이 질문을 올린 Minkoo Seo라는 이름을 보니 얼마전 댓글을 달아주신 서민구님이네요. :-))) Question #include <iostream> using namespace std; const char *foo() { string f = "fo"; f += "o"; return f.c_str(); } int main() { cout << foo() << endl; // I need const char * here!!! return EXIT_SUCCESS; } 질문은. 위의 코드와 같이 local 변수를 const char*로 리턴하면 괜찮다고 하던데 정말인가요? (어디서 char*로 리턴하는건 안되지만 const char*로 리턴하는건 된다는 내용을 읽은 것 같아요.) 만약 안 괜찮다면 foo()함수는 어떻게 만들어야 할까요? Answer 사실 이 질문들에 대한 답은 간단합니다. 괜찮지 않습니다. local 변수인 string 객체는 이미 파괴되었기 때문에 cout 에서 그 포인터를 읽을 때는 이미 존재하지 않는 객체에 access하는 것이기 때문입니다. 간단히 리턴 타입을 string으로 바꿔주면 됩니다. 물론 리턴할때도 f.c_str() 대신에 그냥 f 를 리턴하면 되겠죠? 이 질문에서 재밌게 생각되는 점은 원래 질문 내용보다는 어디선가 이런 내용을 읽은 것 같다는 내용입니다. 사실 내용은 전혀 다르지만 비슷한 내용이 있지요. 바로 const reference에 대한 casting입니다. 아마 const reference 를 본것을 const pointer로 착각하신건 아닐까 하는 생각이 드네요. 간단히 설명하면 다음과 같습니다. 먼저 아래 코드를 보시죠. class Widget; Widget getWidget();

Re: Concatenating items in the array

Concatenating items in the array 글에 대한 trackback입니다. 댓글에 <...> 괄호를 사용하니 이상해져서요. -_-; C++을 주로 사용하는 개발자로써 언어에 대한 변명(?)을 해볼까 합니다. C나 C++은 위의 join과 같은 작업은 언어 차원에서 지원하지 않습니다. 왜냐하면 orthogonal하지 않기 때문이죠. 쉽게 다음과 같이 얘기할 수 있을 것 같습니다. "도대체 int array와 string이 무슨 관계가 있지?" 즉, C++이 생각하기에는 int array를 string으로 만드는 것은 int array가 할 일이 아닌거죠. 따라서 이러한 작업은 사용자가 직접 해주어야 합니다. (물론 여기서 사용자란 라이브러리 제작자들도 포함되겠죠?) 제가 C++로 작성해 본 join 함수는 아래입니다. iterator에 대해 decrement operator를 사용하기 위해 bidirectional iterator를 사용합니다. #include <iostream> #include <sstream> #include <vector> #include <boost/lambda/lambda.hpp> template <class BidirectionalIterator> std::string join(BidirectionalIterator first, BidirectionalIterator last, const std::string& sep = " ") { using namespace boost::lambda; std::stringstream ss; std::for_each(first, --last, ss << _1 << sep); ss << *last; return ss.str(); } int main() { int items[] = { 1, 2, 3,

C++ of the Day #17 - expression? statement?

뉴스그룹에 다음과 같은 질문이 올라왔네요. (( c.l.c.m:The for-init-statement in a for statement )) Question for ( for-init-statement condition opt ; expression opt ) statement C++98 문서에 보면 for expression은 위와 같이 정의되어 있는데 for-init-statement 부분을 보면 optional이 아닙니다. 그렇다면 for (;;) 와 같은 문장은 standard compliant가 아닌가요? 그리고 for-init-statement 부분을 보면 semi-colon이 빠져 있는데 표준 문서의 오류인가요? Answer 먼저 결론부터 말하자면 위의 두 질문의 답은 모두 NO 입니다. 먼저 for-init-statement가 무엇인지는 문서의 뒤쪽에 있는 Annex A Grammar summary 에서 찾을 수 있으며 다음과 같습니다. ((여기서 (opt)는 opt 입니다. code 태그안에서 sub 태그가 안되서... ;-))) for-init-statement: expression-statement simple-declaration expression-statement: expression(opt) ; simple-declaration: decl-specifier-seq(opt) init-declarator-list(opt) ; 보시는 바와 같이 for-init-statement는 expression-statement이거나 simple-declaration인데 둘다 semi-colon으로 끝나고 있습니다. 따라서 for 문을 정의할 때 for-init-statement뒤에 semi-colon이 없었던 것이죠. 그리고 위의 두 경우 모두 semi-colon 앞은 모두 optional임을 알 수 있습니다. 따라서 for-init-statement는 그냥 semi-colon만으로도 standard compliant한 문장

Erich Gamma: A pattern of success

Erich Gamma 하면 제일 먼저 뭐가 생각나시나요? 전 제일 먼저 Design Pattern 책의 저자였지라는 생각이 나고 다음은 eclipse 를 개발한 사람이라는 생각이 듭니다. 이렇게 두가지만 했어도 대단한데 알고 보니 Unit Testing 를 만들어낸 사람의 한명이기도 했군요. 이제까진 Unit Testing이니 eXtreme Programming이니 하면 Kent Beck만 생각했었는데요. 지금은 Jazz라는 eclipse 기반의 collaborative software development을 위한 framework을 만들고 있다고 하네요. 벌써 유명한 작업 같은데 저만 모르고 있었는지... 한번 들여다봐야겠네요. ((IBM에서 개발중인데 core는 open source 화 예정이라는군요.)) C++개발자로써 eclipse를 볼때마다 Java 개발자가 너무 부럽습니다. 거의 유일하게 Java가 더 좋아보이는 이유이자 가끔 Java를 공부하는 이유입니다. 사실 몇년전에 BlogReader 를 만들었던 이유도 Java공부보다는 eclipse를 써보고 싶어서에 더 가까웠던 것 같네요.. eclipse 덕분에 점점 더 많은 좋은 툴들이 Java용으로만 나오는 것 같아요. ㅜㅜ 하나의 큰 성공에 안주하지 않고 계속 큰 성과를 내고 있는 Erich Gamma씨가 너무 멋있어 보입니다. :-) 원문 보기

C++ of the Day #16 - Question about STL iterator usage

역시 뉴스 그룹에서 하나 가져왔습니다. (( c.l.c.m:Question about STL iterator usage )) 오늘 내용은 기술적이라기보단 역사적인 것이네요. :-) Question 질문은. iterator를 사용할때 대부분의 사람들은 아래 코드의 1)번보다는 2)번과 같은 방식으로 사용하는 것 같아 보입니다. 이것은 단지 취향 문제인가요 아니면 뭔가 그럴듯한 이유가 있나요? i->func(); // 1) (*i).func(); // 2) Answer 처음 질문을 보고 저는 "이거야 뭐 그냥 취향 문제 아냐?" 라고 생각했습니다만 P.J. Plauger 씨가 역사적인 이유를 들어 설명을 해주셨네요. 예전에는 컴파일러가 template class를 instantiation할 때 어떤 member function이 실제 호출되는지 여부에 관계없이 모든 member function을 instantiation 할 수 있었다고 합니다. ((아마도 1998년의 표준화보다도 훨씬 이전인듯 합니다.)) 따라서 특정 iterator 클래스를 정의할 때 operator->() 를 정의하는 것은 안전하지 않았습니다. 다음 코드를 보면 이해가 쉬울 것 같습니다. template <class T> class Iterator { public: T& operator*() { // for int - *i return *data_; } T* operator->() { // for int - i-> what? return data_; } private: T* data_; }; 위의 Iterator 클래스가 만약 어떤 class 타입에 대해 instantiation 되었다면 두 operator 모두 문제 없이 instantiation될 수 있습니다. 왜냐하면 두 문법 모두 그 class의 member 에 접근하기 위해 사용되는 문법이기 때문입니다. 하지만 위의 클래스가

C++ of the Day #15 - Question on type deduction

오늘도 역시 뉴스그룹에서 하나 가져왔습니다. (( c.l.c.m:Question on type deduction )) Question void augment(int& outNumber) { ++outNumber; } template<typename R, typename A1> R exec(R (*pfunc)(A1), A1 arg1 ) { return pfunc(arg1); } int main() { int number = 10; exec(augment, number); // <-- compiler error } 위의 코드에서 main() 안의 첫번째 exec 함수 호출을 보면 R 타입과 A1 타입을 컴파일러가 유추하게 되는데 augment 인자 타입이 void (*)(int&) 이므로 R은 void, A1은 int& 가 되어 다음과 같이 instantiation될 것으로 생각됩니다 void exec(void (*pfunc)(int&), int& arg1) 하지만 예상과는 달리 컴파일 에러가 발생하는군요. 이제 질문입니다. 왜 위와 같이 컴파일 에러가 발생할까요? 해결 방법은 무엇일까요? Answer 먼저 이유를 살펴보겠습니다. 컴파일러는 exec 함수를 찾기 위해 먼저 각각의 인자에 대해 type deduction을 수행합니다. exec(augment, number) 함수 호출에서 컴파일러는 augment 인자는 void (*)(int&) 타입이라는 것을 찾아내고 number 인자는 int 라는 것을 찾아냅니다. 여기서 중요한 점은 number는 int 이지 int& 가 아니라는 점입니다. 이렇게 찾아낸 정보를 가지고 적당한 함수를 찾아보게 됩니다. 컴파일러는 위의 template 함수인 exec 를 찾아내게 되고 자신이 유추한 타입을 대입하여 올바른지 확인합니다. 하지만 이 경우에는 첫번째 인자와 두번째 인자가 A1 타입에 대해 동의하지 않고 있다는 것을

C++ of the Day #14 - Overload resolution before access checking

오늘 내용은 뉴스 그룹에서 가져왔습니다.(( c.l.c.m:overload resolution before access checking )) Question struct S { public: void foo(long); private: void foo(int); }; .... S s; s.foo(1); // error, S::foo(int) is private 위의 코드와 같이 s.foo(1) 문은 컴파일 에러가 발생합니다. 이유는 C++ 컴파일러가 access checking을 하기 전에 overload resolution을 먼저 수행하기 때문입니다. 즉, 접근 권한에 관계 없이 두개의 foo 함수중 어떤 것이 인자 '1'에 더 적합한가를 확인하는데 이때 literal 1은 int이기 때문에 foo(int) 를 수행하도록 결정됩니다. 하지만 함수를 결정한 후 접근 권한을 검사해보면 이 foo(int)는 private이기 때문에 컴파일 에러가 발생하는 것이죠. 만약 overload resolution이 접근 가능한 멤버에 대해서만 동작한다면 위의 코드는 컴파일 에러 없이 foo(long)을 호출할 수 있었겠죠? 오늘의 질문은 이것입니다. C++에서 컴파일을 이렇게 하는 타당한 이유는 무엇인가? 다른 말로 하자면, private 멤버는 왜 보이지 않는 것(invisible)이 아니라 접근 불가능(inaccessible)인가? Answer 먼저 첫번째 질문에 대한 답은 애석하게도 "뭐 특별한 이유가 있었던 것은 아니다"입니다. :-( 그래도 가장 그럴듯한 답은 아래 내용이 아닐까 합니다. ((D&E 책에 보면 Bjarne Stroustrup씨가 왜 이렇게 설계했는지 기억을 못한다고 하네요. ;-) )) The design goal was that, as long as the code remains well-formed, changing a member's access level

리눅스 이쁘게 더 이쁘게~

오늘 JayBlog 님의 도움으로 집의 리눅스를 이쁘게 꾸며보았는데 너무 맘에 드네요. 사실 사용하고 있는 Ubuntu에서는 기본으로 한글을 지원하기 때문에 로그인시 설정만 하면 한글을 사용할 수 있으나 너무 이쁘지가 않았습니다. 프로그래밍하는데 뭐 이쁜게 문제냐라고 생각할수도 있으나 개발자들.. 나름대로 까다로운 사람들이지 않습니까? 제가 특히 그런지 기능이 좋아보이는 프로그램도 이쁘지 않으면 잘 사용하지 않습니다. :-) 그럼 제가 적용한 방법을 적어보겠습니다. 참고로 제 Ubuntu 버전은 6.06 LTS - Dapper Drake 입니다. 먼저 이 사이트 의 내용대로 따라합니다. 다음으로 맑은 고딕체를 구해 인스톨합니다. ((이건 알아서들 구하셔야됩니다. 죄송하군요. Windows Vista에서 기본으로 사용될 폰트라는군요. ;-) )) $ sudo mkdir /usr/share/fonts/myfonts $ sudo cp /home/user/Desktop/*.ttf /usr/share/fonts/myfonts/ $ sudo fc-cache -f 언어를 한국어로 설정하고 로그인합니다. 시스템 -> 기본설정 -> 폰트 메뉴에서 글꼴을 아래와 같이 변경합니다. Firefox의 편집 -> 환경설정 -> 내용 -> 글꼴 메뉴에서 글꼴을 다음과 같이 변경합니다. 이것으로 셋팅 끝입니다. 그럼 제 리눅스 구경해보실까요? :-) 이글을 쓰고 있는 flock 실행 화면입니다. 그림이나 사진을 flickr 같은 곳에 올려놓고 글쓰기에 편리해서 자주 사용하고 있습니다. 전체 바탕 화면입니다. 보시라고 몇가지 프로그램을 실행시켜두었습니다.

C++ of the Day #13 - Boost.Python 사용하기 #4

오늘은 Boost.Python에 관한 마지막 글로 이전 글들 ((http://ideathinking.com/blog/?p=35 http://ideathinking.com/blog/?p=38 http://ideathinking.com/blog/?p=39 )) 에서 언급되었던 자동 코드 생성 도구인 Pyste에 대해 살펴보도록 하겠습니다. Requirements 먼저 준비해야 할 것들은 다음과 같습니다. pyste <your path>/boost_1_33_1/libs/python/pyste/install 디렉토리에서 다음과 같이 pyste를 인스톨합니다. python setup.py install elementtree library Cmake GCCXML gccxml 은 다운로드 버전보다는 cvs 버전을 사용하시는게 좋습니다. 환경에 따라 이전 버전은 컴파일이 안되더군요. ((집에서 사용하는 Ubuntus에서는 컴파일이 되지 않아 cvs 버전을 사용했습니다.)) 다음과 같이 인스톨합니다. $ mkdir gccxml-build $ cd gccxml-build $ cmake ../gccxml $ make $ make install Usage 먼저 이번에 사용할 코드는 이전에 사용하였던 것과 같은 Point 클래스 코드입니다. #include <string> #include <sstream> #include <iostream> class Point { public: Point(int x = 0, int y = 0) : x_(x), y_(y) { } void x(int x) { x_ = x; } void y(int y) { y_ = y; } int x() const { return x_; } int y() const { return y_; } void set(int x = 0, int y = 0) { x_ = x; y_

C++ of the Day #12 - Boost.Python 사용하기 #3

지난번 글들 ((http://ideathinking.com/blog/?p=35 http://ideathinking.com/blog/?p=38)) 에 이어 이번에는 Boost.Python에서 C++ 함수 사용시에 신경써야 할 점 몇가지를 알아보겠습니다. Call Policies 먼저 이해를 돕기 위해 이전에 사용했던 Point 클래스에 아래의 두개의 함수를 추가합니다. Point* clone() const { return new Point(x, y); } Point& get_this() { return *this; } clone() 함수는 자신을 그대로 복제한 새로운 객체를 리턴하는 함수이고 get_this()는 자신을 그대로 리턴합니다. 하지만 python에서는 함수가 리턴하는 것이 새로 생성된 객체인지 - 이 경우 python 런타임은 이 객체를 새로운 obejct로써 관리해야 합니다. 기존에 있던 객체인지 - 이 경우에는 기존의 객체와 이 객체를 같은 object로써 관리해야 합니다. 를 알아야 합니다. 따라서 다음과 같이 python에게 실제 리턴되는 객체의 특성을 설명해주어야 합니다. .def("clone", &Point::clone, return_value_policy ()) .def("get_this", &Point::get_this, return_value_policy ()) 위의 선언을 통해 clone() 함수는 새로 생성된 객체 포인터를 리턴하고 get_this() 는 기존의 객체를 리턴한다는 것을 python에 알려줄 수 있습니다. 그럼 실행해보기에 앞서 실제 객체의 lifetime이 어떻게 되는지 알아보기 위해 Point 클래스의 소멸자에 cout을 넣도록 하겠습니다. virtual ~Point() { std::cout << "~Point()" << std::endl; } 다음은 실행 결과입니다. >

C++ of the Day #11 - Boost.Python 사용하기 #2

지난번 글 ((http://ideathinking.com/blog/?p=35)) 에 이어 이번에는 C++로 작성한 클래스를 python에서 사용하는 방법에 대해 알아보겠습니다. 예제까지 원문 ((http://www.boost.org/libs/python/doc/tutorial/doc/html/python/exposing.html)) 과 똑같으면 재미가 없으므로 조금 바꾸어서 작성해보았습니다. ;-) 멤버 함수 사용하기 #include <string> #include <sstream> struct Point { Point() : x(0), y(0) { } void set_xy(int x, int y) { this->x = x; this->y = y; } std::string to_s() const { std::ostringstream ss; ss << "(" << std::dec << x << ", " << y << ")"; return ss.str(); } int x, y; }; #include <boost python.hpp> using namespace boost::python; BOOST_PYTHON_MODULE(point) { class_<point>("Point") .def("set_xy", &Point::set_xy) .def("to_s", &Point::to_s) ; } 위의 코드에서는 간단히 Point 클래스의 set_xy()와 to_s() 함수를 python에서 사용할 수 있도록 Boost.Python을 사용하였으며 간단히 class_ 의 def() 문법을 사용하여 멤버 함수를 내보낼 수 있음을 알 수 있습니다. 아래는 이 클

C++ of the Day #10 - null pointer constant

Question 오늘은 오랫만에 뉴스그룹에서 하나 가져왔습니다. (( comp.lang.c++.moderated:conversion from int to int * of zero in comma operator )) int* p = (0, 0); 질문은 - 위의 코드가 컴파일이 되지 않는 이유는 무엇인가요? ((g++에서는 -pedantic 옵션을 주어야 합니다. 참고로 -pedantic 옵션은 표준에 부합되지 않는 모든 경우에 대해 에러를 발생시키고자 할때 사용됩니다.)) Answer 위의 코드는 얼핏 봐서는 아무 문제 없이 컴파일이 되어야 할 것 같습니다. 오른편의 comma operator는 왼쪽부터 오른쪽으로 수행되며 마지막으로 제일 오른쪽의 식의 결과가 리턴되는 operator이기 때문에 이 식의 리턴 값은 0 이 됩니다. 게다가 이 0 은 integral constant expression이기 때문이죠. 그럼 왜 이 문장이 컴파일이 안되는지 알아보겠습니다. 먼저 표준 문서의 4.10(1) 을 보면 null pointer constant에 대한 다음과 같은 내용이 나옵니다. A null pointer constant is an integral constant expression (5.19) rvalue of integer type that evaluates to zero. A null pointer constant can be converted to a pointer type; the result is the null pointer value of that type and is distinguishable from every other value of pointer to object or pointer to function type. Two null pointer values of the same type shall compare equal. The conversion of a null pointer constant to a poin