C++ 표준 문서 N2798의 14.3.1의 4번째 항목을 보면. 위와 같은 내용이 있다.
 ( 더 최신 문서인 N3092 에는 위 항목이 빠져 있음. )

 해석을 해서 옮기고 싶지만..
 괜히 혼란만 가중 시킬것 같아서.. 패스하고..

  아무튼 자세히 보면.. 정말 해괴망측한 부분이 있는데
  그 부분을 조금 더 살펴 보자면... 아래와 같은 코드가 있다고 했을 때

    1 template<typename T>

    2 struct X

    3 {

    4     static void f(const T& t)

    5     {

    6         t.a = 100;

    7     }

    8 };

 
 함수 f()는 const로 받은 변수의 값을 변경하고 있다.
 얼핏 보아도 이상한 코드다.
 (  아직 template가 instantiation이 되지 않아서 이 부분만 가지고는 컴파일 에러가 나지 않는다.)


 코드를 조금 더 작성해 보면..

    9 struct S

   10 {

   11     int a;

   12 };

   13 

   14 int main(int argc, char* argv[])

   15 {

   16     S s;

   17     X<S>::f(s);

   18 


이제 아마 Line 17에서 readonly인 a에 값을 넣으려고 했다고 해서 컴파일 에러가 날 것이다.
근데 C++0x 부터는 이를 회피할 수 있는 아주 간단한 방법이 생겼다.
단순히 ' & ' 하나만 추가 하면 된다.


   16     S s;

   17     X<S&>::f(s);

   18 

   19     cout << s.a << endl;


 이제 함수 f()는 
 void X::f(S& t) 와 같이 파라미터의 타입이 정해지고, const 속성을 날려버리게 된다. 우왕ㅋ굳ㅋ
 ( 물론, 이렇게 만들어서 const 속성을 없애서 쓰자는 이야기는 절대 아님...)



 C++의 복잡도는.. 날이 갈 수록 올라가고 있다.


끝으로 Quiz..
각각 파라미터 타입은 어떻게 될까요? ( Line 11 ~ Line 29 )
참고로 gcc 4.5.0과 VS2010 에서 둘다 돌려봤는데. 같게 나왔습니다.

    1 template<typename T>

    2 struct R

    3 {

    4     static void A(T&){}

    5     static void B(const T&){}

    6     static void C(T&&){}

    7     static void D(const T&&){}

    8 };

    9 

   10 {

   11     R<int&>::A(?)

   12     R<int&>::B(?)

   13     R<int&>::C(?)

   14     R<int&>::D(?)

   15 

   16     R<const int&>::A(?)

   17     R<const int&>::B(?)

   18     R<const int&>::C(?)

   19     R<const int&>::D(?)

   20 

   21     R<int&&>::A(?)

   22     R<int&&>::B(?)

   23     R<int&&>::C(?)

   24     R<int&&>::D(?)

   25 

   26     R<const int&&>::A(?)

   27     R<const int&&>::B(?)

   28     R<const int&&>::C(?)

   29     R<const int&&>::D(?)

   30 }



Posted by U_Seung

r-value reference 쓸려고 테스트를 해봤습니다.


#. 일단 임의의 문자열(string) N개를 vector에 넣고..

    1     static const size_t N = 100000;

    2     static const size_t MaxLen = 100;

    3     static const char range[] = "abcdefghijklmnopqrstuvwxyz_0123456789";

    4 

    5     struct rnd_gen

    6     {

    7         char operator()() const

    8         {

    9             return range[std::rand() % (_countof(range) - 1)];

   10         }

   11     };

   12 

   13     std::vector<std::string> v(N);

   14     for(int i=0; i<N; ++i)

   15     {

   16         std::generate_n(

   17             std::back_inserter(v[i]),

   18             std::rand() % MaxLen + 1, rnd_gen());

   19     }



#. Sorting 해보고

   20     {

   21         boost::timer t;

   22         std::sort(v.begin(), v.end());

   23         std::cout << "Sort: " << t.elapsed() << std::endl;

   24     }



#. Join 해봤습니다.

   25     {

   26         boost::timer t;

   27         std::string result = boost::algorithm::join(v, ",");

   28         std::cout << "Join: " << t.elapsed() << std::endl;

   29     }




-[ VS 2008 ]--------------
Sort: 0.178
Join: 0.104

-[ VS 2010 ]--------------
Sort: 0.079
Join: 0.029


 C++0x 이전에는 위 처럼 코딩 하지 말라고 했겠죠..
 사실 부자연 스러울 것이 없는 코드인데..
 하지 말라고 하는게 안타까운 상황이었습니다..ㅋ




Posted by U_Seung

C++로 프로그래밍을 하다보면 딱히 변수 이름이 중요하지 않을 때가 많이 있습니다.
주로 생성자 혹은 파괴자를 이용한 RAII 패턴을 사용하는 상황이 그렇습니다.


임의의 변수명은 Macro를 이용하여 구현합니다.

 

#include <boost/preprocessor/cat.hpp>

#define RANDOM_NAME BOOST_PP_CAT(_random_, __COUNTER__)

 


선언은 위와 같이 하고
대략적인 사용은 아래와 같이 합니다.

 

namespace NS

{

    bool Func(Arg* arg)

    {

        SomeGuard<Arg> RANDOM_NAME(arg);

 

        (...)

    }

 

    static Initialization RANDOM_NAME;

}

 



이렇게 만들어진 변수 명은 '해당 변수를 생성자와 파괴자의 동작만 사용하겠다' 라고 명시적으로 지정하는 역할도 담당하게 됩니다. 변수 명을 알 수가 없으니까 강제로 다른 용도로 사용하려고 해도 할 수가 없겠지요.



Posted by U_Seung

제가 구독하는 블로그 중에
rein님의 C++0x를 써서 Closure 다시 만들기와 C++기반의 closure를 보고
제가 쓰고 있던 방법도 공개 합니다.


사용할 수 있는 곳은 여러 곳이 있겠습니다만..
다른 곳으로 Message를 Passing 한다던가 Timer를 쓴다던가 할 때
유용하게 쓸 수 있겠습니다.




typedef
function<void (void)> Closure;


C++0x가 지원되면 std.function을 써도 되고
그렇지 않으면 boost.function을 쓰면 됩니다.




void
freef(int a, int b)

{

    cout << a << " + " << b << " = " << a+b << endl;

}

 

struct Class

{

    Class(int a): a_(a) {}

 

    void memf(int b)

    {

        cout << a_ << " - " << b << " = " << a_-b << endl;

    }

 

    int a_;

};

 

vector<Closure> closures;

{

    int a = 10;

    closures.push_back(bind(freef, a, 4));

    closures.push_back(bind(&Class::memf, new Class(a), 2));

//  closures.push_back([=](){ cout << a << endl; });

}

 

foreach(Closure& closure, closures)

{

    closure();

}



마찬가지로 std.bind 나 boost.bind를 사용하고,
C++0x가 지원되면 lambda를 추가적으로 사용할 수 있습니다.




Posted by U_Seung


 둘다 깔아서 써보고 있는데 괜찮네요 :)

 이제 tr1뿐만 아니라.
 auto, decltype, lambda, strongly typed enum, Rvalue references 등등의 
 C++0x의 훌륭한 Feature 들을 양쪽에서 사용할 수 있게 되었습니다.

 저런 것들은 Boost 등과 같은 Library 만으로 커버가 안되는 것들이라서 더 의미가 깊다 할 수 있겠습니다.



 이건 g++ 4.5.0 인증샷.

#1.  Local Class를 template parameter로 넘길 수 있게 되었습니다.

#2. -flto 옵션이 컴파일, 링크에 추가 되었습니다. 

#3. c++0x feature 중에 하나인 lambda 가 가능해졌습니다.









Posted by U_Seung
TAG C++, gcc

C++ 관련 자료를 찾다 보면 
Google에서 공개한 자료를 많이 접할 수 있게 됩니다.

뭐 사실 C++의 Library야 찾아보면 다른 Library도 많습니다만...
Google의 자료들은 몇가지 공통적으로 기본빵을 해주는게 몇 가지 있는데..
 - 오픈소스라서 소스레벨의 접근이 가능합니다.
 - 라이선스가 BSD/MIT 입니다. 즉 GPL이 아니라서 라이선스 제약이 상대적으로 덜 합니다.
 - 멀티 플랫폼을 지원합니다. (Windows/Linux은 기본으로 됩니다.)
 - 마지막으로 Google이라는 거대 회사에서 직접 적용해본 도입 사례가 있습니다.


제가 찾은 Google의 자료를 공유 합니다. 이밖에 좋은 자료가 있으시면 공유 부탁드립니다.


#. Google Test & Google Mock
 - Google Test: Google C++ Testing Framework
 - Google Mock: Google C++ Mocking Framework

   C++의 xUnit 도구들이 상당히 많은데.. 사용하다보면 뭔가 하나씩 부족한 느낌이 듭니다.
  그 중에서 제일 덜 부족한 느낌이 드는게 Google Test가 아닌가 싶습니다.







#. Google Performance Tools
 - Google Perftools: Fast, multi-threaded malloc() and nifty performance analysis tools

   멀티쓰레드에서 더욱 좋은 성능을 내는 TCMalloc(Thread-Caching Malloc), CPU Profiler를 제공 합니다. TCMalloc에는 HeapProfiler와 HeapChecker포함되어 있습니다. 하지만, Windows 쪽은 지원이 미약하며 다른 좋은 것들도 많기 때문에 Linux에서 쓰기에 적합하다고 할 수 있겠습니다.







#. Google Log
 - Google glog: Logging library for C++

   glog는 Application Level Logging을 할 때 쓰는 Library입니다. 서버 Application을 만든다면 반드시 어떠한 방식으로든 로깅 기능을 사용할텐데 참조가 될만한 Library입니다. 직접 써도 좋을 것 같다는 생각도 들고요.

  인상적인 부분중에 하나는 google::InstallFailureSignalHandler() 함수 하나만 호출하면.. SIGSEGV 등을 받으면 아래와 같이 Logging을 하게 해준다고 합니다. 매우 편할 것 같습니다. 

*** SIGSEGV (@0x0) received by PID 17711 (TID 0x7f893090a6f0) from PID 0; stack trace: *** PC: @ 0x412eb1 TestWaitingLogSink::send() @ 0x7f892fb417d0 (unknown) @ 0x412eb1 TestWaitingLogSink::send() @ 0x7f89304f7f06 google::LogMessage::SendToLog() @ 0x7f89304f35af google::LogMessage::Flush() @ 0x7f89304f3739 google::LogMessage::~LogMessage() @ 0x408cf4 TestLogSinkWaitTillSent() @ 0x4115de main @ 0x7f892f7ef1c4 (unknown) @ 0x4046f9 (unknown)







#. Google Sparse Hash
 - Google Sparse Hash: An extremely memory-efficient hash_map implementation

 긴 설명 보다는 아래 테스트 결과가 많은 것을 말해줄 수 있을 것 같습니다.

Average over 10000000 iterations
Wed Dec  8 14:56:38 PST 2004

SPARSE_HASH_MAP:
map_grow                  665 ns
map_predict/grow          303 ns
map_replace               177 ns
map_fetch                 117 ns
map_remove                192 ns
memory used in map_grow    84.3956 Mbytes

DENSE_HASH_MAP:
map_grow                   84 ns
map_predict/grow           22 ns
map_replace                18 ns
map_fetch                  13 ns
map_remove                 23 ns
memory used in map_grow   256.0000 Mbytes

STANDARD HASH_MAP:
map_grow                  162 ns
map_predict/grow          107 ns
map_replace                44 ns
map_fetch                  22 ns
map_remove                124 ns
memory used in map_grow   204.1643 Mbytes

STANDARD MAP:
map_grow                  297 ns
map_predict/grow          282 ns
map_replace               113 ns
map_fetch                 113 ns
map_remove                238 ns
memory used in map_grow   236.8081 Mbytes






#. Google CoreDumper
 - Google CoreDumper: A neat tool for creating GDB readable coredumps from multithreaded applications

  Runtime에 원하는 시점의 Memory를 Dump할 수 있는 기능입니다.
  ASSERT를 걸어야할 시점이 Runtime에서 한참 작업을 진행중이라면 Core만 떠두고, 일단 작업을 진행하도록 한 다음에 나중에 수정하여 업데이트 하는 방법이 있겠습니다.
 (당연히도) Linux만 지원됩니다.







#. 그 밖에..
 - gflags: Commandline flags module for C++
 - Keyczar: Toolkit for safe and simple cryptography
 - Breakpad: An open-source multi-platform crash reporting system
 - Omaha: Software installer and auto-updater for Windows
 - Protocol Buffers: Google's data interchange format

그리고 꼭 읽어보길 권장하는...
Posted by U_Seung

 컴파일 속도가 느리다면.. IncrediBuild를 쓰세요.




 링크 속도가 느리다면.
 일단 64비트 OS를 설치하고,
 RAM을 넉넉하게 늘리세요.   ( 4G 이상, 6G 이상 추천 )



 RAM 업그레이드 이후에 체감 링크 속도가 2~3배 빨라졌습니다.

------------------------------------

 OS를 Windows 7 x64로 업그레이드 했더니 빌드 속도가 더 빨라 졌습니다.
 Windows 7 에서 멀티코어 지원이 강화 된 영향 같습니다.





Posted by U_Seung
TAG C++

 Linux에서 Daemon을 돌리다 보면 버그로 인해서 프로그램이 Crash 되는 일이 종종 있습니다.
 당연히 문제가 되는 부분 찾아서 고쳐야 합니다.

 그럴때 대부분 사용하는 방법이 Process가 죽기전에 기록을 Core파일에 남기도록 설정 하는 방법 입니다.
 정상적으로 남겨진 Core파일 경우라면 디버깅 하는게 큰 문제는 없을 테지만
 아래와 같이 콜스택이 깨져 보이는 난감한 상황을 만들어 내기도 합니다.

Loaded symbols for /lib/tls/librt.so.1

Reading symbols from /usr/lib/libggauth2.so.56...done.

Loaded symbols for /usr/lib/libggauth2.so.56

#0  0x02d10100 in ?? ()

(gdb) bt

#0  0x02d10100 in ?? ()

#1  0x01010000 in ?? ()

#2  0x000002d2 in ?? ()



 콜스택이 깨져 보이는 경우는 주로 Stack Buffer Overrun 으로 인한 문제 입니다.
 (다른 문제는 이 글에서 다루지 않습니다.)
 이 경우, 스택에 있던 Return Address가 지워져 있기 때문에 GDB는 콜스택을 표시하지 못합니다.

 하지만 이런 경우라고 해서 너무 낙심할 것은 아닙니다.
 Stack Buffer가 조금 넘친 경우에는 남은 정보를 이용해서 문제를 해결 할 수 있기 때문입니다.

 이때, 실마리를 제공해주는 것은 Register의 ESP 입니다.
 비록 원하던 Return Adress는 지워졌지만 ESP는 값은 정상적인 값을 유지하고 있습니다.
 이제 ESP가 가르키던 메모리 주변을 뒤져서 Return Address와 유사한 값들이 있는지 찾아보면 문제에 대한 힌트를 얻을 수 있습니다.

 일단 Return Address랑 유사한 녀석들은 어떤 녀석이어야 하는지 파악 해야 합니다.
 당연히 Return Address는 실행가능한 Instruction 들이 저장되어 있는 Code Segment를 가르키고 있습니다. GDB에서는 maint info sections .text 명령어를 통해서 Code Segment를 찾아 볼 수 있습니다.

(gdb) maint info sections .text

Exec file:

    `/home/aa/bb.debug.1f25cf3be302bd7308d598c132ac904b', file type elf32-i386.

    0x08051e20->0x0824fc08 at 0x00009e20: .text ALLOC LOAD READONLY CODE HAS_CONTENTS

Core file:

    `/home/aa/core.1214458224.bb.30678', file type elf32-i386.



 이제 Code Segment의 범위도 구했으니 ESP를 기준으로 주변 탐색해서 Return Address와 유사한 녀석들을 찾아 본다면 아마 콜스택과 유사한 모양이 만들어 질 수 있을 겁니다.

 이를 위해서 간단한 User-defined Command를 만들었습니다.

define findsymbols
set $textbegin = $arg0
set $textend = $arg1
set $si = $arg2
set $ei = $arg3
while $si < $ei
set $value = *(int*)$si
if ($textbegin <= $value && $value <= $textend)
printf "[0x%08x] ", $si
info symbol $value
end
set $si = $si + sizeof(void*)
end
end

 가독성이 다소 떨어지긴 하지만 동작은 간단합니다.
 주어진 범위를 탐색하면서 정해진 범위의 값(Code Segment의 범위)이 들어오면..
 해당 값을 info symbol 을 이용해서 출력하는 Command 입니다.



 실제로 사용하면 아래와 같은 결과를 얻을 수 있습니다. (함수 이름은 가명..)

(gdb) findsymbols 0x08051e20 0x0824fc08 $esp-1024 $esp+1024

[..] IoHandler::SendPacket(char const*, unsigned int, bool) + 786 in section .text

[..] IoHandler::UniCast(char const*, unsigned int) + 26 in section .text

[..] Remote::PartyCast(Party*, char const*, unsigned int) + 141 in section .text

[..] Dispatcher::Do(Task*, TaskMsg*) + 795 in section .text

[..] Task::ExtractTaskMsg(TaskMsg*) + 953 in section .text

[..] Task::svc() + 510 in section .text



콜스택(?)을 얻었으니 ESP값과 비교 등을 통해서 어느 지점에서 Crash되었는 지를 파악할 수 있을 것입니다. 아마 있어야 하는 함수 1~2개가 누락되어서 보이는 경우가 대부분 일테고요. 어떤 함수가 버퍼를 넘치게 했는지 찾아내면 됩니다.

좀 더 정확한 원인을 얻어 보기 위해서는 GDB의 x 명령어 등을 이용해서 ESP 주변 메모리를 잘 뒤져보면 답을 찾을 수 있습니다. 하지만 이 정도 까지 얻었다면 소스를 보고 문제의 원인을 먼저 찾고 문제점이 맞는지 검증하는 편이 더 빠를 수 있습니다.

그럼 즐거운 디버깅 되세요 :)


Posted by U_Seung


기본적으로 STL Container들은 Thread Safety하지 않다.
뭐 구현에 따라 다를 수 있지만..

서로 다른 Thread에서 Writing을 하거나
한 Thread에서 Writing을 하는 도중에 다른 Thread에서 Reading을 할 경우,
문제가 발생할 수 있다. (많은 경우, 프로그램이 Crash됨.)

관련 문서들은 아래를 참조..
- [Dinkumware] Thread Safety

---------------------------------------------------------------------------------

따라서, Multi-threading 환경에서 STL Container를 사용하기 위해서는 주의가 필요하다.
한 Thread에서 특정 Container object에 Writing operation을 하기 위해서는 (특히, 삽입/삭제)
다른 Thread에서는 해당 Container object에 reading이나 writing을 하지 않도록 locking을 해주어야 한다.

뭐 복잡하게 설명할 것 없이, 
Intel에서는 Concurrency한 Template Library를 제공한다.

해당 Library에는 여러가지 기능이 제공되지만, 가장 관심이 가는 것 중 하나로
High concurrency & Thread-safe(인텔 측에 따르면)한 Container가 아닐까 싶다.

High concurrency라고 한 이유는, 걍 적당히 Operation 위 아래에다가 Lock/Unlock을 한게 아니라 Lock-free algorithmFine-grained locking을 사용했다는 것인데 실제로 이렇게 구현하는 것은 몹시 까다로운 방법이다. 

intel TBB에서는 아래와 같은 총 3개의 Container가 제공 된다.


사용 예는 기회가 되면 공유하기로 하고,
문서 중 흥미로운 사항을 몇 가지 이야기를 적어 보면..

[concurrent_vector] 
 - Operations on concurrent_vector are concurrency safe with respect to growing, not for 
clearing or destroying a vector. Never invoke method clear() if there are other 
operations in flight on the concurrent_vector.

 = concurrent_vector의 경우, dynamic하게 growing하는 부분에서는 thread-safe하나 벡터의 clearing이나 destroying 부분에서는 thread-safe하지 않단다. clear()나 파괴자 등이 실행 중일 때, 다른 오퍼레이션도 일어난다면 해당 변수에 대한 Logic적인 부분도 고려 해봐야하지 않을까 싶다.

[concurrent_queue]
- Queues are widely used in parallel programs to buffer consumers from producers. Before 
using an explicit queue, however, consider using parallel_while or pipeline instead. These options are often more efficient than queues for the following reasons: 
•  A queue is inherently a bottle neck, because it must maintain first-in first-out order.  
•  A thread that is popping a value may have to wait idly until the value is pushed.  
•  A queue is a passive data structure. If a thread pushes a value, it could take time until 
it pops the value, and in the meantime the value (and whatever it references) 
becomes “cold” in cache. Or worse yet, another thread pops the value, and the value 
(and whatever it references) must be moved to the other processor. 

 = 뭐 복잡하게 설명하고 있지만, Queue의 FIFO 특성 자체가 순서가 강요되기 때문에.. 효율적이지 않는다는 부분이다. Queue를 꼭 사용해야하는지 고려해보고, 굳이 사용하지 않아도 된다면 다른 방법을 사용하길 권장하고 있다. 


Posted by U_Seung

 제목이 약간 자극적이다. (사실, 방식 자체가 다르기 때문에 동등한 조건에서 비교하는 것은 좀 그렇다.)

 Boost.Intrusive는 Boost Library의 Container중 하나로, Performance를 높이는 방법으로 사용할 수 있다.

 Boost.Intrusive에서 제공되는 Container의 종류는 아래과 같음.
 - Singly Linked List
 - Circularly Linked List (like std::list)
 - set/multiset/rbtree
 - avl_set/avl_multiset/avltree
 - splay_set/splay_multiset/splaytree
 - sg_set/sg_multiset/sgtree
 - unordered_set/unordered_multiset

 먼저 Intrusive Container와 Non-intrusive Container의 차이점에 대해서 알아야 한다.

 쉽게 설명하면.. 기존에 우리가 쓰는 Container들은 보통 Non-intrusive Container라고 생각하면 된다.
 Linked List를 한번이라도 구현해보거나 구조에 대해서 생각해 본 사람이라면 알겠지만.

 대략 Linked List는 아래와 같이 생겼다.

    1 class SomeValue

    2 {

    3     //.. some members ..///

    4     int asdf;

    5 };

    6 

    7 template <typename T>

    8 class List<T>

    9 {

   10     class list_node

   11     {

   12        list_node *next;

   13        list_node *previous;

   14        T value;

   15     };

   16 

   17     //....

   18 };

   19 

   20 List<SomeValue> list;


 여기서 14번째 줄처럼 Non-Intrusive Container는 Value값을 복사해서 가지게 되어 있다.
 
 하지만 Intrusive Container의 Linked List는 아래와 같이 구현한다.

    1 class SomeValue

    2 {

    3     // intrusive members.

    4     SomeValue *previous;

    5     SomeValue *next;

    6 

    7     //.. some members ..///

    8     int asdf;

    9 };

   10 

   11 template <typename T>

   12 class List<T>

   13 {

   14     //....

   15 };

   16 

   17 List<SomeValue> list;


 4번째, 5번째 줄처럼 기존에 Value 구조체에 Linked List에 필요한 구조체를 포함 시키는 게 핵심이다.


 따라서, 메모리 관리는 Container 외부에서 담당하게 되는 것이고
 Container 관리를 위한 메모리를 따로 할당 하지 않기 때문에 Performance 향상을 볼 수 있다.
 아래는 비교표.

Issue

Intrusive

Non-intrusive

Memory management

External

Internal through allocator

Insertion/Erasure time

Faster

Slower

Memory locality

Better

Worse

Can hold non-copyable and non-movable objects by value

Yes

No

Exception guarantees

Better

Worse

Computation of iterator from value

Constant

Non-constant

Insertion/erasure predictability

High

Low

Memory use

Minimal

More than minimal

Insert objects by value retaining polymorphic behavior

Yes

No (slicing)

User must modify the definition of the values to insert

Yes

No

Containers are copyable

No

Yes

Inserted object's lifetime managed by

User (more complex)

Container (less complex)

Container invariants can be broken without using the container

Easier

Harder (only with containers of pointers)

Thread-safety analysis

Harder

Easier



 내가 제일 마음에 드는 부분은..
 메모리 관리를 외부에서 할 수 있다는 점과 Memory Locality가 향상 된다는 점이 아닐까 싶다.

 이를 잘 활용하면 불필요하게 Heap을 사용하는 것을 줄일 수 있다.


 개발의 편의성 보다는 성능(!!)에 초점이 맞추어진 프로젝트가 있다면 써보길 추천..


 아.. 실제 Boost.Intrusive를 쓰는 코드는 아래와 같이 생겼다.

    1 

    2 struct SomeStruct

    3 {

    4     int i;

    5     char someBuffer[1024];

    6 

    7     SomeStruct()

    8     {

    9     }

   10 };

   11 

   12 struct SomeStructI

   13     : public boost::intrusive::slist_base_hook<>

   14     , public SomeStruct

   15 {

   16 };

   17 

   18 slist<SomeStructI, cache_last<true> > ptrContainer;

   19 

   20 BOOST_FOREACH(SomeStructI& someStruct, ptrContainer)

   21 {

   22     // ...

   23 }



 그다지 못 생긴 것도 아니다.




Posted by U_Seung

EC++ 에 보면 const에 대한 설명이 잠깐 나온다.

설명을 보면..

1. char* a;
2. const char* a;
3. char* const a;
4. const char* const a;

위의 넷이 다른 타입이라는 것을 설명하고 있다.

그렇다면.. 간혹 볼 수 있는 const char** 는  어떤 타입일까?

5. const (const char*)*
6. const (char *)*
7. (const char*)*


일단 5,6,7 중의 하나라고 생각할 수 있겠다.
5,6,7의 차이는 어떤 곳에 const의 속성이 붙느냐에 따라서 명확히 다른 type이다.

그렇다면....
- const char**은 5,6,7 중 어떤 타입일까?
- 셋 중에 하나라면 다른 두 가지는 어떻게 표현해야 할까?

이런 의문점이 생기게 만드는 const char** 표현은 그다지 바람직한 모양은 아닌 것 같다.

일단 정답은 7번
const char**는 (const char*)* 이다.
사용법은 아래와 같다.

char* pc = new char[20];
const char* cpc = new char[20];

const char** cppc;
cppc = &pc; // (X) error: invalid conversion from 'char**' to 'const char**'
cppc = &cpc;
*cppc = pc;
*cppc = cpc;
**cppc = '1'; // (X) error: you cannot assign to a variable that is const

 char** ppc = cppc; // (X) error: invalid conversion from 'const char**' to 'char**'


유의 깊게 볼 점은..
char** 와 const char** 은 상호 conversion이 안 된다는 점인데
char*는 const char*로 conversion이 된다는 점에서 차이가 있음을 알고 있을 필요가 있다.



그렇다면 5, 6번의 타입을 만들고 싶다면 어떻게 해야 할까?
방법 중 하나는 typedef를 쓰면 된다.

typedef char* PCHAR;
typedef const char* CPCHAR;

const PCHAR* a;
const CPCHAR* a;




 

Posted by U_Seung
TAG C++, char**, const, CPP


이전에 C#으로 SICP에 나온 Infinite Streams을 구현 해본적이 있는데
이번에는 C++로 구현 해보았습니다. 실제로 이런 Lazy한 기법들이 사용될 곳이 있을지는 생각을 더 해봐야 겠지만 일단은 공부 한다는 차원에서 .... 해볼만한 것 같습니다.


만든 Infinite를 사용하는 main코드는 아래와 같은 모양으로 생겼습니다.
(wcout를 쓰기 위해서 그냥 tchar를 사용하지 않았습니다.)

bool isEven(int i) { return (i%2 == 0); }
 
int wmain(int argc, wchar_t* argv[])
{
    fibs()->filter(isEven)->take(10)->out(std::wcout);
    return 0;
}

눈치가 빠르신 분들은 벌써 아셨겠지만 코드는...
피보나치수열(fibs)를 얻어서, 이 중 짝수만 걸러내서(filter) 그 중에서 제일 앞에 10개만(take) 출력(out)하겠다는 코드 입니다. 물론 C++에서 저렇게 exception처리도 없이 ->연산자를 중복해서 사용하는 것은 좋지 않지만 실제로 사용하는 코드가 아니니 이해해 주시기 바랍니다.

아무튼 그래서 피보나치 수열 중에서 짝수 중 앞에 10개는 아래와 같습니다.
위 코드가 아래와 같은 결과를 나오게 만들면 되겠지요?
시간 있으신 분들은 제가 한 방법 말고 다른 방법으로 구현 해보셔도 재미있을 것 같네요 ^^

2
8
34
144
610
2584
10946
46368
196418
832040


- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
소스보기 :




- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

소스를 직접 까보기 귀찮은신 분들을 위해서..
주요 부분 소스를 붙입니다.. (메모리는.. shared_ptr을 썼다가 지저분해서 뺐습니다.)

class fibo_stream : public make_stream<int>
{
public:
    fibo_stream()
        : make_stream<int>(1, 1)
    {}
    virtual IStream<int>* _tail()
    {
        IStream<int>* super_tail = make_stream<int>::_tail();
        return new fibo_stream(super_tail->head(),
                                head()+super_tail->head());
    }
protected:
    fibo_stream(int head, int tail)
        : make_stream<int>(head, tail)
    {}
};
IStream<int>* fibs() { return new fibo_stream(); }

피보나치 수열 스트림의 포인트는 항상 두개의 값만 계산해 놓는다는 겁니다. 즉, 미리 앞서나가지 않는 것 입니다.


덧)
아래 wafe님의 지적을 받아서 표준이 아닌 문법을 고쳐서 g++에서도 컴파일 될 수 있도록 했습니다.


Posted by U_Seung
이전 글에 이어서 포스팅 합니다. VC++ 2008에서 TR1 구현을 일부 지원합니다. 이전 프로젝트에서는 Boost C++ Library를 쓰고 있었는데 이런 Library를 연결시키지 않고 컴파일 할 수 있다는 것이 매우 좋은 소식인 것 같습니다.

 
도움말에 첨부된 예제 소스를 일부 공유해봅니다.

// bind.cpp
// compile with /EHsc
#include <functional>
#include <algorithm>
#include <iostream>
 
using namespace std::tr1::placeholders;
 
void square(double x)
    {
    std::cout << x << "^2 == " << x * x << std::endl;
    }
 
void product(double x, double y)
    {
    std::cout << x << "*" << y << " == " << x * y << std::endl;
    }
 
int main()
    {
    double arg[] = {1, 2, 3};
 
    std::for_each(&arg[0], &arg[3], square);
    std::cout << std::endl;
 
    std::for_each(&arg[0], &arg[3], std::tr1::bind(product, _1, 2));
    std::cout << std::endl;
 
    std::for_each(&arg[0], &arg[3], std::tr1::bind(square, _1));
 
    return (0);
    }
1^2 == 1
2^2 == 4
3^2 == 9
 
1*2 == 2
2*2 == 4
3*2 == 6


위에서 처럼 bind()와 placeholder(_1, _2, ... )가 생겨서 함수형 포인터를 다루기가 매우 수월해졌습니다. mem_fn()과 함께 쓴다면, 여러가지 상황에서 응용할 수 있을 것 같습니다.

// sp_construct.cpp
// compile with /EHsc
#include <memory>
#include <iostream>
 
struct deleter
    {
    void operator()(int *p)
        {
        delete p;
        }
    };
 
int main()
    {
    std::tr1::shared_ptr<int> sp0;
    std::cout << "(bool)sp0 == " << std::boolalpha
        << (bool)sp0 << std::endl;
 
    std::tr1::shared_ptr<int> sp1(new int(5));
    std::cout << "*sp1 == " << *sp1 << std::endl;
 
    std::tr1::shared_ptr<int> sp2(new int(10), deleter());
    std::cout << "*sp2 == " << *sp2 << std::endl;
 
    std::tr1::shared_ptr<int> sp3(sp2);
    std::cout << "*sp3 == " << *sp3 << std::endl;
 
    std::tr1::weak_ptr<int> wp(sp3);
    std::tr1::shared_ptr<int> sp4(wp);
    std::cout << "*sp4 == " << *sp4 << std::endl;
 
    std::auto_ptr<int> ap(new int(15));
    std::tr1::shared_ptr<int> sp5(ap);
    std::cout << "*sp5 == " << *sp5 << std::endl;
 
    return (0);
    }
(bool)sp0 == false
*sp1 == 5
*sp2 == 10
*sp3 == 10
*sp4 == 10
*sp5 == 15


TR1 스펙 중에 가장 매력적인 것 중에 하나가 이 shared_ptr/weak_ptr이 아닌가 싶습니다. 포인터를 Class화시켜서 다루면서 알아서 Reference Count계산과 Garbages Collecting을 해주니 C++에서도 Java나 C#처럼 메모리 관리에 대해서 별로 신경 쓰지 않고 프로그래밍 할 수 있습니다. 위의 예제에서처럼 weak_ptr과 shared_ptr간의 상호 변환 그리고, auto_ptr의 이용이 가능합니다.
 ㅎ

Posted by U_Seung


Microsoft가 Visual Studio 2008 Feature Pack Beta를 발표 하였습니다. 여기에는 Office UI 구현 등 기존의 MFC 확장 기능을 비롯하여, C++ 표준 TR1 구현이 추가 되는 등의 새로운 Feature들을 선보이고 있습니다. 정리된 내용을 요약하면... 아래와 같고, 자세한 내용은 문서를 참조하세요.


VC++ 2008 MFC 라이브러리에 아래와 같은 Application 제작 기능이 추가 되었습니다.
  • Office 리본 스타일 인터페이스
  • Office 2007/2003/XP Look & Feel
  • Visual Studio 스타일 툴바와 Pane
  • Full Customizing 가능한 툴바와 메뉴
  • 고급 GUI Controls
  • 고급 MDI Tabs와 Groups
  • 기타 많은 것들..


또한, C++0x 표준의 TR1이 구현되었습니다.

  • Smart pointers <memory>
  • Regular expression parsing <regex>
  • New containers (tuple, array, unordered set, etc) <unordered_map>, <unordered_set>, <tuple>, <array>
  • Sophisticated random number generators <random>
  • Polymorphic function wrappers <functional>
  • Type traits <type_traits>
  • And more! <utility>



 








Posted by U_Seung