Protobuf 3.4.0下非反射式指定字段比较的C宏实现需求
Alright, let's solve this problem without relying on reflection (good call avoiding MessageDifferencer if that's a concern!). Since you're on Protobuf 3.4.0 and need to compare specific fields—including well-known types like Timestamp and Duration—we can build a combination of template helper functions and a macro to get that clean, concise syntax you want.
Step 1: Create Type-Specific Comparison Helpers
First, we'll write template functions that handle comparing different field types. For basic Protobuf types (like int32, string, double), a simple equality check works. For well-known types, we need to dig into their internal values (seconds() and nanos()):
#include <google/protobuf/timestamp.pb.h> #include <google/protobuf/duration.pb.h> // Generic comparison for basic Protobuf types template<typename T> bool CompareProtobufField(const T& expected, const T& actual) { return expected == actual; } // Specialization for Timestamp (compares both seconds and nanos) template<> bool CompareProtobufField(const google::protobuf::Timestamp& expected, const google::protobuf::Timestamp& actual) { return expected.seconds() == actual.seconds() && expected.nanos() == actual.nanos(); } // Specialization for Duration (same deep-compare logic as Timestamp) template<> bool CompareProtobufField(const google::protobuf::Duration& expected, const google::protobuf::Duration& actual) { return expected.seconds() == actual.seconds() && expected.nanos() == actual.nanos(); }
Step 2: Build the Macro for Clean Calls
Next, we'll wrap this logic in a macro that takes your two objects and the field name, expands to the comparison, and integrates with your CHECK assertion (assuming you're using a framework like Google Test—adjust the assertion syntax if you're using something else):
#define CHECK_PROTO_FIELD(expected_obj, actual_obj, field_name) \ CHECK(::CompareProtobufField(expected_obj.field_name(), actual_obj.field_name())) \ << "Mismatch in field '" #field_name "'!\n" \ << "Expected: " << expected_obj.field_name().DebugString() << "\n" \ << "Actual: " << actual_obj.field_name().DebugString()
How to Use It
Let's say you have a GpsData proto definition like this:
message GpsData { google.protobuf.Timestamp gps_time = 1; double latitude = 2; google.protobuf.Duration time_since_last_fix = 3; }
You can use the macro exactly like you wanted (it's generic, so it works for any field in your proto):
GpsData expectedGps; GpsData storedGps; // Populate your objects with test data... CHECK_PROTO_FIELD(expectedGps, storedGps, gps_time); CHECK_PROTO_FIELD(expectedGps, storedGps, latitude); CHECK_PROTO_FIELD(expectedGps, storedGps, time_since_last_fix);
Bonus: Handle Optional/Unset Fields (If Needed)
If you need to verify whether the field was actually set (not just compare values), you can tweak the specialized functions. For example, for Timestamp:
template<> bool CompareProtobufField(const google::protobuf::Timestamp& expected, const google::protobuf::Timestamp& actual) { // Check if both fields are unset (using default values as a proxy) bool expected_unset = (expected.seconds() == 0 && expected.nanos() == 0); bool actual_unset = (actual.seconds() == 0 && actual.nanos() == 0); if (expected_unset != actual_unset) { return false; // One field is set, the other isn't } // If both are set, compare their values return expected.seconds() == actual.seconds() && expected.nanos() == actual.nanos(); }
Why This Works
- No Reflection: All comparisons are resolved at compile time via template specialization—no runtime reflection overhead.
- Clean Syntax: The macro hides the boilerplate of accessing nested fields like
.seconds()and.nanos(). - Debuggable: If the check fails,
DebugString()prints the full field value, making it easy to spot exactly what went wrong.
内容的提问来源于stack exchange,提问作者János Balázs

