'Program Lang./C++'에 해당되는 글 36건

  1. 2019.09.20 [Modern C++] unique_ptr 사용법 상세
  2. 2019.09.20 [Modern C++] Summary of special member function generation
  3. 2019.07.31 [Modern C++] reference collapsing
  4. 2019.07.29 [Modern C++] Type Deduction - decltype
  5. 2019.07.29 [Modern C++] Type Deduction - auto
  6. 2019.07.28 [Modern C++] Type Deduction - Template
  7. 2017.07.09 [Basic] Object Generator
  8. 2017.05.12 boost asio 정리 - 5 (work 고찰)
  9. 2017.05.12 boost asio - 정리 4 (strand 적용)
  10. 2017.05.12 boost asio - 3 (multi-thread 동작 고찰)

[Modern C++] unique_ptr 사용법 상세

Program Lang./C++ 2019. 9. 20. 16:14

1. unique_ptr 이란?

 

unique_ptr은 스마트포인터로 creational design pattern에서 많이 사용되는 포인터이다.

unique_ptr을 완벽히 이해하기 위해서는 move에 대한 고찰도 필요하다.

여기서는 복잡한 개념보다는 실제 사용법 위주로 정리하였다.

 

2. 사용예제

 

실제 개발을 진행하면서 명확한 사용법에서 해메는 경우가 종종있다. 이런 측면에서 보통 많이

적용되는 사용법 위주로 정리한 코드 예제들이다.

특히 unique_ptr을 함수의 파라미터로 사용할 때, 값/참조/rvalue의 선택지가 있는데

어떤 것을 사용해야 할지가 가장 햇갈리는 부분이였다.

간단히 사용법 측면에서 정리하면 다음과 같이 정리할 수 있을 것 같다.

 

  • 받는쪽(함수)로 완전히 자원을 넘겨야 할 경우
    • 함수파라미터를 값이나 rvalue로 선언한다.
    • rvalue로 선언할 경우, 받는쪽에서 move를 통해서 자원을 가져가야 자원이동이 완료된다.
  • 전달한 쪽에서도 계속 자원을 사용해야 할 경우
    • 함수파라미터를 참조로 선언해야 한다.

 

 

4. Pimpl Idiom with unique_ptr

 

unique_ptr과 함께 숙지해 놓으면 좋은 예가 c++ Idiom 중에서 Pimpl이다.

Pimpl은 컴파일 시간을 줄이기 위한 방법으로 Effective Modern C++ 22장에 정리되어 있다.

그 책을 예제로 정리해 놓는다.

아래 코드는 user define Gadget class가 widget.h에 포함되어 있다. widget.h 파일을 많은 cpp 파일에서

include 시킨다고 가정하면, 사용자가 개발 중에 Gadget을 빈번하게 수정한다면 widget.h를 include하는

모든 파일은 컴파일 대상이되어 빌드 시간이 오래걸린다. (컴파일러는 header 파일만 바라본다)

 

<컴파일 시간이 오래걸리는 코드 원본>

 

아래 코드는 위의 컴파일 시간 문제를 C++98 버전으로 Pimpl을 구현한 코드이다.

  1. struct Impl에 사용자 변수를 package한다.
  2. Impl *pImpl raw pointer로 할당받는다. Gadet의 변경은 이제 컴파일 시간에 영향을 주지 않는다.
  3. 실제 코드는 cpp 구현부에 존재한다.
  4. raw pointer의 사용으로 사용자 정의 생성자/소멸자를 만들어줘야 한다.

<C++ 98 Pimpl 구현 코드>

 

이제 Pimpl에 c++11 unique_ptr 사용한다면 컴파일러가 자동으로 소멸자를 생성할 것으로 기대할 수 있다.

하지만 사용자가 생성자를 생성해야 한다. 그 이유는 책에 잘 설명되어 있다.

결론은 shared_ptr와 다르게 unique_ptr은 불완전 type(Impl)에 대해서 소멸자를 자동생성할 수 없다.

따라서 Pimpl처럼 불완전 타입을 unique_ptr에 적용하려면 사용자가 소멸자를 선언 및 정의해 줘야 한다.

소멸자를 사용자 재정의함에 따라서 move/copy 모든 생성자까지 사용자가 생성해줘야 한다.

special member function(https://chipmaker.tistory.com/entry/Modern-C-Summary-of-special-member-function-generation)을 참조하면 상세하게 정리되어 있다.

  1. C++98과 동일하다.
  2. raw pointer에서 smart pointer로 바꾸었다.
  3. header 파일에는 생성자를 선언해야하고, 구현부(cpp)에는 정의가 필요
  4. 생성자와 동일 (unique_ptr을 사용하는 효과가 전혀없다)
  5. move 사용자 재정의로 copy operation(constructor, assignment operator)도 사용자 정의해야 한다.
  6. 소멸자 사용자 재정의로 move operation(constructor, assignment operator)도 사용자 정의해야 한다.

C++98에서 unique_ptr을 사용하면 사용자가 해주어야 할 일이 줄어야 하는데, 차이가 없다.

raw pointer의 자원 관리 문제를 smart pointer를 통해서 Pimpl에서 확장하려면

결국 모든 생성자와 소멸자를 사용자가 해 주어야 한다점이 핵심이다.

물론 compiler가 생성하는 코드와 동일한 코드라면 모두 예제 코드처럼 default로 처리하면 된다.

 

<C++11 Pimpl 구현 코드>

 

5. 정리

 

cppreference에도 사용법은 잘 정리되어 있지만, 바로 보기 힘들어서 여기서 사용법만 재 정리하였다.

unique_ptr을 Pimpl Idiom에 적용할 때, 컴파일 에러상황과 고려해야 할 사항을 정리하였다.

물론 책에도 있지만 네가 다음에 보기 편하게 정리하였다.

Pimpl Idiom은 실제 unique_ptr과 연관성이 높으며, shared_ptr에 적용 예는 드물다.

shared resource에 대해서 Pimpl Idiom을 shared_ptr로 구현한다면, unique_ptr처럼 복잡하지 않다.

단순히 아래 코드처럼 생성자만 사용자가 생성해주면 된다.

소멸자를 사용자가 재정의하지 않아서 나머지 special member function은 컴파일러 모두 자동생성하기

때문이다.   

 

:

[Modern C++] Summary of special member function generation

Program Lang./C++ 2019. 9. 20. 10:56

1. special member function이란?

 

아래 6가지 member function에 대해서 컴파일러가 자동생성해주는 것을 말함. (by Scott Meyers)

아래 6가지 모든 함수를 사용자가 생성하지 않으면, 컴파일러가 자동으로 모두 생성해준다. 

하지만 경우에 따라서 사용자가 아래 6가지 함수 중에 하나 이상을 선언하는 경우가 발생할 수 있다.

특히 move는 성능과 직결되는 것으로 move관점에서 핵심만 정리한다.

  • default constructor (기본생성자)
  • copy constructor (복사생성자)
  • copy assignment operator (복사대입연산자)
  • move constructor (이동생성자)
  • move assignment operator (이동대입연산자)
  • destructor (소멸자)

2. move 자동생성 조건

 

move가 자동생성되기 위해서는 아래 세가지 조건이 모두 만족되어야 한다.

  • No copy operations are declared in the class
  • No move operations are declared in the class
  • No destructor is declared in the class.

하지만 위의 세가지 조건을 모두 만족시키기 어려운 상황이 크게 두 가지 존재한다.

  • class내에 raw pointer 기반 resource 관리 시
    • raw pointer는 모두 삭제하고, STL container 기반으로 코드 수정해야 한다.
  • 클래스가 상속관계에 있을 경우
    • 해당 경우에 대해서 아래에서 상세하게 살펴본다.

3. move 자동생성이 안되는 경우 - class inherit condition

 

class 상속관계에서는 상위(부모) class의 desctructor는 virtual로 사용자가 선언해야 한다.

이런 제약 사항이 move의 자동생성 조건을 만족시킬 수 없어서 move가 자동생성되지 않는다. 

이로 인해 class 설계자가 고려해야 할 사항들에 대해서 정리하였다.

  1. class 설계자는 move operation들을 생성해야한다.
    • 사용자 desctructor의 생성은 move operation이 자동생성되지 않기 때문
  2. class 설계자는 copy operation들을 생성해야 한다
    • 사용자가 move operation들을 생성하면, copy operation들은 제거되거나 자동생성되지 않기 때문
    • copy operation 자동생성 조건은 따로 정리하지 않았지만, 이 정도만 기억하면 될 것으로 보인다.
  3. class 설계자는 default 생성자를 사용해야 한다면, 생성해야 한다.
    • 사용자가 move operation를 생성하면 모든 생성자가 제거되거나 자동생성되지 않기 때문

결론적으로 class가 상속관계에 있는 경우, 6가지 special member function들에 대해서 잘 설계를 해야한다.

 

만약 단순히 virtual 때문에 destructor를 사용자가 선언해야하는 조건이라면, 컴파일러가 자동생성한 것과 동일하게 생성해 달라고 명시하면 된다.

만약 사용자가 컴파일러가 자동생성하는 코드와 다르다면 모두 사용자가 생성해줘야 한다.

 

 

4. template 사용의 경우

 

Effective Modern C++ 책에서는 17장 마지막에 애매하게 설명하고 정리하였다.

아래 코드 테스트를 통해서, template라고 해서 특별히 위의 생성 규칙과 다른 점은 없었다.

move에 대해서 위의 규칙을 잘 이해하고 있다면, template에서도 동일하게 적용된다. 

 

5. 정리

 

special member function은 컴파일러가 자동생성하는 함수들이다.

자동생성되는 함수들은 클래스의 public 영역에 inline으로 생성된다.

special member function들 중에 사용자 생성으로 자동생성되지 않는 규칙이 존재한다.

다른 규칙은 외우지 않아도 빌드 중에 모두 수정할 수 있다.

하지만 move의 특성상 move는 copy로 대체가 가능하여 move의 자동생성조건을

명확히 이해하지 않는다면, 원하지 않게 copy로 동작하여 성능저하의 문제를 겪을 수 있다.

이런 관점에서 move 관점에서 상세하게 살펴보았다.

:

[Modern C++] reference collapsing

Program Lang./C++ 2019. 7. 31. 14:22

1. reference collapsing 이란?

 

type deduction 과정에서 유도된 reference와 원본 expression이 가지고 있는 reference가 충돌하는 것을 말한다.

위의 문장으로는 어떤 의미인지 마음에 와 닿지 않는다. 예제를 하나 살펴보면

 

auto& rx = x;

예제가 이상하기는 하지만 x 가 int lvalue reference라고 한다면,

auto -> int&로 유도된다.

auto를 int&로 치환하면, 아래와 같고 결국 reference와 reference가 충돌하는 상황이 발생한다.

int& & rx = x;


2. reference collapsing 세가지 유형

 

reference collapsing이 발생하는 상황은 크게 아래 세가지 경우에 해당한다.

  • template
  • auto
  • decltype

3. reference collaping 처리 규칙

 

"lvalue reference 이면 최종 결과는 lvalue reference, rvalue reference이면 최종 결과는 rvalue reference"

 

4. perfect forwarding

 

reference collapsing 규칙은 perfect forwarding 이해하는데 핵심 규칙으로 보인다.

perfect forward의 핵심인 forward 기술을 가지고 reference collasping 적용 규칙을 살펴본다.

 

 

아래 표에 main함수에서 예시한 예제에 대해서 reference collapsing 과정을 기술하였다.

표를 통해서 reference collapsing 규칙을 이해하는데 어렵지 않을 것이다.

 

reference collasing설명에서 perfect forwarding이라는 머나먼 주제로 이동한 느낌이지만

reference collasping 주제를 통해서 perfect forwarding 기술이 무엇인지 알 수 있다.

 

perfect forwarding은 somefunction까지 매개변수가 전달되는 과정에서 원래 f 함수에 전달된

함수의 매개변수 reference 속성이 변경되지 않는 기술로 요약할 수 있다.

Case reference collapsing final result

Widget w;

f(w);

lvalue reference로 유도, T : Widget&

Widget & && (Widget& param)

{

    return static_cast<Widget & &&>(param);

}

reference collasping 규칙 적용,

Widget(Widget& param)

{

    return static_cast<Widget&>(param);

}

f(Widget());

rvalue reference로 유도, T : Widget

Widget && (Widget& param)

{

    return static_cast<Widget &&>(param);

}

reference collasping 규칙 적용,

Widget&& (Widget& param)

{

    return static_cast<Widget&&>(param);

}

 

5. reference collasing 추가 고려 사항

 

아래 코드는 책에 존재하는 코드로

RvalueRefToT 변수는 Rvalue Reference로 사용을 위해서 typedef한 경우라고하면,

실제 type deduction 과정에서 reference collasping 과정을 거치고 나면

RvalueRefToT 변수는 lvalue reference로 정의되어진다.

 

reference collasping에 대한 정확한 이해를 바탕으로 구현이 필요함을 보여주는 예제이다. 

 

 

5. 정리

 

reference collasping은 perfect forwarding을 이해하는데 핵심 요소로 보여진다.

c++11을 정리하다보면 각자 다른 주제인 것 같지만, 결국 모두 상호 엮여있어서

다른 것을 이해하는데 각 요소의 정확한 이해가 필요해 보인다. 

 

:

[Modern C++] Type Deduction - decltype

Program Lang./C++ 2019. 7. 29. 15:35

1. decltype type deduction

 

decltype의 사용형식은 다음과 같다.

 

  • decltype(expr) n

 

expr의 표현식의 따라서 type deduction은 크게 두가지로 분류될 수 있다.

 

  • expr에 연산자가 존재하지 않을 경우의 type deduction
  • expr에 연산자가 존재할 경우의 type deduction

 

2. expr에 연산자가 존재하지 않는 경우

 

expr에 연산자가 존재하지 않으면, expr과 동일한 type으로 변환된다.

 

 

3. expr에 연산자가 포함되어 있는 경우

 

expr에 연산자가 존재하면, expr이 lvalue냐 또는 rvalue냐를 식별해야 한다.

lvalue일 경우, reference가 붙지만 rvalue이면 reference는 붙이지 않는다.

 

 

4. decltype(auto) 정리

 

c++14부터 지원하는 문법이다. 정확한 의미는 다음과 같다.

  • auto
    • type deduction이 필요하다.
  • decltype
    • type deduction rule은 decltype의 rule을 따른다.

결론적으로 type deduction을 수행해야 하는데, 이 때 decltype의 rule을 따른다의 의미이다.

이 형식이 어디에 사용되느냐는 함수의 return type과 관련이 있다.

Effective Modern C++의 추가 내용들은 perfact forwarding과 관련된 부분이 대부분이라서

간단히 왜 이런 구문이 나왔는지 측면에서 정리하고 넘어간다.

 

 

위의 코드에서 함수의 return을 auto로 받고 있다. auto의 type deduction에서 기술하였듯이

함수의 return type의 auto의 경우 template의 type deduction 규칙을 따른다.

이 경우 return되는 c[i]는 C&이지만 auto의 type deduction이 template 규칙을 따르기 때문에

최종적으로 return type은 C가 된다.

 

이 경우 문제는 다음과 같은 코드에서 문제가 발생할 수 있다.

 

std::deque<int> d;

authAndAccess(d, 5) = 10;

 

authAndAccess()의 return type은 rvalue를 return하고 있어서 대입 시 에러가 발생한다.

 

여기서 해결책은 무엇인가? c++11의 경우에는 perfact forwarding에 대한 지식이 필요하므로

c++14 기준으로 해결책은 decltype(auto)로 return type을 바꾸는 것이다.

decltype(auto)의 의미에 따라서 C&로 return type이 결정된다.

 

 

5. 정리

 

decltype의 type deduction은 expr의 표현식의 연산자 유무에 따라서 그 동작에 차이가 있다.

이 부분을 주의해서 사용하는 것이 핵심이고, decltype(auto)는 양념정도로 알아두면 좋을 듯 하다.

:

[Modern C++] Type Deduction - auto

Program Lang./C++ 2019. 7. 29. 10:37

1. auto type deduction

 

auto와 template의 type deduction 과정은 braced initalizer에 대한 type deduction을 제외하고는 동일하다.

 

braced initalizer에 대한 auto와 template의 기본 동작은 아래와 같다.

  • auto
    • std::initializer_list로 type deduction 된다.
  • template
    • std::initializer_list로 type deduction을 지원하지 않는다.

 

auto에 대한 위의 braced initalizer 기본 동작이 아래와 같은 경우에 또 예외 동작을 수행한다.

  • auto가 함수의 return type으로 사용 시
    • template처럼 동작한다. 즉 std::initializer_list로 type deduction되지 않는다.
  • lambda의 매개변수에 사용 시
    • template처럼 동작한다.

다음절에서 위에서 기술한 기본동작과 예외 동작에 대해서 상세하게 알아본다.

 

2. auto 기본동작 (template와 동일)

 

auto type specifier template Result
auto x = 27;

template<typename T> void f(T param);

f(27);

T : int,

int x = 27;

const auto cx = x;

template<typename T> void f(const T param);

f(x);

T : int,

const int cx = x;

const auto& rx = x;

template<typename T> void f(const T& param);

f(x);

T : int,

const int& rx = x;

auto&& fr = x;

template<typename T> void f(T&& param);

f(x);

x : int 이고 lvalue,

int& fr = x;

auto&& fr = cx;

template<typename T> void f(T&& param);

f(cx);

cx : const int 이고 lvalue,

const int& fr = cx;

auto&& fr = 27

template<typename T> void f(T&& param);

f(27);

27 : int이고  rvalue,

int&& = 27

const char name[] = "heesoon.kim";

auto ar = name;

template<typename T> void f(T param);

f(name);

name : const char[13],

const char* ar = name;

const char name[] = "heesoon.kim";

auto& ar = name;

template<typename T> void f(T& param);

f(name);

name : const char[13],

const char(&) ar = name;

void function(int, double);

auto fp = function;

template<typename T> void f(T param);

f(function);

function : void(*)(int, double),

void(*fp)(int, double) = function;

void function(int, double);

auto& fp = function;

template<typename T> void f(T& param);

f(function);

function : void(*)(int, double),

void(&fp)(int, double) = function;

 

3. auto 와 template의 braced intializer와의 기본동작

 

서두에 기술한 auto와 template의 braced initializer에 대한 동작 차이점을 보여주는 예제이다.

 

 

4. braced intializer와 auto의 기본동작의 예외 케이스

 

서두에 기술한 auto와 template의 braced initializer에 대한 동작 차이점을 보여주는 예제이다.

 

 

5. 정리

 

auto의 type deduction 동작은 template와 동일하다. 다만 braced initializer와의 동작에 대해서는

auto와 template간의 차이가 있고, 그 경우에 대해서 정리했다.

'Program Lang. > C++' 카테고리의 다른 글

[Modern C++] reference collapsing  (0) 2019.07.31
[Modern C++] Type Deduction - decltype  (0) 2019.07.29
[Modern C++] Type Deduction - Template  (0) 2019.07.28
[Basic] Object Generator  (0) 2017.07.09
boost asio 정리 - 5 (work 고찰)  (0) 2017.05.12
:

[Modern C++] Type Deduction - Template

Program Lang./C++ 2019. 7. 28. 15:16

1. Type Deduction History

 

1) C++98

  • Template Type Deduction

2) C++11

  • Template Type Deduction 변경
  • auto와 decltype 추가

3) C++ 14

  • decltype(auto)가 가능

C++11에서 Template Type Deduction은 auto와 동일하다. 

 

2. Template Type Deduction

 

Effective Modern C++ 기준으로 정리하였으나, 내가 이해한 방식되로 정리하였다. 크게 세가지 관점에서 접근할 수 있다.

 

2.1. ParamType이 T인 경우 - pass by value

 

 

f(expr)에서 expr로 전달되는 원본객체의 const, volatile, reference, pointer를 무시한다.

이 상태에서 T의 타입을 결정하고, expr의 pattern-matching 방식으로 최종 type을 결정한다.

 

f(expr) T type param type Result
f(x) int T f(int param)
f(cx) int T f(int param)
f(rx) int T f(int param)
f(ptr) const char* T f(const char* parm)

 

2.2. ParamType이 참조 또는 포인터인 경우

 

아래 크게 세가지의 경우로 나누어서 정리하였지만, type을 최종 결정하는 방식은

동일한 방식으로 진행된다. 따라서 ParamType을 조금 변경하더라도 동일하게 유추가능하다.

 

1) T&인 경우

 

 

f(expr)에서 expr로 전달되는 원본 객체의 참조를 무시한다.

이 상태에서 T type과 expr과의 pattern-matching 방식으로 최종 type을 결정한다.

 

f(expr) T type param type Result
f(x) int T& f(int& param)
f(cx) const int T& f(const int& param)
f(rx) const int T& f(const int& param)

 

2) const T&인 경우

 

 

f(expr)에서 expr로 전달되는 원본 객체의 상수성과 참조는 무시한다.

이 상태에서 T type과 expr과의 pattern-matching 방식으로 최종 type을 결정한다.

 

f(expr) T type param type Result
f(x) int const T& f(const int& param)
f(cx) int const T& f(const int& param)
f(rx) int const T& f(const int& param)

 

3) ParamType이 포인터인 경우

 

 

f(expr)에서 expr로 전달되는 원본 객체의 포인터는 무시한다.

이 상태에서 T type과 expr과의 pattern-matching 방식으로 최종 type을 결정한다.

 

f(expr) T type param type Result
f(&x) int T* f(int* param)
f(px) const int T* f(const int* param)

 

2.3. ParamType이 foward reference 일 경우 (책에서는 universal reference)

 

 

forward reference의 경우에는 expr로 전달되는 객체가 lvalue인지 rvalue인지를 통해서

T type을 결정하고 param type과는 reference collapsing(책, Item28)을 통해서 최종 타입이 결정된다.

 

f(expr) T type param type Result
f(x)

x : lvalue,

T : int&

T&& f(int& param)
f(cx)

cx : lvalue,

T : const int&

T&& f(const int& param)
f(rx)

rx : lvalue,

T : const int&

T&& f(const int& param)
f(27)

27 : rvalue,

T : int

T&& f(int&& param)

 

3. 추가적인 Type Deduction

 

추가적으로 살펴보아야 할 type deduction은 배열과 함수이다. 이는 원래의 객체의 type이

type deduction 과정 중에 포인터로 바뀌는 현상이 존재하여 이를 잘 구분애서 사용해야 한다.

 

3.1.  배열 Type Deduction

 

배열 type을 갖는 함수 매개변수는 존재하지 않는다.

따라서 T를 통한 type deduction인 f(name)의 경우는 전통적인 C표준에 따라서

const char[13]은 const char*로 변환된다.

 

다만, 배열 type을 그대로 사용하기 위해서는 T&으로 함수템플릿을 정의해야 한다.

 

 

3.2.  함수 Type Deduction

 

위의 배열에서 적용된 타입 변경 방식은 함수에도 동일하게 적용된다.

 

 

4. 정리

 

type deduction에서 pass by value는 expr로 넘겨오는 것은 값으로 복사한다는 측면에서 상수성과 참조성은 무시된다.

reference는 expr로 넘어오는 과정에서 원 객체의 reference를 무시하고 타입 유도를 진행한다.

다만 forward reference는 forward reference 특성을 반영하여 expr로 넘어오는 객체가 lvalue인지 rvalue인지가

타입 유도과정에 적용된다.

 

일반적인 타입 유도뿐만 아니라 전통적인 C에 뿌리를 두고 있는 타입 유도 과정도 같이 고민을 해야 하며,

expr로 넘겨지는 원 객체의 타입을 유지하려면 참조 정의해야 한다.

'Program Lang. > C++' 카테고리의 다른 글

[Modern C++] Type Deduction - decltype  (0) 2019.07.29
[Modern C++] Type Deduction - auto  (0) 2019.07.29
[Basic] Object Generator  (0) 2017.07.09
boost asio 정리 - 5 (work 고찰)  (0) 2017.05.12
boost asio - 정리 4 (strand 적용)  (0) 2017.05.12
:

[Basic] Object Generator

Program Lang./C++ 2017. 7. 9. 10:48

1. 목적

Object Generator 정의는 다음과 같다. 함수 탬플릿으로 클래스 탬플릿의 객체를 생성하는 기술. 정의만으로는 이 기술이 얼마나 멋진지 와 닿지 않는다. 아래 소스 코드를 이용해서 설명을 진행하도록 한다.


2. 소스코드

아래 코드의 예제는 make_tuple의 동작을 알아보기 위해서 make_tuple과 유사 동작을 하는 xmake_tuple을 만들어 보았다.



tuple을 print하는 help function은 일단 무시한다. 우리가 tuple 객체를 생성하는 방법은 아래와 같이 두가지 방식이 있다. 일반 클래스 객체를 선언하기 위한 방법과 동일하게 tuple template에 type 정보를 주고 각 type에 해당하는 argument를 주는 방식이다. 두 번째 방법은 tuple에서 제공하는 make_tuple() 함수를 이용해서 tuple 객체를 생성하는 것이다.


그럼 첫번째 방식과 두 번째 방식의 차이는 무엇인가? 여러가지 차이가 있지만 여기서는 Object Generator 관점에서 살펴본다.


두 번째 방식은 함수 템플릿은 암시적/명시적 객체 인스턴스화가 가능한 반면, 클래스 탬플릿은 명시적인 객체 인스턴스만 가능하다. C++17부터는 클래스 탬플릿도 암시적 객체 인스턴스화가 가능하지만, 이는 논외로 한다.

make_tuple() 함수를 통해서 명시적 Type을 지정하지 않고 Argument로 전달된 객체의 타입으로 tuple의 객체 인스턴스화가 가능하다. 즉 함수 탬플릿의 암시적인 객체 생성 기법을 이용해서 클래스의 명시적 객체 생성이 가능한 것이다.



3. 결론

C++ 표준에는 함수 탬플릿의 암시적 객체 생성 기법을 통해서 클래스 탬플릿의 명시적 객체 생성을 할 수 있는 함수들을 많이 제공하고 있다. 이는 Object Generator 기법에 그 근간이 있다.


:

boost asio 정리 - 5 (work 고찰)

Program Lang./C++ 2017. 5. 12. 14:38

1. 목적

이제 마무리 단계에 왔다. 바로 work의 사용법이다. work는 io_service start 전에 post()에 기술된 handler함수가 먼저 종료되는 상황에서 io_service start를 차 후에 호출하더라도 실제 post의 핸들러 함수는 돌지 않는다. 이를 방지 하기 위해서 항상 block되어 돌도록 하기 위해서 사용한다.


2. 코드 설명

아래 코드에서 현재 상태로 코드를 돌리면 아무 동작을 하지 않는다. 비동기 동작과 유사한 동작을 위해서 post에 전달하는 방식을 thread로 변경하였다. 이 부분이 앞의 예제와 변경된 사항이다.



3. 정리

주석 처리된 부분을 풀고 빌드해서 실행해 보면 정상적으로 동작한다. 이것으로 boost asio 기본 API 함수들의 동작에 대해서 알아보았다. 이제 Network이나 Timer의 동작을 좀 더 명확하게 이해할 수 있는 기반을 갖췄다고 개인적으로 생각이 듭니다.


:

boost asio - 정리 4 (strand 적용)

Program Lang./C++ 2017. 5. 12. 12:27

1. 목적

boost asio 정리 -4는 정리 3에서 multi-thread에서 동일 handler로 처리될 경우 공유 자원에 대한 접근을 제어하기 위해서 lock대신 boost strand를 적용한 예제이다. strand는 handler 처리에 대해서 순차적인 처리를 보장해서 공유 자원 접근에 대한 제어를 lock 객체와 유사하게 해 준다.


2. 코드 설명

post()함수에 strand.wrap()함수로 handler를 한번 wrapping해 주면 strand 동작을 위해서 해 줄 일은 끝난다. 하지만 여기서 눈여겨 봐야할 부분이 strand1과 strand2 두 개의 객체를 선언하였는데 strand1로만 post함수를 wrapping했을 경우와 post 함수를 각각 strand의 wrap함수로 했을 때 동작은 다르다. 결론은 strand 하나의 객체로 wrapping해야지만 진정한 동기를 맞출 수 있다.


아래 코드는 start 함수의 strand wapping 하는 부분의 일 부분만 캡쳐한 것이고, 첫번째로 같은 strand 객체로 wrapping 했을 때 코드와 결과이다.



결과: 첫번째 post에 할당된 handler가 모두 완료되고, 두번째 handler가 순차적으로 돌고 있다.



아래 코드는 post 함수에 서로 다른 strand 변수를 사용했을 때의 코드 조각이다.



결과: 서로 경쟁적으로 돌고 있음을 알 수 있다. 실행 결과를 보기 좋게 하기 위해서 루프는 5번만 돌렸다.



3. 전체 코드



4. 정리


strand를 이용한 공유 자원 처리 방법까지 알아보았다. 이제 남은 것은 work에 대한 것이다. work의 동작을 비동기IO아닌 예제에서 알아보기에는 좀 어려운 점이 있지만 예제 차원에서 억어지로 다음절에서 알아본다.


:

boost asio - 3 (multi-thread 동작 고찰)

Program Lang./C++ 2017. 5. 12. 12:00

1. 목적

boost asio 정리 -2는 io_service 처리를 single thread로 하였다. 여기서는 mulit-thread처리 방식에 대해서 알아본다. 하지만 내용은 매우 간단하다. io_service thread를 여러개 만들면 된다.


2. 코드 설명

기존의 코드와 형태가 좀 바꿨다. boost thread group를 통해서 io_service를 2개 생성함을 볼 수 있다. (boost::thread_group io_threads). 결론적으로 io_service에 대한 처리를 multi-thread로 처리하는 것이다. 또한 post() 함수를 통해서 2개의 handler함수를 추가함을 알 수 있다.




3. 실행결과

기존 정리 -2와 유사하지만 차이는 io_service를 multi-thread로 처리한 것이고, 출력 결과도 post에 정의된 handler가 하나 모두 완료하고 다른 것이 순차적으로 되는 것이 아니라 random하게 실행된다. 여기서는 loop가 작아서 자세히 보이지 않지만 loop를 늘리면 random하게 보일 것이다. thread Id에 주목해보면 서로 번갈아 가면서 동작하는 것을 알 수 있다.



4. 정리

이제 strand에 대한 이야기를 해야 할 시간이 온 것 같다. 위의 예제의 문제에 대해서 이미 파악했을 것이다. post()함수에 do_print()함수가 두 개 동일한 함수가 호출되어서 결론적으로 multi-thread의 처리가 동일 handler에서 처리되는 것으로 do_print()함수에 공유자원이 있다면 공유자원에 대한 mutual access가 필요해진다. stackoverflow에 lock 객체를 쓰는 것이 낫나 아니면 strand를 쓰는 것이 호율적이냐에 대해서 논의가 많다. 각자의 구현이 따라 잘 판단해서 처리해야 하는 것이 답으로 보인다. strand에 대한 필요성과 이 예제의 문제를 개선하기 위한 방법을 알아본다.


: