bool operator idiom

Programming/C++ 2015. 2. 13. 11:45

http://en.wikibooks.org/wiki/More_C++_Idioms/Safe_bool


흐 - 음. 

선 기록 후 정리


struct Testable
{
    operator bool() const {
          return false;
    }
};
struct AnotherTestable
{
    operator bool() const {
          return true;
    }
};
int main (void)
{
  Testable a;
  AnotherTestable b;
  if (a == b) { /* blah blah blah*/ }
  if (a < 0) { /* blah blah blah*/ }
  // The above comparisons are accidental and are not intended but the compiler happily compiles them.
  return 0;
}


Solution


class Testable {
    bool ok_;
    typedef void (Testable::*bool_type)() const;
    void this_type_does_not_support_comparisons() const {}
  public:
    explicit Testable(bool b=true):ok_(b) {}
 
    operator bool_type() const {
      return ok_ ? 
        &Testable::this_type_does_not_support_comparisons : 0;
    }
};
template <typename T>
bool operator!=(const Testable& lhs, const T&) {
    lhs.this_type_does_not_support_comparisons();	
    return false;
}
template <typename T>
bool operator==(const Testable& lhs, const T&) {
    lhs.this_type_does_not_support_comparisons();
    return false;
}
class AnotherTestable ... // Identical to Testable.
{};
int main (void)
{
  Testable t1;
  AnotherTestable t2;
  if (t1) {} // Works as expected
  if (t2 == t1) {} // Fails to compile
  if (t1 < 0) {} // Fails to compile
 
  return 0;
}



In C++11, the explicit keyword can now be applied to conversion operators. As with constructors, it prevents the use of those conversion functions in implicit conversions. However, language contexts that specifically require a boolean value (the conditions of if-statements and loops, as well as operands to the logical operators) count as explicit conversions and can thus use a bool conversion operator.


struct Testable
{
    explicit operator bool() const {
          return false;
    }
};
 
int main()
{
  Testable a, b;
  if (a)      { /*do something*/ }  // this is correct
  if (a == b) { /*do something*/ }  // compiler error
}


Tutorial 1. sync_timer

Programming/Boost asio 2015. 2. 4. 22:58

다음 내용에 기반합니다. 


http://www.boost.org/doc/libs/1_57_0/doc/html/boost_asio/tutorial/tuttimer1.html


시작하기에 앞서, 환경을 구성한다.

boost windows prebuilt 를 받아 적당한 경로에 준비시켜 두었으며,

그 적당한 경로를 Project > Properties 에서 include header 설정 해두었고, 



마찬가지로 lib 파일의 경로도 잡아준다. 




lib 파일의 경로는 잡아줬지만, 실제 링킹되어야 하는 파일명은 dependencies 에 지정하지 않은 상태다.

직접 링크에러가 날때마다 properties 에 추가하거나, pragma comment 로 소스코드에서 linking 할 수 있는데, 

편의상 소스코드에 넣었다.


sync_timer 소스코드는 다음과 같다. 



첫 튜토리얼 답게 굉장히.. 간단하다.

그냥 asio 의 deadline_timer 를 이용, 생성시 지정한 시간만큼 대기하다 종료하는 프로그램.

deadline_timer 의 wait() 함수는 지정된 시간이 지나기 전까지 리턴되지 않는다.


io_service 라는 생소한 클래스와 deadline_timer 라는 녀석이 있는데, 

deadline_timer 는 그냥 timer 기능을 제공하는 녀석이구나 정도로 쉽게 이해가 갈텐데, 

이 녀석이 사용하는 io_service 라는 클래스가 앞으로도 상당히 자주 보게될 클래스. 

I/O 기능을 제공한다 - 라고 튜토리얼에서는 소개하고 있는데, 단독으로 사용되는 경우는 없고,(아는한) 이 예의 deadline_timer 같은 io_object 와 결합이 되어 사용된다. 


IO_SERVICE 

> io_object 로부터 시작 요청을 받으면 OS 와 io 동작을 제공해주는 클래스. 

sync 의 경우에,

시작함수 (initiating function) 가 호출된 스레드에서 I/O 를 수행한 후 리턴


async 의 경우에, 

io_object 의 시작 함수가 (initiating function) 호출되면 io_object 에 지정된 io_service 가 OS 영역-정확히는 아니지만 이렇게 이해해도 큰 무리가 없다고 boost 개발자가 설명한바있다..(로 들렸다)-에 work 라는 객체와 핸들러를 함께 전달하여 queuing 해뒀다가, io_service.run() 이 호출되면 OS 로 실제로 요청 동작을 실행하고, 

OS 로부터 io_service 로 완료 알림이 오면 io_service 는 생성했던 work 객체와 handler 를 dequeue 하여 핸들러를 호출한다. 


실행결과는 - 지정한 5초동안 아무 반응이없다가 hello world 가 출력되고 프로그램이 종료되는 것이다.





'Programming > Boost asio' 카테고리의 다른 글

Tutorial 5. Synchronising handlers in multithreaded programs  (0) 2015.04.11
Tutorial 4. member function handler  (0) 2015.03.17
Tutorial 3. Binding arguments to a handler  (0) 2015.03.15
Tutorial 2. async_timer  (0) 2015.03.14
0. 소개  (0) 2015.02.02

Chapter 7 - 1. Pointers, Arrays, and References

The C++ Programming Languages




7.1 Introduction



메모리를 참조하는 방법을 다룰 것.

이름으로 객체를 접근할 수 있지만 특정 메모리의 주소에 객체들이 존재하고 주소와 그것의 타입을 알면 접근할 수 있다.. 

주소를 쥐고, 사용하기 위한 C++ 언어의 수단은 포인터와 레퍼런스.


7.2 Pointers 

포인터는 T* 와 같이 '*' 를 타입 뒤에 붙여 T 타입의 객체의 주소를 쥘 수 있게 한다. 

포인터에 사용할 수 있는 기본 연산자는 dereferencing(indirection) 인데, 포인터에 의해 가리켜지는 객체에 접근한다. 

배열의 포인터에 대해서는 산술 연산이 가능하다.


포인터의 구현은 실제로 런타임 머신의 어드레싱 메커니즘을 직접적으로 매핑한 것이다. 대부분의 머신들은 1 바이트에 접근가능하다. 그렇지 못한 것들은 words 로부터 바이트를 추출할 수 있는 하드웨어를 갖는 경향이 있다. 반면에, 아주 적은 수의머신들 만이 각각의 비트를 지접적으로 참조 할 수 없다. 

결과 적으로, 독립적으로 할당되고 포인팅 될 수 있는 빌트인 포인터 타입은 char 이다.

bool 은 실제로 char 만큼의 공간을 차지 한다는 것을 기억해 두자. 콤팩트하게 더 작은 값들을 저장하기 위해 bitwise 논리 연산들을 사용하거나, 구조체들의 비트 영역을 사용하거나, bitset 을 사용해야 한다.


타입이름 뒤에 붙이는 *는 pointer to 를 의미한다. 불행히도 배열이나 함수를 포인팅하기 위해서는 더 복잡한 개념을 사용해야 한다.


char** ppc    // char 를 가리키는 포인터를 가리키는 포인터

int* ap[15]    // 인트타입의 포인터의 15개 배열

int (*fp)(char*)    // int 를 리턴하고 char* 를 인자로 하는 함수의 포인터 

int* f(char*)    // char* 인자를 받아 인트 포인터를 리턴하는 함수 


7.2.1 void*



저수준코드에서, 우리는 때때로 어떤 타입의 객체가 실제로 저장되어 있는지 모르는 채로 주소를 넘기거나 저장할 필요가 있습니다.

이때 사용할 수 있는 것이 void* 이고, 의미는 "알려지지 않은 타입의 객체에 대한 포인터" 로 이야기 할 수 있습니다.

하지만 함수 포인터나 멤버포인터로는 사용할 수 없습니다. 게다가, void* 는 다른 void* 로 할당 될 수 있으며, void* 는 동등성을 비교하는 데 사용될 수 있으며, 명시적으로 다른 타입으로 변환될 수 있습니다. 실제로 어떤 타입의 객체가 저장되어있는지 컴파일러는 알 수 없기 때문에 다른 연산들은 안전하지 않을 수 있습니다. 결과적으로 컴파일 타임 에러로 연결 될 수 있습니다. 실제로 이용하기 위하여 명시적으로 변환 될 필요가 있습니다.


void* pv = pi;

*pv; // error 

pv++ // error, unknown size


void* 를 캐스팅 하여 사용하는 것은 타입에 대해 추정하여 이뤄지는 것이므로 위험한 작업이 될 수 있다. void* 를 리턴하는 함수는 그렇지 않은 인터페이스로 완벽하게 숨기는 것이 좋다. 


7.2.2 nullptr



포인터 타입에 할당 될 수 있는 null pointer.

어떤 주소도 0으로 할당되지 않았기 때문에 예전에는 0을 널포인터의 용도로 사용하였다. 하지만 최근에는 0이 포인터의 상수 ( constant of pointer )로 사용되도록, 멤버타입의 포인터로 사용되도록 허용 함에 따라 이렇게 사용할 수 없게 되었다.

nullptr 의 등장에따라 정수를 인자로 받는 함수와 포인터를 인자로 받는 오버로드함수의 모호성을 제거하였다.


7.3 Arrays



배열은 스택영역 / 힙영역에 선택적으로 할당 될 수 있다.


int a1[10]; // static storage 

void f()

{

int a2[20]; // stack

int* p = new int[40]; // heap

}


더 고수준의 인터페이스로 숨겨져 있는 편이 좋다. 배열의 이름은 첫번째 요소의 포인터가 된다. 하지만 이런 묵시적 변환은 에러를 만들 수 있다. 힙영역에 생성했으면 delete[] 를 잊지 말자.  (string / vector / unique_ptr 과 같은 리소스 핸들링을 통해 쉽게 처리될 수 있다.) 

7.3.1 Array Initializers 



[] 로 지정한 사이즈보다 initializer list 의 요소수가 더 적은경우 나머지는 default 값으로 채워진다.

기본 복사 생성자는 배열에 대해 존재하지 않는다. 


int v6[8] = v5; // error - can't copy


비슷하게 값으로 어레이를 전달 할 수 없다. 


객체의 콜렉션에 대해 할당이 필요하다면 vector / array (STL) / valarray 를 대신 사용하도록 한다.

char 배열은 string 리터럴을 이용하여 쉽게 초기화 될 수 있다.


7.3.2  String Literals



쌍따옴표로 감싸진 문자 시퀀스이다. 보이는 것보다 한개의 문자를 더 갖고 있다. '\0' 

string 리터럴의 타입은 const char[] 와 같다.

최신 C++ 11부터는 char* 에 (const 가 아닌) 문자열 리터럴을 할당할 수 없다.. ( C++11 의 예전 버전에서까지만 해도 허용 )

이런 조치는 명확성에 대해서만 이점이 있는 것이 아니라 구현상의 상당한 최적화 효과를 볼 수 있다.


반드시 바뀔 수 있는 문자열을 원한다면 non-const 배열을 사용한다. 


문자열 리터럴은 정적으로 할당되므로 함수에서 리턴하는 것도 안전하다.


const char* function()

{

return "~~~~"; // safe 

}

"~~~~" 를 갖고 있는 메모리는 호출이 끝난 뒤에 어디로 날아 가지 않는다. 


완전히 동일한 문자열 리터럴이 한 배열에 할당될지 / 두 배열에 할당될지는 구현에 달려있다.


7.3.2.1 Raw Character Strings 



역슬래쉬를 특수문자를 리터럴에 넣기위해 사용하기는 간편하지만, 그 숫자가 많을수록 관리하기 어려워지며 정규식의 경우 이를 이스케이프문자와 문자클래스들에(\w 와 같이) 사용하는 두가지를 표현하기 위해 사용한다. 

이런 혼란을 피하기위해 raw string literals 이 존재한다. 


string s = R"(\w\\w)"; // 역슬래쉬로 구분된 두 문자


괄호는 이스케이프되지 않은 쌍따옴표를 허용하기 위해 필요하다. 


그럼, )" 를 포함하는 문자열을 raw string literal 에 넣으려면 ?

다행히 default 구분자에 불과하다. 단순히 '(' 전에 원하는 구분자를 넣는 것으로 처리 될 수 있다.


R"***("quoted string ("))")***"     //"quoted string("))"

) 뒤의 구분자는 반드시 ( 앞의 구분자와 같아야한다. 


또 raw string literals 는 개행을 포함할 수 있다.


7.3.2.2 Larger Character Sets


L"angst" 와 같은 문자열은 와이드 캐릭터 문자열을 나타낸다. 타입은 const wchar_t[] 가 된다. 

이런 문자열은 L'\0' 로 종료된다. 


유니코드를 지원하는 캐릭터 리터럴은 6가지 종류가 있다. 8, 16, 32 그리고 각각 일반용 리터럴과 raw 용 리터럴, 이렇게 6개이다.


UTF-8 은 1~4바이트의 문자로 이뤄져 있다. 1바이트짜리는 ascii 와 일치한다. '\0' 으로 종료.

UTF-16 은 u'\0' 으로 종료

UTF-32 는 U'\0' 으로 종료.


일반적인 영어 문자열을 다양한 방법으로 표시할수 있다.


u8"folder\\file"

u"folder\\file"

U"folder\\file"


u8"\u00E6" 와 같이 유니코드 문자를 표기할 수 있음.


u'04030' ( Cyrillic lowercase latter "a" ) 는 UTF-8 에서 D0B0, UTF-16 에서 0403, UTF-32 에서 00000403 로 표기할수 있는데, 

이와 같은 헥사데시멀 값은 universal character names 라고 불린다.