
C++에서 열거형을 비트마스크로 쓰도록 연산자를 제공할 때, 타입에 주석을 붙이고 전역 연산자를 찾게 하는 방식은 이름 검색 문제에 부딪힌다. P4380의 코드 주입 방식을 쓰면 연산자를 열거형과 같은 네임스페이스에 생성해 이 문제를 피할 수 있다.
P4313은 열거형에 [[=std::bitmask_type]] 주석을 붙이면 비트 연산자를 제공하는 라이브러리 방식을 제안한다. 하지만 연산자를 전역 네임스페이스에 두면, 열거형이 다른 네임스페이스에 있을 때 인자 의존 검색(ADL)으로 찾지 못한다. 일반 비한정 이름 검색에 의존하게 되는데, 사용 위치와 전역 네임스페이스 사이에서 같은 이름의 연산자를 먼저 발견하면 검색이 그곳에서 멈춘다. 해당 연산자가 열거형과 무관하거나 호출 가능한 후보가 아니어도 마찬가지다. std::bit_or 안에서 연산자를 호출하는 경우에도 std의 연산자들이 먼저 검색될 수 있다.
C++26에서는 연산자를 열거형의 네임스페이스에 using 선언으로 가져오거나, 매크로로 그 네임스페이스에 연산자를 생성하는 방법을 쓸 수 있다. 첫 번째 방법은 |, &뿐 아니라 ^, |=, &=, ^=, ~도 각각 나열해야 한다. 코드 주입 제안은 주석 기반 또는 매크로 기반으로 연산자 함수를 생성해 같은 네임스페이스에 두는 대안이다. 제안된 예시는 단항 +와 비트 연산자 및 대입 연산자를 생성한다.
글은 코드 주입의 다른 예로 기본 비교 연산자 생성도 다룬다. 반사 정보를 이용해 멤버를 비교하고 배열은 재귀적으로 처리해 operator==와 operator!=를 생성하는 방식이다. 이는 C++20의 기본 비교 연산자 기능과 유사한 동작을 라이브러리 코드 생성으로 구현하는 예시다.
C++의 반사와 주석 기능으로 풀 기반 맞춤화는 크게 확장됐지만, 글은 필요한 위치에 맞춤 코드를 생성하는 푸시 기반 맞춤화에는 여전히 한계가 있다고 본다. 글의 제안은 토큰 시퀀스 주입이 그 간극을 메울 가능성이 있다는 것이며, 설계는 아직 초기 단계라고 덧붙인다. 관련 배경은 이전 글과 후속 글의 비트마스크 절에서 확인할 수 있다.
부록: 원문 예시
template <class T> concept EqComparable = requires (T const& t) { static_cast<bool>(t == t); }; template <class T> constexpr auto array_eq(T const& lhs, T const& rhs) -> bool { if constexpr (std::is_array_v<T>) { return std::ranges::equal(lhs, rhs, [](auto const& x, auto const& y){ return array_eq(x, y); }); } else { return lhs == rhs; } } struct Eq_t { consteval auto inject_members(std::meta::info r) const -> std::meta::token_sequence { auto subobjects = subobjects_of(r, std::meta::access_context::unchecked()); if (not std::ranges::all_of(subobjects, [](std::meta::info o){ return extract<bool>(substitute(^^EqComparable, {remove_all_extents(type_of(o))})); })) { // not all comparable? defined as deleted return ^^{ friend constexpr bool operator==(\(r) const&, \(r) const&) = delete; friend constexpr bool operator!=(\(r) const&, \(r) const&) = delete; }; } auto cmp = ^^{ true }; for (std::meta::info s : subobjects) { if (is_array_type(type_of(s))) { cmp += ^^{ and ::array_eq(__lhs.\(s), __rhs.\(s)) }; } else { cmp += ^^{ and (__lhs.\(s) == __rhs.\(s)) }; } } return ^^{ friend constexpr bool operator==(\(r) const& __lhs [[maybe_unused]], \(r) const& __rhs [[maybe_unused]]) { return \(cmp); } friend constexpr bool operator!=(\(r) const& __lhs, \(r) const& __rhs) { return not (__lhs == __rhs); } }; } }; inline constexpr Eq_t Eq{};
댓글 (0)
로그인하면 이 기사에 내 생각을 남길 수 있어요