protos: Collapse ClearMessage template
We introduce Ptr removal so that our helper functions can be agnostically passed in a Ptr<T> or a T*.
We utilize SFINAE to determine if PtrOrRaw. If the target is const, or not in {T, T*, Ptr<T>}, the template will not match and be discarded (but still compile successfully).
PiperOrigin-RevId: 640198851
diff --git a/protos/protos.h b/protos/protos.h
index 87547f9..6037b44 100644
--- a/protos/protos.h
+++ b/protos/protos.h
@@ -242,6 +242,26 @@
const upb_MiniTableExtension* ext,
const upb_Message* extension);
+template <typename T>
+struct RemovePtr;
+
+template <typename T>
+struct RemovePtr<Ptr<T>> {
+ using type = T;
+};
+
+template <typename T>
+struct RemovePtr<T*> {
+ using type = T;
+};
+
+template <typename T>
+using RemovePtrT = typename RemovePtr<T>::type;
+
+template <typename T, typename U = RemovePtrT<T>,
+ typename = std::enable_if_t<!std::is_const_v<U>>>
+using PtrOrRaw = T;
+
} // namespace internal
template <typename T>
@@ -280,14 +300,10 @@
}
template <typename T>
-void ClearMessage(Ptr<T> message) {
- static_assert(!std::is_const_v<T>, "");
- upb_Message_Clear(internal::GetInternalMsg(message), T::minitable());
-}
-
-template <typename T>
-void ClearMessage(T* message) {
- ClearMessage(protos::Ptr(message));
+void ClearMessage(internal::PtrOrRaw<T> message) {
+ auto ptr = Ptr(message);
+ auto minitable = internal::GetMiniTable(ptr);
+ upb_Message_Clear(internal::GetInternalMsg(ptr), minitable);
}
class ExtensionRegistry {
diff --git a/protos_generator/tests/test_generated.cc b/protos_generator/tests/test_generated.cc
index 61bd66f..35d8562 100644
--- a/protos_generator/tests/test_generated.cc
+++ b/protos_generator/tests/test_generated.cc
@@ -1157,6 +1157,44 @@
EXPECT_FALSE(::protos::HasExtension(&model, theme));
}
+TEST(CppGeneratedCode, CanInvokeClearMessageWithPtr) {
+ // Fill model.
+ TestModel model;
+ model.set_int64(5);
+ auto new_child = model.add_child_models();
+ // Clear using Ptr<T>
+ auto ptr = ::protos::Ptr<TestModel>(&model);
+ ::protos::ClearMessage(ptr);
+ // Successful clear
+ EXPECT_FALSE(model.has_int64());
+}
+
+TEST(CppGeneratedCode, CanInvokeClearMessageWithRawPtr) {
+ // Fill model.
+ TestModel model;
+ model.set_int64(5);
+ auto new_child = model.add_child_models();
+ // Clear using T*
+ ::protos::ClearMessage(&model);
+ // Successful clear
+ EXPECT_FALSE(model.has_int64());
+}
+
+template <typename T>
+bool CanCallClearMessage() {
+ return Requires<T>([](auto x) -> decltype(::protos::ClearMessage(x)) {});
+}
+
+TEST(CppGeneratedCode, CannotInvokeClearMessageWithConstPtr) {
+ EXPECT_TRUE(CanCallClearMessage<::protos::Ptr<TestModel>>());
+ EXPECT_FALSE(CanCallClearMessage<::protos::Ptr<const TestModel>>());
+}
+
+TEST(CppGeneratedCode, CannotInvokeClearMessageWithConstRawPtr) {
+ EXPECT_TRUE(CanCallClearMessage<TestModel*>());
+ EXPECT_FALSE(CanCallClearMessage<const TestModel*>());
+}
+
TEST(CppGeneratedCode, DeepCopy) {
// Fill model.
TestModel model;
@@ -1203,16 +1241,10 @@
EXPECT_EQ(225, TestModel::kChildMapFieldNumber);
}
-// TODO : Add BUILD rule to test failures below.
-#ifdef TEST_CLEAR_MESSAGE_FAILURE
-TEST(CppGeneratedCode, ClearConstMessageShouldFail) {
- // Fill model.
+TEST(CppGeneratedCode, ClearConstMessageShouldFailForConstChild) {
TestModel model;
- model.set_int64(5);
- model.set_str2("Hello");
- // Only mutable_ can be cleared not Ptr<const T>.
- ::protos::ClearMessage(model.child_model_1());
+ EXPECT_FALSE(CanCallClearMessage<decltype(model.child_model_1())>());
+ EXPECT_TRUE(CanCallClearMessage<decltype(model.mutable_child_model_1())>());
}
-#endif
} // namespace