diff --git a/pc/dtmf_sender.h b/pc/dtmf_sender.h index cb515e850d..b64b50e09c 100644 --- a/pc/dtmf_sender.h +++ b/pc/dtmf_sender.h @@ -102,7 +102,6 @@ class DtmfSender : public DtmfSenderInterface, public sigslot::has_slots<> { // Define proxy for DtmfSenderInterface. BEGIN_PRIMARY_PROXY_MAP(DtmfSender) - PROXY_PRIMARY_THREAD_DESTRUCTOR() PROXY_METHOD1(void, RegisterObserver, DtmfSenderObserverInterface*) PROXY_METHOD0(void, UnregisterObserver) @@ -112,7 +111,7 @@ PROXY_CONSTMETHOD0(std::string, tones) PROXY_CONSTMETHOD0(int, duration) PROXY_CONSTMETHOD0(int, inter_tone_gap) PROXY_CONSTMETHOD0(int, comma_delay) -END_PROXY_MAP() +END_PROXY_MAP(DtmfSender) // Get DTMF code from the DTMF event character. bool GetDtmfCode(char tone, int* code); diff --git a/pc/media_stream_proxy.h b/pc/media_stream_proxy.h index b2d4443e6c..36069a4369 100644 --- a/pc/media_stream_proxy.h +++ b/pc/media_stream_proxy.h @@ -37,7 +37,7 @@ PROXY_METHOD1(bool, RemoveTrack, AudioTrackInterface*) PROXY_METHOD1(bool, RemoveTrack, VideoTrackInterface*) PROXY_METHOD1(void, RegisterObserver, ObserverInterface*) PROXY_METHOD1(void, UnregisterObserver, ObserverInterface*) -END_PROXY_MAP() +END_PROXY_MAP(MediaStream) } // namespace webrtc diff --git a/pc/media_stream_track_proxy.h b/pc/media_stream_track_proxy.h index 1727799a84..f563137c77 100644 --- a/pc/media_stream_track_proxy.h +++ b/pc/media_stream_track_proxy.h @@ -37,7 +37,7 @@ PROXY_METHOD0(rtc::scoped_refptr, GetAudioProcessor) PROXY_METHOD1(bool, set_enabled, bool) PROXY_METHOD1(void, RegisterObserver, ObserverInterface*) PROXY_METHOD1(void, UnregisterObserver, ObserverInterface*) -END_PROXY_MAP() +END_PROXY_MAP(AudioTrack) BEGIN_PROXY_MAP(VideoTrack) PROXY_PRIMARY_THREAD_DESTRUCTOR() @@ -57,7 +57,7 @@ BYPASS_PROXY_CONSTMETHOD0(VideoTrackSourceInterface*, GetSource) PROXY_METHOD1(void, RegisterObserver, ObserverInterface*) PROXY_METHOD1(void, UnregisterObserver, ObserverInterface*) -END_PROXY_MAP() +END_PROXY_MAP(VideoTrack) } // namespace webrtc diff --git a/pc/peer_connection_factory_proxy.h b/pc/peer_connection_factory_proxy.h index b257be70fe..59e373db7b 100644 --- a/pc/peer_connection_factory_proxy.h +++ b/pc/peer_connection_factory_proxy.h @@ -51,7 +51,7 @@ PROXY_METHOD2(rtc::scoped_refptr, AudioSourceInterface*) PROXY_SECONDARY_METHOD2(bool, StartAecDump, FILE*, int64_t) PROXY_SECONDARY_METHOD0(void, StopAecDump) -END_PROXY_MAP() +END_PROXY_MAP(PeerConnectionFactory) } // namespace webrtc diff --git a/pc/peer_connection_proxy.h b/pc/peer_connection_proxy.h index 212f6192e3..7601c9d053 100644 --- a/pc/peer_connection_proxy.h +++ b/pc/peer_connection_proxy.h @@ -157,7 +157,7 @@ PROXY_METHOD1(bool, StartRtcEventLog, std::unique_ptr) PROXY_METHOD0(void, StopRtcEventLog) PROXY_METHOD0(void, Close) BYPASS_PROXY_CONSTMETHOD0(rtc::Thread*, signaling_thread) -END_PROXY_MAP() +END_PROXY_MAP(PeerConnection) } // namespace webrtc diff --git a/pc/proxy.cc b/pc/proxy.cc index 7dc47865cf..5f4e0b8832 100644 --- a/pc/proxy.cc +++ b/pc/proxy.cc @@ -14,8 +14,12 @@ namespace webrtc { namespace proxy_internal { -void TraceApiCall(const char* class_name, const char* method_name) { - TRACE_EVENT1("webrtc", class_name, "method", method_name); +ScopedTrace::ScopedTrace(const char* class_and_method_name) + : class_and_method_name_(class_and_method_name) { + TRACE_EVENT_BEGIN0("webrtc", class_and_method_name_); +} +ScopedTrace::~ScopedTrace() { + TRACE_EVENT_END0("webrtc", class_and_method_name_); } } // namespace proxy_internal } // namespace webrtc diff --git a/pc/proxy.h b/pc/proxy.h index c4e4966869..565ae80175 100644 --- a/pc/proxy.h +++ b/pc/proxy.h @@ -71,16 +71,30 @@ #include "rtc_base/event.h" #include "rtc_base/message_handler.h" #include "rtc_base/ref_counted_object.h" +#include "rtc_base/string_utils.h" #include "rtc_base/system/rtc_export.h" #include "rtc_base/thread.h" +#if !defined(RTC_DISABLE_PROXY_TRACE_EVENTS) && !defined(WEBRTC_CHROMIUM_BUILD) +#define RTC_DISABLE_PROXY_TRACE_EVENTS +#endif + namespace rtc { class Location; } namespace webrtc { namespace proxy_internal { -RTC_EXPORT void TraceApiCall(const char* class_name, const char* method_name); + +// Class for tracing the lifetime of MethodCall::Marshal. +class ScopedTrace { + public: + explicit ScopedTrace(const char* class_and_method_name); + ~ScopedTrace(); + + private: + const char* const class_and_method_name_; +}; } // namespace proxy_internal template @@ -195,8 +209,8 @@ class ConstMethodCall : public QueuedTask { template \ class c##ProxyWithInternal : public c##Interface { \ protected: \ + static constexpr char proxy_name_[] = #c "Proxy"; \ typedef c##Interface C; \ - const char* class_name_ = PROXY_STRINGIZE(c); \ \ public: \ const INTERNAL_CLASS* internal() const { return c_; } \ @@ -205,8 +219,10 @@ class ConstMethodCall : public QueuedTask { // clang-format off // clang-format would put the semicolon alone, // leading to a presubmit error (cpplint.py) -#define END_PROXY_MAP() \ - }; +#define END_PROXY_MAP(c) \ + }; \ + template \ + constexpr char c##ProxyWithInternal::proxy_name_[]; // clang-format on #define PRIMARY_PROXY_MAP_BOILERPLATE(c) \ @@ -306,53 +322,67 @@ class ConstMethodCall : public QueuedTask { \ public: // NOLINTNEXTLINE -#define PROXY_METHOD0(r, method) \ - r method() override { \ - proxy_internal::TraceApiCall(class_name_, PROXY_STRINGIZE(method)); \ - MethodCall call(c_, &C::method); \ - return call.Marshal(RTC_FROM_HERE, primary_thread_); \ +#if defined(RTC_DISABLE_PROXY_TRACE_EVENTS) +#define TRACE_BOILERPLATE(method) \ + do { \ + } while (0) +#else // if defined(RTC_DISABLE_PROXY_TRACE_EVENTS) +#define TRACE_BOILERPLATE(method) \ + static constexpr auto class_and_method_name = \ + rtc::MakeCompileTimeString(proxy_name_) \ + .Concat(rtc::MakeCompileTimeString("::")) \ + .Concat(rtc::MakeCompileTimeString(#method)); \ + proxy_internal::ScopedTrace scoped_trace(class_and_method_name.string) + +#endif // if defined(RTC_DISABLE_PROXY_TRACE_EVENTS) + +#define PROXY_METHOD0(r, method) \ + r method() override { \ + TRACE_BOILERPLATE(method); \ + MethodCall call(c_, &C::method); \ + return call.Marshal(RTC_FROM_HERE, primary_thread_); \ } -#define PROXY_CONSTMETHOD0(r, method) \ - r method() const override { \ - proxy_internal::TraceApiCall(class_name_, PROXY_STRINGIZE(method)); \ - ConstMethodCall call(c_, &C::method); \ - return call.Marshal(RTC_FROM_HERE, primary_thread_); \ +#define PROXY_CONSTMETHOD0(r, method) \ + r method() const override { \ + TRACE_BOILERPLATE(method); \ + ConstMethodCall call(c_, &C::method); \ + return call.Marshal(RTC_FROM_HERE, primary_thread_); \ } -#define PROXY_METHOD1(r, method, t1) \ - r method(t1 a1) override { \ - proxy_internal::TraceApiCall(class_name_, PROXY_STRINGIZE(method)); \ - MethodCall call(c_, &C::method, std::move(a1)); \ - return call.Marshal(RTC_FROM_HERE, primary_thread_); \ +#define PROXY_METHOD1(r, method, t1) \ + r method(t1 a1) override { \ + TRACE_BOILERPLATE(method); \ + MethodCall call(c_, &C::method, std::move(a1)); \ + return call.Marshal(RTC_FROM_HERE, primary_thread_); \ } -#define PROXY_CONSTMETHOD1(r, method, t1) \ - r method(t1 a1) const override { \ - proxy_internal::TraceApiCall(class_name_, PROXY_STRINGIZE(method)); \ - ConstMethodCall call(c_, &C::method, std::move(a1)); \ - return call.Marshal(RTC_FROM_HERE, primary_thread_); \ +#define PROXY_CONSTMETHOD1(r, method, t1) \ + r method(t1 a1) const override { \ + TRACE_BOILERPLATE(method); \ + ConstMethodCall call(c_, &C::method, std::move(a1)); \ + return call.Marshal(RTC_FROM_HERE, primary_thread_); \ } -#define PROXY_METHOD2(r, method, t1, t2) \ - r method(t1 a1, t2 a2) override { \ - proxy_internal::TraceApiCall(class_name_, PROXY_STRINGIZE(method)); \ - MethodCall call(c_, &C::method, std::move(a1), \ - std::move(a2)); \ - return call.Marshal(RTC_FROM_HERE, primary_thread_); \ +#define PROXY_METHOD2(r, method, t1, t2) \ + r method(t1 a1, t2 a2) override { \ + TRACE_BOILERPLATE(method); \ + MethodCall call(c_, &C::method, std::move(a1), \ + std::move(a2)); \ + return call.Marshal(RTC_FROM_HERE, primary_thread_); \ } -#define PROXY_METHOD3(r, method, t1, t2, t3) \ - r method(t1 a1, t2 a2, t3 a3) override { \ - proxy_internal::TraceApiCall(class_name_, PROXY_STRINGIZE(method)); \ - MethodCall call(c_, &C::method, std::move(a1), \ - std::move(a2), std::move(a3)); \ - return call.Marshal(RTC_FROM_HERE, primary_thread_); \ +#define PROXY_METHOD3(r, method, t1, t2, t3) \ + r method(t1 a1, t2 a2, t3 a3) override { \ + TRACE_BOILERPLATE(method); \ + MethodCall call(c_, &C::method, std::move(a1), \ + std::move(a2), std::move(a3)); \ + return call.Marshal(RTC_FROM_HERE, primary_thread_); \ } #define PROXY_METHOD4(r, method, t1, t2, t3, t4) \ r method(t1 a1, t2 a2, t3 a3, t4 a4) override { \ - proxy_internal::TraceApiCall(class_name_, PROXY_STRINGIZE(method)); \ + TRACE_BOILERPLATE(method); \ MethodCall call(c_, &C::method, std::move(a1), \ std::move(a2), std::move(a3), \ std::move(a4)); \ @@ -361,7 +391,7 @@ class ConstMethodCall : public QueuedTask { #define PROXY_METHOD5(r, method, t1, t2, t3, t4, t5) \ r method(t1 a1, t2 a2, t3 a3, t4 a4, t5 a5) override { \ - proxy_internal::TraceApiCall(class_name_, PROXY_STRINGIZE(method)); \ + TRACE_BOILERPLATE(method); \ MethodCall call(c_, &C::method, std::move(a1), \ std::move(a2), std::move(a3), \ std::move(a4), std::move(a5)); \ @@ -369,61 +399,61 @@ class ConstMethodCall : public QueuedTask { } // Define methods which should be invoked on the secondary thread. -#define PROXY_SECONDARY_METHOD0(r, method) \ - r method() override { \ - proxy_internal::TraceApiCall(class_name_, PROXY_STRINGIZE(method)); \ - MethodCall call(c_, &C::method); \ - return call.Marshal(RTC_FROM_HERE, secondary_thread_); \ +#define PROXY_SECONDARY_METHOD0(r, method) \ + r method() override { \ + TRACE_BOILERPLATE(method); \ + MethodCall call(c_, &C::method); \ + return call.Marshal(RTC_FROM_HERE, secondary_thread_); \ } -#define PROXY_SECONDARY_CONSTMETHOD0(r, method) \ - r method() const override { \ - proxy_internal::TraceApiCall(class_name_, PROXY_STRINGIZE(method)); \ - ConstMethodCall call(c_, &C::method); \ - return call.Marshal(RTC_FROM_HERE, secondary_thread_); \ +#define PROXY_SECONDARY_CONSTMETHOD0(r, method) \ + r method() const override { \ + TRACE_BOILERPLATE(method); \ + ConstMethodCall call(c_, &C::method); \ + return call.Marshal(RTC_FROM_HERE, secondary_thread_); \ } -#define PROXY_SECONDARY_METHOD1(r, method, t1) \ - r method(t1 a1) override { \ - proxy_internal::TraceApiCall(class_name_, PROXY_STRINGIZE(method)); \ - MethodCall call(c_, &C::method, std::move(a1)); \ - return call.Marshal(RTC_FROM_HERE, secondary_thread_); \ +#define PROXY_SECONDARY_METHOD1(r, method, t1) \ + r method(t1 a1) override { \ + TRACE_BOILERPLATE(method); \ + MethodCall call(c_, &C::method, std::move(a1)); \ + return call.Marshal(RTC_FROM_HERE, secondary_thread_); \ } -#define PROXY_SECONDARY_CONSTMETHOD1(r, method, t1) \ - r method(t1 a1) const override { \ - proxy_internal::TraceApiCall(class_name_, PROXY_STRINGIZE(method)); \ - ConstMethodCall call(c_, &C::method, std::move(a1)); \ - return call.Marshal(RTC_FROM_HERE, secondary_thread_); \ +#define PROXY_SECONDARY_CONSTMETHOD1(r, method, t1) \ + r method(t1 a1) const override { \ + TRACE_BOILERPLATE(method); \ + ConstMethodCall call(c_, &C::method, std::move(a1)); \ + return call.Marshal(RTC_FROM_HERE, secondary_thread_); \ } -#define PROXY_SECONDARY_METHOD2(r, method, t1, t2) \ - r method(t1 a1, t2 a2) override { \ - proxy_internal::TraceApiCall(class_name_, PROXY_STRINGIZE(method)); \ - MethodCall call(c_, &C::method, std::move(a1), \ - std::move(a2)); \ - return call.Marshal(RTC_FROM_HERE, secondary_thread_); \ +#define PROXY_SECONDARY_METHOD2(r, method, t1, t2) \ + r method(t1 a1, t2 a2) override { \ + TRACE_BOILERPLATE(method); \ + MethodCall call(c_, &C::method, std::move(a1), \ + std::move(a2)); \ + return call.Marshal(RTC_FROM_HERE, secondary_thread_); \ } -#define PROXY_SECONDARY_CONSTMETHOD2(r, method, t1, t2) \ - r method(t1 a1, t2 a2) const override { \ - proxy_internal::TraceApiCall(class_name_, PROXY_STRINGIZE(method)); \ - ConstMethodCall call(c_, &C::method, std::move(a1), \ - std::move(a2)); \ - return call.Marshal(RTC_FROM_HERE, secondary_thread_); \ +#define PROXY_SECONDARY_CONSTMETHOD2(r, method, t1, t2) \ + r method(t1 a1, t2 a2) const override { \ + TRACE_BOILERPLATE(method); \ + ConstMethodCall call(c_, &C::method, std::move(a1), \ + std::move(a2)); \ + return call.Marshal(RTC_FROM_HERE, secondary_thread_); \ } -#define PROXY_SECONDARY_METHOD3(r, method, t1, t2, t3) \ - r method(t1 a1, t2 a2, t3 a3) override { \ - proxy_internal::TraceApiCall(class_name_, PROXY_STRINGIZE(method)); \ - MethodCall call(c_, &C::method, std::move(a1), \ - std::move(a2), std::move(a3)); \ - return call.Marshal(RTC_FROM_HERE, secondary_thread_); \ +#define PROXY_SECONDARY_METHOD3(r, method, t1, t2, t3) \ + r method(t1 a1, t2 a2, t3 a3) override { \ + TRACE_BOILERPLATE(method); \ + MethodCall call(c_, &C::method, std::move(a1), \ + std::move(a2), std::move(a3)); \ + return call.Marshal(RTC_FROM_HERE, secondary_thread_); \ } #define PROXY_SECONDARY_CONSTMETHOD3(r, method, t1, t2) \ r method(t1 a1, t2 a2, t3 a3) const override { \ - proxy_internal::TraceApiCall(class_name_, PROXY_STRINGIZE(method)); \ + TRACE_BOILERPLATE(method); \ ConstMethodCall call(c_, &C::method, std::move(a1), \ std::move(a2), std::move(a3)); \ return call.Marshal(RTC_FROM_HERE, secondary_thread_); \ @@ -432,10 +462,10 @@ class ConstMethodCall : public QueuedTask { // For use when returning purely const state (set during construction). // Use with caution. This method should only be used when the return value will // always be the same. -#define BYPASS_PROXY_CONSTMETHOD0(r, method) \ - r method() const override { \ - proxy_internal::TraceApiCall(class_name_, PROXY_STRINGIZE(method)); \ - return c_->method(); \ +#define BYPASS_PROXY_CONSTMETHOD0(r, method) \ + r method() const override { \ + TRACE_BOILERPLATE(method); \ + return c_->method(); \ } } // namespace webrtc diff --git a/pc/proxy_unittest.cc b/pc/proxy_unittest.cc index 497c99a54a..ef3d97eddc 100644 --- a/pc/proxy_unittest.cc +++ b/pc/proxy_unittest.cc @@ -71,7 +71,7 @@ PROXY_CONSTMETHOD0(std::string, ConstMethod0) PROXY_SECONDARY_METHOD1(std::string, Method1, std::string) PROXY_CONSTMETHOD1(std::string, ConstMethod1, std::string) PROXY_SECONDARY_METHOD2(std::string, Method2, std::string, std::string) -END_PROXY_MAP() +END_PROXY_MAP(Fake) // Preprocessor hack to get a proxy class a name different than FakeProxy. #define FakeProxy FakeSignalingProxy @@ -84,7 +84,7 @@ PROXY_CONSTMETHOD0(std::string, ConstMethod0) PROXY_METHOD1(std::string, Method1, std::string) PROXY_CONSTMETHOD1(std::string, ConstMethod1, std::string) PROXY_METHOD2(std::string, Method2, std::string, std::string) -END_PROXY_MAP() +END_PROXY_MAP(Fake) #undef FakeProxy class SignalingProxyTest : public ::testing::Test { @@ -272,7 +272,7 @@ class Foo : public FooInterface { BEGIN_OWNED_PROXY_MAP(Foo) PROXY_PRIMARY_THREAD_DESTRUCTOR() PROXY_METHOD0(void, Bar) -END_PROXY_MAP() +END_PROXY_MAP(Foo) class OwnedProxyTest : public ::testing::Test { public: diff --git a/pc/rtp_receiver_proxy.h b/pc/rtp_receiver_proxy.h index 409612e744..d4114e0f0b 100644 --- a/pc/rtp_receiver_proxy.h +++ b/pc/rtp_receiver_proxy.h @@ -47,7 +47,7 @@ PROXY_SECONDARY_CONSTMETHOD0(rtc::scoped_refptr, PROXY_SECONDARY_METHOD1(void, SetDepacketizerToDecoderFrameTransformer, rtc::scoped_refptr) -END_PROXY_MAP() +END_PROXY_MAP(RtpReceiver) } // namespace webrtc diff --git a/pc/rtp_sender_proxy.h b/pc/rtp_sender_proxy.h index e23f57b38e..2f8fe2c0bf 100644 --- a/pc/rtp_sender_proxy.h +++ b/pc/rtp_sender_proxy.h @@ -44,7 +44,7 @@ PROXY_METHOD1(void, SetStreams, const std::vector&) PROXY_METHOD1(void, SetEncoderToPacketizerFrameTransformer, rtc::scoped_refptr) -END_PROXY_MAP() +END_PROXY_MAP(RtpSender) } // namespace webrtc diff --git a/pc/rtp_transceiver.h b/pc/rtp_transceiver.h index 3c7db95c5a..6b1307b1db 100644 --- a/pc/rtp_transceiver.h +++ b/pc/rtp_transceiver.h @@ -310,7 +310,7 @@ PROXY_CONSTMETHOD0(std::vector, PROXY_METHOD1(webrtc::RTCError, SetOfferedRtpHeaderExtensions, rtc::ArrayView) -END_PROXY_MAP() +END_PROXY_MAP(RtpTransceiver) } // namespace webrtc diff --git a/pc/sctp_data_channel.cc b/pc/sctp_data_channel.cc index 76091c25bf..359cc798c8 100644 --- a/pc/sctp_data_channel.cc +++ b/pc/sctp_data_channel.cc @@ -65,7 +65,7 @@ PROXY_CONSTMETHOD0(uint64_t, buffered_amount) PROXY_METHOD0(void, Close) // TODO(bugs.webrtc.org/11547): Change to run on the network thread. PROXY_METHOD1(bool, Send, const DataBuffer&) -END_PROXY_MAP() +END_PROXY_MAP(DataChannel) } // namespace diff --git a/pc/video_track_source_proxy.h b/pc/video_track_source_proxy.h index 5a6def6935..8914dd0525 100644 --- a/pc/video_track_source_proxy.h +++ b/pc/video_track_source_proxy.h @@ -42,7 +42,7 @@ PROXY_SECONDARY_METHOD1(void, PROXY_SECONDARY_METHOD1(void, RemoveEncodedSink, rtc::VideoSinkInterface*) -END_PROXY_MAP() +END_PROXY_MAP(VideoTrackSource) } // namespace webrtc diff --git a/rtc_base/string_utils.h b/rtc_base/string_utils.h index 23c55cb893..d844e5e125 100644 --- a/rtc_base/string_utils.h +++ b/rtc_base/string_utils.h @@ -88,6 +88,43 @@ std::string string_trim(const std::string& s); // TODO(jonasolsson): replace with absl::Hex when that becomes available. std::string ToHex(const int i); +// CompileTimeString comprises of a string-like object which can be used as a +// regular const char* in compile time and supports concatenation. Useful for +// concatenating constexpr strings in for example macro declarations. +namespace rtc_base_string_utils_internal { +template +struct CompileTimeString { + char string[NPlus1] = {0}; + constexpr CompileTimeString() = default; + template + explicit constexpr CompileTimeString(const char (&chars)[MPlus1]) { + char* chars_pointer = string; + for (auto c : chars) + *chars_pointer++ = c; + } + template + constexpr auto Concat(CompileTimeString b) { + CompileTimeString result; + char* chars_pointer = result.string; + for (auto c : string) + *chars_pointer++ = c; + chars_pointer = result.string + NPlus1 - 1; + for (auto c : b.string) + *chars_pointer++ = c; + result.string[NPlus1 + MPlus1 - 2] = 0; + return result; + } + constexpr operator const char*() { return string; } +}; +} // namespace rtc_base_string_utils_internal + +// Makes a constexpr CompileTimeString without having to specify X +// explicitly. +template +constexpr auto MakeCompileTimeString(const char (&a)[N]) { + return rtc_base_string_utils_internal::CompileTimeString(a); +} + } // namespace rtc #endif // RTC_BASE_STRING_UTILS_H_ diff --git a/rtc_base/string_utils_unittest.cc b/rtc_base/string_utils_unittest.cc index 2fa1f220ac..120f7e60f5 100644 --- a/rtc_base/string_utils_unittest.cc +++ b/rtc_base/string_utils_unittest.cc @@ -39,4 +39,29 @@ TEST(string_toutf, Empty) { #endif // WEBRTC_WIN +TEST(CompileTimeString, MakeActsLikeAString) { + EXPECT_STREQ(MakeCompileTimeString("abc123"), "abc123"); +} + +TEST(CompileTimeString, ConvertibleToStdString) { + EXPECT_EQ(std::string(MakeCompileTimeString("abab")), "abab"); +} + +namespace detail { +constexpr bool StringEquals(const char* a, const char* b) { + while (*a && *a == *b) + a++, b++; + return *a == *b; +} +} // namespace detail + +static_assert(detail::StringEquals(MakeCompileTimeString("handellm"), + "handellm"), + "String should initialize."); + +static_assert(detail::StringEquals(MakeCompileTimeString("abc123").Concat( + MakeCompileTimeString("def456ghi")), + "abc123def456ghi"), + "Strings should concatenate."); + } // namespace rtc