Source code

  • 왜 생성자가 protected 일까? 
    • 클래스 상속시 sub-class의 기본생성자는 자동으로 super-class의 기본생성자를 호출한다. 이때 super-class의 protected인자들은 sub-class가 호출할 수 있지만 private로 생성하는 경우 접근할 수 없다. 한마디로 super class의 기본 생성자를 private으로 하는 경우 sub-class는 기본생성자를 만들 수 없다.
프로그램을 짜다 보면 딱 한번만 실행해야 하는 함수들이 있을수 있다.

그런경우 아래와 같이 static bool 변수같은걸 두고 그 값에 따라서 수행하기도 하는데



이 함수가 multi thread 접근을 하는경우 초기화가 다중으로 될 수 있는 위험이 있다.

다행히 boost에서 thread 에 안전한 함수를 제공한다. 

  • boost::call_once() 는 연결된 함수포인터를보고 단 한번만 수행한다. 두번째로 받는 파라미터는 boost::once_flag 형이다. thread safe 하다.

  • boost::once_flag 는 매크로 BOOST_ONCE_INIT 으로 초기화 한다. 초기화 역시 thread safe 하다.

플래그를 전역변수가 아닌 함수 내부 static 변수로 둘경우 아래와 같이 구성할수 있다.


주의해야 할 점은 이 함수들을 클래스의 멤버함수로 구현하는 경우, 초기화의 실제 동작 부분인 initOnlyOnceCore() 는 private 으로 가려져야 이 함수가 직접 호출되는 것을 막을 수 있다.
  1. Favicon of http://warmz.tistory.com BlogIcon 음주코딩 2013.04.12 10:05 신고

    좋은 정보 감사합니다. 퍼갈게요.

boost 메뉴얼은 거지같은.. 으악

아래와 같이 사용하면 스트링을 얻을 수 있다.

What is boost thread
----------------------------------------------------------------------------------
   쓰레드와 프로세스의 개념에 대해 알고싶으면 이곳을 참조하라 (조인시위키). 쓰레드는 각 플랫폼마다 조금씩 다르게 설정되어 있어서, 여러 플랫폼의 스레드를 지원하는 프로그램을 짤 경우 신경쓸 부분이 많아진다. boost thread는 이런 신경쓸 부분들을 줄여주고자 만든 thread관련 멀티플랫폼 라이브러리이다.



Conditions in boost thread
----------------------------------------------------------------------------------
  1. boost thread에 연동시킬 함수는 인자가 없는 함수여야 하는데, 만약 인자가 있는 함수를 써야 하는 경우 boost::bind를 사용하여 인자없는 함수로 변환후 사용 가능하다.
  2. boost thread object는 복사는 안되고 단지 이동만 가능하다.
    (예)
    boost::thread first_thread();
        
    void func()
    {
         boost::thread move_test_thread = make_thread();
         some_thread.join();
    }


How to use boost thread
----------------------------------------------------------------------------------

  • 헤더 : #include <boost/thread.hpp>
  • 선언
    • 쓰레드함수가 외부에 있을때 ( func1 은 void형 )
        : boost::thread threadVar1( &func1 );

    • 쓰레드함수가 클래스의 멤버 평선일때
        : boost::thread threadVar2( boost::bind(&ClassName::func2, this) ); 

      boost::bind는 함수의 인자의 갯수변화를 줄때 쓰이는데, 여기서 thread 생성시 필요한 함수는 인자가 void여야 한다. 우리가 맴버함수안에서 "인자가 void인 다른 맴버함수"를 부를때는 선언한 형식대로 선언해주면 된다고 생각하고 있는데, 모든 맴버함수는 가장 첫번째 인자 this 를 생략하고 있음을 명심해야 한다. 즉 void TestClass(int t1, int t2)는 원래 TestClass(this, 3, 4) 형태로 불리워야 한다는 것이다.


Advanced Usage
----------------------------------------------------------------------------------
  • Thread를 제어하는 2가지: Mutex 와 condition

    • Mutex : 운영체제에서 익히나온 것이다. Thread 가 동시다발적으로 어느 특정 리소스를 사용할때, 한개의 Thread만 사용하게 하고싶을때 사용하는 녀석이다. 제대로 사용하지 않으면 성능저하를 일으키니 디자인설계때부터 조심히 사용해야 하는 녀석이다. 추천하는

      - header : #include <boost/thread/mutex.hpp>
      - 추천하는 것 : boost::mutex::scoped_lock
      - 이유 : 사용하기 편하다. 이 변수를 괄호 안에 선언하게 되면 괄호의 끝을 만날때까지 잠금이 되고, 괄호를 벗어나게되면 락이 풀린다. (이에 대한 원리는 간단히 생각해보면 되는데, 생성자에서 락을 걸고, 소멸자에서 락을 푸는 코드를 삽입한 것이다)

      boost::mutex mutexVar; // Lock을 걸 mutex 변수

      if ( test_module == true )
      {
           boost::mutex::scoped_lock( mutexVar ); // Mutex Lock이 걸림
           // Do something
      } // 이 시점에서 락이 풀린다.


    • condition : 쓰레드를 잠들게 하고 동작하게 하는 제어 변수. 보통 어떤 값을 기다릴때 while 로 돌리는 방법이나 sleep 을 줘서 일정시간마다 체크하는 경우가 있는데 이는 대부분 비효율적이며, 데이터가 아직 완성이 안되어있으면 잠들어있다가 완료시점에 신호를 보내어 깨어나게 한다. 기억해야 할 점은, 만약 집단의 쓰레드들을 관리해서 경쟁적으로 한개씩 깨어나게 하고 싶다면 하나의 condition 변수를 쓰레드들 사이에서 공유해야 한다.

      void threadFunction(void)
      {
           while( true )
           {
                boost::mutex::scoped_lock lock( mutexVar );
                // Thread 초기화 작업
                condition.wait(mutexVar); // 현재 실행중인 Thread는 대기모드로
                                                    // 들어가고 mutexVar로 걸린 락이 해지
                                                    // 된다.
      // 외부 코드에서 깨우고 싶을때.. 
      condition.notify_one(); // 잠들어있는 것중에서 경쟁적으로 한개만 깨울때
      condition.notify_all(); // 잠들어있는 모든 녀석을 깨울때


Error Message
----------------------------------------------------------------------------------
  1.  /usr/local/include/boost-1_36/boost/thread/pthread/condition_variable.hpp:71: boost::condition_variable_any::~condition_variable_any(): Assertion `!pthread_mutex_destroy(&internal_mutex)' failed.

    원인 : 생성한 Thread 들은 condition.wait() 을 하고 있는데 프로세스가 죽은 경우 이런 메시지가 나온다.

    해결 : 프로세스 종료 루틴에서 모든 쓰레드를 join 시킨후(또는 모두 종료시킨후) 종료하게 한다.

  1. noaster 2010.11.19 10:49 신고

    검색타고 들어왔는데
    이런 유용한 정보가 감샤 ㅎ

    • Favicon of http://finsternis.tistory.com BlogIcon leanu 2010.11.19 11:53 신고

      나의 고생을 사뿐히 즈려밟고 가시옵소서.

      밥사줘



주의 : shared_ptr형은 "shared_ptr이 아닌 데이터형"의 주소를 복사할 수 없다. 왜냐하면, shared_ptr은 참조 횟수가 0일때 자동으로 메모리를 해제하게 되는데, 만약 해제할 메모리가 자기가 관리하는 영역밖의 것이라면 안정성을 보장할 수 없기 때문이다. 아래와 같은 것이 안된다는 것이다.
     (잘못된 예)
        int a = 3;
        boost::shared_ptr<int> test;
        test = &a    // Compile error 대입 자체가 안된다. 대입은 shared_ptr끼리만 된다.
        (*test)++;

주석에 쓴 것처럼 shared_ptr끼리의 주소 대입은 된다. shared_ptr중 적어도 한개는 내부에 메모리를 할당한 object를 가지고 있어야 하기 때문에, 결국 shared_ptr끼리 물고있는 영역은 shared_ptr이 관리할 수 있는 영역이 되고 이로인해 메모리 해제시 안전성을 보장할 수 있다.

       (올바른 예)
          boost::shared_ptr<int> test(new int(3));
          boost::shared_ptr<int> test2;
          test2 = test;
          (*test2)++;
          std::cout << *test<< std::endl;
소켓 요청이 들어오면 등록한 콜백함수를 호출하게 하는 형식으로 작성하였다. 사용시 참고바람

<사용방법>
   0. handler 함수 구현 (형태는 void (string, socket) 이지만 socketmanager를 변경하면 다른 타입도 가능)
   1. io_service obj 선언
         (예) boost::asio::io_service io_service;
   2. endpoint object 선언
         (예) boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), PortNo);
   3. SocketManager object 선언
         (예) SocketManager socketManager(io_service, endpoint, handler)
   4. accept() 함수 실행
         (예) socketManager.accept();
   5. io_service 구동
         (예) io_service.run();

///
/// @file   SocketManager.h
/// @brief  header file of SocketManager class
/// @author Dohyun Yun
/// @date   2008-10-01
/// @details
/// - Log
///


#ifndef _SOCKET_MANAGER_H_
#define _SOCKET_MANAGER_H_

#include <string>

#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>

//#define DEBUG

class SocketObject : public boost::enable_shared_from_this<SocketObject>
{
public:
    SocketObject(boost::asio::io_service& io_service)
        : socket_(io_service), data_(NULL)
    {
    }

    ~SocketObject()
    {
        if (data_ != NULL)
        {
            delete[] data_;
        }
    }


    boost::asio::ip::tcp::socket& socket() { return socket_; };

    void run(void func1(std::string, boost::asio::ip::tcp::socket&))
    {
#ifdef DEBUG
        std::cout << "run() start" << std::endl;
#endif // DEBUG

        handlerFromOutside_ = func1;
        if (data_ != NULL)
        {
            delete[] data_;
        }
        data_ = new char[HEADER_LENGTH];

        boost::asio::async_read(socket_,
                boost::asio::buffer(data_, HEADER_LENGTH),
                boost::bind(&SocketObject::readData, shared_from_this(),
                    boost::asio::placeholders::error));
    }

    void readData(const boost::system::error_code& error)
    {
#ifdef DEBUG
        std::cout << "readData() start" << std::endl;
#endif // DEBUG

        if (!error)
        {
            dataLength_ = *(reinterpret_cast<unsigned int*>(data_));
            if (data_ != NULL)
            {
                delete[] data_;
                data_ = NULL;
            }
            data_ = new char[dataLength_ + 1];
            boost::asio::async_read(socket_,
                    boost::asio::buffer(data_, dataLength_),
                    boost::bind(&SocketObject::runHandlerFromOutside, shared_from_this(),
                        boost::asio::placeholders::error));
        }
    }

    void runHandlerFromOutside(const boost::system::error_code& error)
    {
#ifdef DEBUG
        std::cout << "runHandlerFromOutside() start" << std::endl;
#endif // DEBUG

        if (!error)
        {
            data_[dataLength_] = '\0';
            std::string queryXML(data_);
            handlerFromOutside_(queryXML, socket_);
        }

#ifdef DEBUG
        std::cout << "runHandlerFromOutside() OK" << std::endl;
#endif // DEBUG

    }
public:
    void (*handlerFromOutside_)(std::string, boost::asio::ip::tcp::socket&);
    static const size_t HEADER_LENGTH = sizeof(int);
    boost::asio::ip::tcp::socket    socket_;

private:
    int dataLength_;

    char* data_;
};

typedef boost::shared_ptr<SocketObject> SocketPointer;

///
/// @brief SocketManager controls socket communications between outside and main process.
/// @details
/// SocketManager gets queryXML from outside and sends the resultXML to the outside.\n
/// It supports:
/// - Async reading from outside.
/// - Async writing to outside.
///
class SocketManager
{
public:
    SocketManager(boost::asio::io_service& io_service,
                  const boost::asio::ip::tcp::endpoint& endpoint,
                  void func(std::string, boost::asio::ip::tcp::socket&))
        : io_service_(io_service),
          acceptor_(io_service, endpoint),
          handlerFromOutside_(boost::ref(func)) {};
    ~SocketManager() {};

    void accept()
    {
#ifdef DEBUG
        std::cout << "accept() start" << std::endl;
#endif // DEBUG
        SocketPointer newSocketPointerObject(new SocketObject(io_service_));
        acceptor_.async_accept(newSocketPointerObject->socket(),
                boost::bind(&SocketManager::handler, this, newSocketPointerObject, boost::asio::placeholders::error));
    } // end - accept()

    void handler(SocketPointer socketPointerObject, const boost::system::error_code& error)
    {
#ifdef DEBUG
        std::cout << "handler() start" << std::endl;
#endif // DEBUG

        if (!error)
        {
            socketPointerObject->run(handlerFromOutside_);

            // Make new socket which gets the next connection request.
            SocketPointer newSocketPointerObject(new SocketObject(io_service_));
            acceptor_.async_accept(newSocketPointerObject->socket(),
                boost::bind(&SocketManager::handler, this, newSocketPointerObject, boost::asio::placeholders::error));
        } // end - if
    } // end - handler
private:
    boost::asio::io_service&        io_service_;
    boost::asio::ip::tcp::acceptor  acceptor_;

public:
    void (*handlerFromOutside_)(std::string, boost::asio::ip::tcp::socket&);


}; // end - class


#endif // _SOCKET_MANAGER_H_



Examples & Reference
---------------------------------------------------------------------------------------------
Examples
Reference


Memo
---------------------------------------------------------------------------------------------

1. 송신
     boost::asio::io_service io_service; // io_service object생성.  thread safe 하므로 여러개의 thread
                                                      // 가 하나의 io_service 사용 가능.
     tcp::endpoint endpoint(tcp::v4(), (int)portNo); // TCP, UDP 방식과 수신할 포트 번호를 설정한다.
     tcp::accepter accepter(io_service, endpoint) ; // 수신할 객체를 선언한다.
                                                                      // 이또한 TCP와 UDP방식이 있다.
     tcp::socket socket(io_service);
     accepter.async_accept(socket, handlerfunc);
         또는
     accepter.accept(socket);
    
    
handlerfunc 에서는 두가지 일을 한다.
  1. 들어온 녀석에 대한 처리
   - 클라이언트로부터 보내는 값 받기

      boost::asio::async_read(socket,
                                         boost::asio::buffer(const void * data, std::size_t max_size_in_bytes),
                                         boost::bind( readHandlerfunc, boost::asio::placeholders::error)
        또는
      boost::asio::read 를 사용 (사용방법은 Reference참조)


  2. async_accept에 대한 새로운 socket을 만들어 수신 대기 상태로 만든다.

    
     io_service.run(); io_service 를 시작한다.


readHandlerfunc
  1.  에러 체크
      void readHandlerfunc( const boost::system::error_code& error)
      {
           if (!error)
           {
        boost::asio::async_read(socket_,
                                           boost::asio::buffer(const void* data, std::size_t max_size_in_bytes),
                                           boost::bind(
            }


2. 수신

try
{
      boost::asio::io_service io_service;
      tcp::resolver resolver(io_service);
      tcp::resolver::query query(const char* host, const char* port);
      tcp::resolver::iterator iter = resolver.resolve(query);
      tcp::resolver::iterator end;
      tcp::socket socket(io_service);
      boost::system::error_code error = boost::asio::error::host_not_found;
 
      while ( error && endpoint_iterator != end )   // IPv4 와v6를 검사하여 연결이 되는 쪽을 찾는 루틴
      {
            socket.close();
            socket.connect(*iter++, error);
       }

        if ( error )
             throw boost::system::system_error(error);       
        for
(;;)
        {
               boost::array<char, 128> buf;
               boost::system::error_code error;
                size_t len = socket.read_some(boost::asio::buffer(buf), error);
                if (error == boost::asio::error::eof)
                     break; // Connection closed cleanly by peer.
                else if (error)
                     throw boost::system::system_error(error); // Some other error.
                std::cout.write(buf.data(), len);
          }
 }
 catch (std::exception& e)
 {
 std::cerr << e.what() << std::endl;
 }




     
     
    

class 의 member function에서 현재 object를 shared_ptr로 리턴하고자 할때 이것을 사용한다.


(ex)
  1. class SharedClass
  2.      : public boost::enable_shared_from_this<SharedClass>
  3. {
  4. public:
  5.      shared_ptr<SharedClass> returnObject() { return shared_from_this(); }
  6. };



Error Message
--------------------------------------------------------------------------------------

uknownlocation(0): fatal error in "테스트케이스이름" : Unknown Type

     - 원인 : 현재 테스트 케이스를 수행하다가 프로세스가 비정상적으로 종료된 경우.
     - 해결 : 디버깅 해서 비정상적인 종료의 원인을 해결한다.
1. 절대 using namespace를 사용하지 말것. boost serialization 에서는 기존의 std container 의 serialization 을 지원해주는 부분이 있기 때문에 충돌이 발생하여  serialization 이 원할히 돌아가지 않는다. 대신 모든 type 마다 앞에 namespace:: 를 붙여준다. 예를 들어 std namespace 안의 vector 로 변수 test 를 생성하고자 하는경우

      using namespace std;
      vector<int> test;                           --->                   std::vector<int> test;

Error Message

--------------------------------------------------------------------------------------------
./실행파일명: error while loading shared libraries: 라이브러리 명: cannot open shared object file: No such file or directory


Why this error occurs
--------------------------------------------------------------------------------------------
.so 같은 동적 라이브러리 같은경우

이런 경우 동적 라이브러리 경로가 환경변수로 등록이 되어있어야

실행시 참조를 할 수 있다.


How to register library path
--------------------------------------------------------------------------------------------

등록하는 방법이 몇가지 있는데 2개만 소개한다.

1. 환경변수 LD_LIBRARY_PATH 에 추가하기 (루트 계정이 필요없고 간편하니 추천!!)

      export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:추가할 라이브러리 경로

2. /etc/ld/so/conf 에 해당 디렉토리를 등록 (루트 계정으로 로그인후 파일을 수정한다!!)

3. /etc/ld.so.conf 에 추가하고 싶은 라이브러리가 있는 디렉토리를 지정하고 ldconfig -v 명령어로
   적용시킨다.


  1. 신승신 2012.03.19 10:02 신고

    혹시 .dll파일이 저 위에 error 메세지에 뜬 경우는 어떤 문젠가요?

    • Favicon of http://finsternis.tistory.com BlogIcon leanu 2012.04.18 01:11 신고

      혹시 cygwin 사용중에 발생하신 건가요?

      바이너리를 실행할 때 필요한 cygwin안의 라이브러리가 꼬였거나, 설치되어 있지 않을때 발생했던 것으로 기억하고 있습니다. 그때 필요 라이브러리가 무엇인지 검색을 통해 찾았고, 새로 설치하는 것으로 해결하였습니다.

부스트란 라이브러리가 있다는 말만 들었지 공부해보지는 못했는데,

이김에 한번 배워볼까 하는 생각이 든다.ㅎ


부스트는 표준화된 라이브러리는 아니지만

stl의 기능적인 한계성을 보완하기위해

여러사람들이 뭉쳐서 하나의 공개 라이브러리를 만들었는데

그 이름하야 boost~!

boost에 대한 내용들을 하나씩 정리하는 페이지를 만들어야 겠다.

boost library 공식 싸이트

http://www.boost.org/

+ Recent posts

티스토리 툴바