boost 관련 예제 프로그래밍..

가끔 경의롭단 생각이.. 이거 만든사람은 뇌에 cpu 박았나...

예전 모 회사 프로그래머가 신의 한수를 알려주겠다고 memcpy를 한번 짜보라고 하시더군요..

그래서 그냥 생각나는데요 조금 노멀하게...

 

void MyMemcpy(void *dest, void *src, size_t len){

char *dest8 = (char*)&dest;

char *src8 = (char*)&src;

 

while(--len){

*dest8++ = *src8++;

}

}

 

했는데, 이거 봐서 님의 경력은 얼마 안되네요 ㅋㅋ 하시더군요

ㅡ_ㅡ...

 

그래서 조금 고친것이

dest메모리 영역과 src메모리 영역이 겹치는지 확인하는 코드를 넣었습니다.

bool MyMemCpy(void *dest, void *src, size_t len)
{
    char *dest8 = (char*)dest;
    char *src8 = (char*)src;

    /////////////////////////////////
    //dest ■■■■■              //1.dest 메모리 영역안에 src가 있거나
    //        src □□□□□
    //            dest ■■■■■  //2.src 메모리 영역안에 dest가 있으면 안됨
    /////////////////////////////////

    if((dest8 <= src8 //1검사
       && src8 <= dest8+len)
    ||(src8 <= dest8 //2검사
       && dest8 <= src8+len))
    {
        return false;
    }

    while(len--){
        *dest8++ = *src8++;
    }

    return true;
}

 

뭐 여기까지 하면 그래도 안전하지 않냐라는 생각이 들었습니다만...

 

최근 boost 라이브러리(mpl 프로그래밍)쪽을 공부하면서 이런 방법이 나오더군요..

#include <typeinfo>
#include <algorithm>
#include <iterator>
#include <memory>

#include <boost/test/included/prg_exec_monitor.hpp>
#include <boost/timer.hpp>
#include <boost/type_traits.hpp>

 

//기존의 copy와 차별화를 위한 네임스페이스
namespace opt{
    namespace detail{
        //이건 직접 대입시켜서 복사하는 방식
        template<typename l1, typename l2, bool b>
        l2 copy_imp(l1 first, l1 last, l2 out, const boost::integral_constant<bool, b>&)
        {
           while(first != last){
                *out = *first;
                ++out;
                ++first;
            }
            return out;
        }

 

       //memcpy를 이용하는 방식
       template<typename T>
       T* copy_imp(const T *first, const T *last, const boost::true_type&)
       {
            memcpy(out, first, (last-first)*sizeof(T));
            return out+(last-first);
        }
    }

 

    //실제 copy명령어
    template <typename l1, typename l2>
    inline l2 copy(l1 first, l1 last, l2 out)
    {
          //복사하는것의 타입을 알아내서
          typedef typename std::iterator_traits<l1>::value_type value_type;

 

    //l1에대해서 "C& C::operator=(const C&);" 와 같은 정의가 없다면
    //trivial_assignment operator 를 가지게 됨. 

    //하지만 컴파일러 능력상 이걸 알아내는건 제각각임;;;
    return detail::copy_imp(first, last, out, boost::has_trivial_assign<value_type>());

    }

}

 

/////////////////////////////////////////////////////////////////////////////////////////////////////

// 이 밑은 위의 소스를 테스트 하는 소스

const int array_size = 1000;
int   i_array_[array_size] = {0,};
const int ci_array_[array_size] = {0,};
char  c_array_[array_size] = {0,};
const char cc_array_[array_size] = {0,};

int *i_array   = i_array_;
const int *ci_array  = ci_array_;
char *c_array   = c_array_;
const char *cc_array = cc_array_;

const int iter_count = 1000000;

 

using namespace std;

int cpp_main(int argc, char* argv[])
{
    boost::timer t;
    double result;
 
    cout << "Measuring times in micro-seconds per "<< array_size << " elements processed" << endl;
    cout << "Testing copy... " << endl;
    cout << "  [some standard liberay version may already perform this optimisation.]" << endl;
  
    //////////////////////////////////////////////////////////////////////////
    //cache load
    opt::copy(ci_array, ci_array + array_size, i_array);

    //time optimised verstion
    t.restart();
    for(int i=0; i< iter_count; ++i){
        opt::copy(ci_array, ci_array + array_size, i_array);
    }
    result = t.elapsed();
    cout << "opt::copy<const int*, int*> : " << result << endl;

 

    //////////////////////////////////////////////////////////////////////////
    //cache load;
    std::copy(ci_array, ci_array + array_size, i_array);

    //time standard version
    t.restart();
    for(int i=0; i< iter_count; ++i){
        std::copy(ci_array, ci_array + array_size, i_array);
    }
    result = t.elapsed();
    cout << "std::copy<const int*, int*> : " << result << endl;

  

    //////////////////////////////////////////////////////////////////////////
    //cache load
    opt::copy(cc_array, cc_array + array_size, c_array);

    //time optimised verstion
    t.restart();
    for(int i=0; i< iter_count; ++i){
        opt::copy(cc_array, cc_array + array_size, c_array);
     }
     result = t.elapsed();
     cout << "opt::copy<const char*, char*> : " << result << endl;

 

     //////////////////////////////////////////////////////////////////////////
     //cache load;
     std::copy(cc_array, cc_array + array_size, c_array);

     //time standard version
     t.restart();
     for(int i=0; i< iter_count; ++i){
         std::copy(cc_array, cc_array + array_size, c_array);
     }
     result = t.elapsed();
     cout << "std::copy<const char*, char*> : " << result << endl;

  

     return 0;
}

 

메타프로그래밍은 프로그램 소스를 프로그래밍 하는 개념인데...

위의 메모리 복사 예제 처럼 C++의 템플릿을 가지고, 컴파일러가 타입에 따라 가장 적절한 알고리즘을 선택 할 수 있게 하거나

미리 상수 같은걸 프로그램 실행 시점에서 결정하지 않고 컴파일 시점에서 결정하게 하여

더 빠른 프로그램 속도를 갖게 하는 비법? 꼼수? 학문? 인거 같습니다.

 

진정한 신의 한수... orz....

 

문제는 내가 아직 이걸 프로젝트에 써먹으면서 머리로 이해 + 손으로 익어야 하는데.......

이 개념 자체가 난해하고 어려워서 자료도 그다지 많이 없고

그나마 boost로 익히는 메타프로그래밍 이라는 책을 사서 봤지만....

 

미국 책 스타일... orz...

그냥 개념만 쫙 ~~~ 설명됬고... 제가 멍청해서 그런지 개념 잡기가 너무 난해 하네요..

영어로 표현하면 보는 내내 so what? 이라는 느낌밖에 안왔습니다. ㅠㅠ

 

어쨋든.. 요즘 boost 쪽... 꽤 유용하고 재미있는 프로그래밍 방법론이라 생각하네요..

 

제 네트워크 프로그래밍에 대해서 복습할 차원(?)으로

게임 회사에서 서버에 이용하는 여러 프로그래밍 기법(?)을 가능한 축약시켜

간단한 채팅서버를 만들었습니다.

 

제작시간 : 약2일 + alpha

사용툴 : Visual Studio 2010, MS-SQL 2012

 

복습 내용 : 네트워크 IOCP, 멀티 쓰레드 기법

                TinyXML 보다 더 짧은 MSXML을 이용한 외부 변수 파싱

                MS-SQL에 연결하기 위한 ADO COM을 사용, DB에 쿼리 던지기

                게임 명령어 처리를 위한 함수포인터 disPatcher

                C#과 이종 교배  (서버는 C++, 클라이언트는 C#)

  

결과 화면 입니다...

 

 

<--- 수정 2012-10-31 --->

역시 주말에 급조해서 만든 소스다 보니 버그가 좀 있었습니다.. orz..

어젯밤 겨우 고쳐서 다시 스크린 샷을 찍었습니다...

 

     ㅁ 버그수정 사항

         - 어느 클라가 /date. /time 같은 명령어로 에코서버 통신을 했다면, 그 이후 다른 클라이언트의 메시지를 1회 못받음.

         - db 주소가 서버와 같지 않으면 db 접속 실패 (ip설정을 잘못 쳤네요 ㅠㅠ)

         - 어떤 클라이언트에서 아무 텍스트도 안넣고 엔터 치면 서버가 다운... (포인터 검사 문제가...)

         - 에러메시지 출력시 한글이 깨지는 문제... orz...

         - 햇갈리는 변수명을 수정... 

 

 

 

 

 

 

서버 프로그램 

 

 

C++11을 많이 이용하려 했으나, auto 키워드 이외 lamda 같은거는 무리하게 넣지 않았습니다.

그래도 auto 때문에 iterator 를 상당히 단축시킬 수 있어서 코딩이 즐겁더군요 ^^

맨위의 DB 쿼리 에러는 멍청하게도 DB마이그레이션을 하지 않아서 생긴 에러 메시지 입니다.. ㅠㅠ

 

 

클라이언트 프로그램

 

사실 C#책에 예제로 있는 소스를 적당히 썻습니다만, 꽤 쓸만했습니다.

서버 만드는데 2일 걸렸지만, 이 클라이언트는 30분정도 끄적이니까 만들어 지네요.

(저 소스가 개행 포함해서 200라인도 안됩니다)

 

일반적으로 메시지를 보내면 접속된 모든 클라이언트에 같은 메시지를 보냅니다.

물론 그 메시지를 보낸 클라이언트를 제외하고 SendAll입니다.

 

별도로 /date, /time 같이 서버의 날짜와 시간은 에코서버퍼럼 해당 명령을 내린 클라에게만 메시지를 보냅니다.

어짜피 시간은 메시지 옆에 찍히는데 뭘 그런걸 만들었냐 하시면.... orz...

온라인 게임에서 중요한 각 패킷의 처리등을 구현하는 방식을 간략화 하다 보니 이렇게 됬습니다..

 

다음 프로젝트는.... 엄청 심플한 mmorpg나 요즘 유행하는 드래곤X라이 같은 서버를.....

 

그리고 다음번엔 서버도 C#으로 작성해 봐야 할거 같습니다.

이 정도의 간편성과 안정성이면, 속도가 조금 느려도 괜찮다는 생각이 드는군요.

 

 Database 쿼리 실행 결과

 

원래 재대로 만들려면, Login 서버 등을 만들어서 이것저것 처리하고 그걸 DB에 넣어야 하지만....

그냥 심플하게 가려고 하다 보니 프로시져를 이용한 쿼리 실행과 결과 보기 정도로 마무리를 했습니다.

 서버 전반적인 복습을 하고 있습니다만...

DB를 붙이려고 하니

 

공급자를 찾을 수 없다는 에러가 나왔습니다.

 

 

평소 회사에는 없던일이라 조금 당황하고 이것저것 삽질했습니다만..

결국위에 노란 형광펜으로 그은 SQLNCLI 가 문제였습니다.

 

이게 MS-SQL 2005, 2008 과 접속할때는 SQLNCLI 이지만,

제 집에 있는 PC에는 MS-SQL2012Express라, 이럴경우에는 SQLNCLI10 으로 지정해야 한다는군요.

 

 

 

.... 음.... orz

 

#include <iostream>

using namespace std;

 

//간단한 텍스트 뒤집기
void recive(char s[])
{
    for(int i=0, j=strlen(s)-1; i<j; ++i, --j){
        char t = s[i];
        s[i] = s[j];
        s[j] = t;
    }
}

 

//안전한 메모리 복사

//차라리 memcpy_s 사용할것 이건 안에 어셈이라 더 빠름
bool MyMemCpy(void *dest, void *src, size_t len)
{
     char *des8 = (char*)dest;
     char *src8 = (char*)src;

     /////////////////////////////////
     //dest ■■■■■               //1.dest 메모리 영역안에 src가 있거나
     //      src □□□□□
     //            dest ■■■■■   //2.src 메모리 영역안에 dest가 있으면 안됨
     /////////////////////////////////
 
     if((des8 <= src8    //1검사
                && src8 <= dest8+len)
     ||(src8 <= des8    //2검사
                && des8 <= src8+len))
     {
            return false;
      }

   

      while(len--){
            *des8++ = *src8++;
      }

      return true;
}

 

//////////////////////////////////////////////

int main()
{
      char s[5] = {'a','b','c','d', '\0'};
      char d[5];

      for(int i=0; i<strlen(s); ++i){
          cout << s[i] << ',';
      }

     cout << endl;
 

     recive(s);

     for(int i=0; i<strlen(s); ++i){
         cout << s[i] << ',';
     }
     cout << endl;
 
     if(MyMemCpy(&s[3], &s[0], _countof(d))){
         for(int i=0; i<strlen(d); ++i){
            cout << d[i] << ',';
         }
     }
     return 0;
}

 

 

 

 

 

'C/C++언어' 카테고리의 다른 글

printf의 흔하진 않지만 때론 굉장히 필요한 포맷들  (0) 2013.10.23
memcpy에 대한 고찰...  (0) 2012.11.27
[ VC11-C++11 ] range base for - 1  (0) 2012.09.24
코드 최적화 팁  (0) 2012.09.10
CAtlMap 사용법 정리  (0) 2012.08.25

+ Recent posts