ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [46] - std::optional
    Graphics 2021. 8. 12. 14:09

    Visual Studio 같은 IDE에서는 C++11 표준이나 C++17 표준을 쉽게 선택할 수 있지만 G++을 통해서 std::optional을 접근하려면 compile 옵션에서 따로 설정을 해주거나 expeimental에 있는 optional을 include해야 합니다.

    std::optional의 주된 사용처는 함수의 반환 값입니다. 예를 들면 외부에서 파일을 불러온다고 했을 때 사용할 수 있을 것입니다.

    위와 같이 파일을 열고 test.txt 내의 모든 string을 반환하는 함수가 있습니다. 만약 파일이 존재하지 않는다면 std::string의 기본 생성자를 반환하게 될 것입니다. 파일이 존재한다면 string을 iterator를 사용해서 만들게 될 것입니다.

    메인 함수 부에 이렇게 간단한 코드를 만들고 돌려보면 아직 파일이 존재하지 않기 때문에 첫 번째 문장인 file cannot be opened가 출력이 될 것입니다. 

    파일을 만들어 주고 실행을 하면 파일이 포함한 문장이 출력이 될 것으로 예상됩니다.

    그런데 파일은 존재하지만 내용이 없으면 파일을 열 수 없다는 결과를 얻을 것입니다. 위와 같이 파일을 열려고 시도하면 string을 반환하도록 하는데 열어본 파일의 내용이 아무것도 없으면 아무것도 포함하지 않은 String을 반환하고 메인 함수에서 판단하기를 아무것도 없는 기본 string은 판단하도록 되어 있습니다.

    이러한 문제를 해결하는 방법은 다양합니다. 가장 직관적으로 풀 수 있는 방법은 std::pair을 도입하는 방법입니다.

    성공 여부를 반환하여 메인 함수에서 해당 State를 참조하면 파일이 존재함에도 불구하고 오류를 내는 것을 방지할 수 있습니다.

    함수의 반환을 포인터로 만드는 방법도 있습니다. 만약 파일이 존재하지 않는다면 null pointer를 반환하는 방법입니다. 그리고 마침내 std::optional을 선택하는 옵션이 있습니다.

    반환이 std::optional<std::string>입니다. 이는 'std::string을 반환 할 것이나 그럴 수도 있고 그러지 않을 수도 있다.'라는 의미를 갖고 있습니다. 그래서 파일이 존재하면 std::string을 반환하지만 그렇지 않으면 단순히 std::optional의 기본 ctor로 만든 인스턴스를 반환합니다. 이 인스턴스에는 아무런 값이 존재하지 않을 것입니다.

    그래서 메인 함수로 넘어오면 const auto에 반환을 잡아주고 std::optional에 overload된 boolean 연산자가 있으니 그것을 통해 값이 있는지 없는지 판단합니다.

    값이 존재하면 해당 값을 인쇄하고 그렇지 않다면 에러 핸들링을 진행하면 됩니다. 

    문제 없이 잘 나옵니다. 더 멋진 기능이 있습니다. 메인 함수 부분의 모든 내용을 한 줄에 줄일 수 있습니다.

    이렇게 어떤 상태가 될 것인지 경우에 따라 다른 상황에서 std::optional은 유용하게 응용할 수 있는 요소입니다. D3D Window를 만드는 과정에서 ProcessMessages함수에 이 std::optional을 선택한 이유도 마찬가지 입니다. 

    std::optional<int>에서 integer는 return value 이며 'WM_QUIT'을 받았을 때 return할 exit code를 나타냅니다. 대부분의 상황에서 Exit code를 Return 하지 않겠지만 영원히 돌아가는 프로그램이 아닌 이상 분명 한 번은 Exit을 하기 때문에 Optional을 선택했습니다. 선언 말고 정의를 살펴보면 다음과 같이 되어 있었습니다.

    프로그램이 돌아가는 내내 Message Queue에 Quit message의 존재 여부를 판단하고 그렇다면 exit code와 함께 프로그램을 끝내지만 그렇지 않다면 Message Queue의 message를 Translate-Dispatch하는 과정을 합니다. Exit code가 아니면 optional의 기본 Ctor를 통해 만든 인스턴스를 Return합니다. 

     

     std::optional의 implementation 중에 흥미로운 것을 하나 배웠습니다. std::optional::value_or이라는 함수는 std::optional에 어떤 값이 있는지 없는지 판단하여 true/false에 대해 각각 행동을 한 줄에 정의할 수 있게 해주는 기능이었습니다. 어떤 상황인지 명시하기 어렵지만 다음과 같은 상황이 있다고 가정해 보겠습니다.

    Get 이라는 함수는 _x에 _y를 곱하고 _z를 더한 값을 반환하는 함수입니다. 그런데 _y와 _z에 기본 parameter가 붙어 있기 때문에 함수를 호출할 때 parameter를 구태여 세 개 다 채울 필요가 없습니다. 하나, 두 개만 넣어도 함수는 알아서 돌아갑니다.

    그러나, 종종 _x, _z는 원하는 값을 넣고 싶지만 _y만큼은 기본 값을 쓰고 싶을 수 있습니다. 위와 같은 구현 방식으로 함수가 제공된다면 우리는 Get(x, 2.0f, z)처럼 Hard coding 할 수 밖에 없습니다. 이렇게 일일이 손으로 적어 넣을 경우에 코드 자체의 유지-보수 과정에서 Default parameter가 바뀌면 이 코드까지 함께 바꾸어야 한다는 문제가 발생합니다. 이런 상황에서 선택할 수 있는 방법이 std::optional을 parameter로 사용하는 것입니다. 다만 주의해야 할 점이 있습니다. 다음과 같은 코드는 원하는 대로 기본 값을 설정해주지 못합니다.

    _y의 값을 Dereference하기 위해 Asterisk를 붙입니다.

    이유는 간단합니다. _y의 위치에 std::optional의 기본 값을 넣어주면 3.0f의 값을 갖는 것이 아니라 아무런 값도 받지 못하게 됩니다. 즉, std::optional이 default parameter를 갖는 문제는 Interface의 문제가 아니라 implementation의 문제가 되어야 한다는 뜻입니다. 

    하지만 이런 식으로 구성을 하면 문제가 발생합니다. 바로 하나의 parameter만 쓰고 싶을 때 오류를 보게 된다는 점입니다. 그래서 std::optional의 기본 값을 설정 해주어야 합니다.

    위와 완전히 같은 효과를 볼 수 있는 std::nullopt을 std::optional의 default parameter로 줄 수 있습니다. 

    'Graphics' 카테고리의 다른 글

    [48] - Rasterization  (0) 2021.08.13
    [47] - Bindable/Drawable System - 3  (0) 2021.08.12
    [45] - Multithreading Execution Policy  (0) 2021.08.11
    [44] - Container Adaptors, Priority Queue  (0) 2021.08.11
    [43] - Heap Operations  (0) 2021.08.11
Designed by Tistory.