Chromium에서 만든 자체 API를 알아보자 👌
안녕하세요! 두두코딩 입니다 ✋
오늘은 Chromium 내 base::Optional 개념에 대해 알아보겠습니다.
포스팅을 위해 류광님 참고문서 와 Chromium 문서 를 참고하였습니다!
🖇 소스코드에 마우스를 올리고 copy 버튼을 누를 경우 더 쉽게 복사할 수 있습니다!
궁금한 점, 보안점 남겨주시면 성실히 답변하겠습니다. 😁
+ 감상평 댓글로 남겨주시면 힘이됩니다. 🙇
base::Optional
base::Optional은 Chromium에서 사용하고 있는 Container이다. 현재는 표준 라이브러리에서 (C++17)기준으로 제공되지만, Chromium은 C++17이전이 나오기 전부터 사용되어졌기 때문에 해당 라이브러리를 구현해 사용하였다. (아마 C++17이 자리를 잡으면 포팅 하지 않을까 싶다.. 🙄) 표준라이브러리와의 차이는 거의없다. 가장 큰 차이점은 대부분의 객체 앞에 std:: 라는 namespace 대신에 base::를 사용한다는 점이다. 또한, optional이라는 형식 대신에 가장 앞 글자가 대문자인 Optional을 이름으로 갖고 있다.
그렇다면, Optioanl은 어떤 경우에 사용하는가 🤔?
Optional의 사용 시나리오
다음과 같은 프로그래밍 시나리오를 생각해보자.
- 실패할 수 있는 어떤 연산을 수행하는 함수를 호출한다.
- 연산이 성공했으면, 함수를 돌려준 결과를 사용하고 실패했으면 다시 시도하거나 오류를 처리한다.
이러한 시나리오를 구현하려면, 함수가 두 가지 결과값을 돌려줘야한다.
하나는 연산의 성공여부이고, 또 다른 하나는 연산의 결과이다. 그런데 우리가 공부해 온(?) C++ 함수에서는 많아야 하나의 값을 돌려줄 수 있다. 하나의 값 이상을 돌려주기 위해서는 여러개의 값을 담을 수 있는 사용자 정의 타입 (객체)를 반환하면 된다. 일례로 std::pair를 담아서 돌려주기도 한다.
보통 해당 시나리오를 작성하기 위해선 아래와 같이 작성한다.
구체적 시나리오.
create()를 호출하고, 성공할 경우 100을 담아오고, 실패할 경우 0을 담아 오시오. (성공 유무와 담는 값은 별개로 구별하라)
bool create(int& val) {
if (try()) {
cout << "ok..";
val = 100;
return true;
} else {
cout << "not ok..";
val = 0;
}
return false;
}
위의 함수와 같이 인자로 담아올 값을 넘겨 구현하는 방식을 추구한다. 또 다른 방법은 아래와 같다.
std::pair<bool, int> create() {
if (try()) {
... // 재대로 구현한 것이 아님.. (예시용)
return make_pair(true, 100);
} else {
...
return make_pair(false, 0);
}
}
위의 방법 같이, std::pair를 이용해 값을 활용해 값을 반환하는 형식으로 해결한다. 앞의 구문은 다소 번잡하고, 뒤(pair사용) 구문은 객체를 생성해야돼 비효율 적이다.
위의 방법을 사용하고자하지 않고 생각해볼 수 있는 방법은 예외를 던지는 것인데, 지금처럼 사용자의 실수가 얼마든지 예상되는, 즉 실패가 일상적인 상황에서 예외사용까지는 과하다고 생각이 될 수도 있다.
이런 상황을 해결하기 위해 등장한 함수가 base::Optional or std::optional 이다.
base::Optional 사용방법
우리는 Chromium 기반의 Optioanl을 공부하고 있기 때문에, base::Optional을 기반으로 작성한다.
API는 아래와 같이 사용하면 된다.
base::Optional<int> opt;
opt == true; // false;
opt.value(); // this is empty
opt == base::nullopt; // true;
위와같이, base::Optional<T>는 T 값으로 타입을 전달하면 된다. 기본 값으로는 아무 것도 들어가있지 않은 비어있는 상태 즉, empty 상태를 유지한다. base::Optional<T>의 empty 상태는 base::nullopt와 동일하다.
우리가 만약 값을 담아 전달한다면 value 값으로 값을 취할 수 있다.
base::Optional<int> opt = 1; // .value() 를 할 경우 1을 반환해줌.
Optional의 가장 큰 장점은 부울 값을 요구하는 문맥에서 나타난다. base::Optional 객체 자체는 하나의 부울 값 (실제로 자료가 있는지 여부를 나타내는)으로 나타낸다. 즉, if문에 base::Optional을 사용할 경우 부울 값으로 판단 한다.
우리가 문제로 삼았던 예시를 보자.
base::Optional<int> create() {
if (try()) {
return 100;
} else {
return {};
}
}
int main()
{
if (auto tmp = create()) {
cout << tmp.value();
}
}
위와 같이, optional을 사용하면 간편하게 해결할 수 있다.
추가로, base::Optional은 value_or()이라는 함수를 제공한다. 해당 함수는 만약 값이 없을 경우 해당 값으로 초기화 해 값을 가져라 라는 의미이다. 보통 초기화 할 때 많이 사용한다.
base::Optional<int> opt;
opt.value_or(42); // 없다면 42를 반환해라.
조금 더 자세한 내용들이나 API는 문서를 참고하자. 참고문서
Chromium에서 Optional 사용 예시
Chromium에서 Optional은 정말 많이 사용된다. 보통 초기화 할 때, 많이 사용되는데 해당 컨테이너가 쓰여지는 부분을 한번 확인해보자. (부분 발췌한다.. 너무 커서. 🤒)
base::optional<VideoFrameLayout> VideoFrameLayout::Create(
VideoPixelFormat format,
const gfx::Size& coded_size) {
return CreateWithStrides(format, coded_size,
std::vector<int32_t>(NumPlanes(format), 0));
}
scoped_refptr<VideoFrame> VideoFrame::CreateEOSFrame() {
auto layout = VideoFrameLayout::Create(PIXEL_FORMAT_UNKNOWN, gfx::Size());
if (!layout) {
DLOG(ERROR) << "Invalid layout.";
return nullptr;
}
scoped_refptr<VideoFrame> frame =
new VideoFrame(*layout, STORAGE_UNKNOWN, gfx::Rect(), gfx::Size(),
kNoTimestamp, FrameControlType::kEos);
...
}
Tip 최근에는 base::Optional을 absl::Optional로 변경해서 사용하는 것 같다. absl이란 구글에서 만든 API 모음이라고 생각하면 편하다. 완전히 다 변경되는지는 모르겠지만, 기원은 base::라는 사실을 기억하자!
위 함수는 VideoFrameLayout을 생성하는 함수인데, 해당 함수를 생성할 때, 제대로 되지 않을 경우 에러 핸들링을 한다. 여기서 Optional을 사용하고 있는 것을 볼 수 있다. 해당 부분 말고도 굉장히 다양하게 사용하고 있다. (e.g. soket, media etc.)
어떻게 사용하는지 전반적으로 아는 것이 중요하며, 알게 되었다면, 필요할 때 API 문서를 보고 쓰면 된다!