Finally make the getters and setters work with repeated fields.

This commit is contained in:
David Given
2025-08-17 23:04:14 +02:00
parent 0a5604521e
commit 9ff3e3b42a
3 changed files with 147 additions and 111 deletions

View File

@@ -157,7 +157,7 @@ static ProtoField resolveProtoPath(
if (!field) if (!field)
fail(); fail();
return std::make_tuple(message, field, index); return ProtoField(message, field, index);
} }
ProtoField makeProtoPath( ProtoField makeProtoPath(
@@ -210,199 +210,223 @@ static void updateRepeatedField(
mrfr.Set(index, value); mrfr.Set(index, value);
} }
void setProtoFieldFromString(ProtoField& protoField, const std::string& value) void ProtoField::set(const std::string& value)
{ {
auto& [message, field, index] = protoField; const auto* reflection = _message->GetReflection();
if (_field->label() == google::protobuf::FieldDescriptor::LABEL_REPEATED)
const auto* reflection = message->GetReflection();
if (field->label() == google::protobuf::FieldDescriptor::LABEL_REPEATED)
{ {
if (index == -1) if (_index == -1)
error("field '{}' is repeated but no index is provided"); error("field '{}' is repeated but no index is provided");
switch (field->type()) switch (_field->type())
{ {
case google::protobuf::FieldDescriptor::TYPE_FLOAT: case google::protobuf::FieldDescriptor::TYPE_FLOAT:
updateRepeatedField( updateRepeatedField(
reflection->GetMutableRepeatedFieldRef<float>( reflection->GetMutableRepeatedFieldRef<float>(
message, field), _message, _field),
index, _index,
toFloat(value)); toFloat(value));
break; break;
case google::protobuf::FieldDescriptor::TYPE_DOUBLE: case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
updateRepeatedField( updateRepeatedField(
reflection->GetMutableRepeatedFieldRef<double>( reflection->GetMutableRepeatedFieldRef<double>(
message, field), _message, _field),
index, _index,
toDouble(value)); toDouble(value));
break; break;
case google::protobuf::FieldDescriptor::TYPE_INT32: case google::protobuf::FieldDescriptor::TYPE_INT32:
updateRepeatedField( updateRepeatedField(
reflection->GetMutableRepeatedFieldRef<int32_t>( reflection->GetMutableRepeatedFieldRef<int32_t>(
message, field), _message, _field),
index, _index,
(int32_t)toInt64(value)); (int32_t)toInt64(value));
break; break;
case google::protobuf::FieldDescriptor::TYPE_INT64: case google::protobuf::FieldDescriptor::TYPE_INT64:
updateRepeatedField( updateRepeatedField(
reflection->GetMutableRepeatedFieldRef<int64_t>( reflection->GetMutableRepeatedFieldRef<int64_t>(
message, field), _message, _field),
index, _index,
toInt64(value)); toInt64(value));
break; break;
case google::protobuf::FieldDescriptor::TYPE_UINT32: case google::protobuf::FieldDescriptor::TYPE_UINT32:
updateRepeatedField( updateRepeatedField(
reflection->GetMutableRepeatedFieldRef<uint32_t>( reflection->GetMutableRepeatedFieldRef<uint32_t>(
message, field), _message, _field),
index, _index,
(uint32_t)toUint64(value)); (uint32_t)toUint64(value));
break; break;
case google::protobuf::FieldDescriptor::TYPE_UINT64: case google::protobuf::FieldDescriptor::TYPE_UINT64:
updateRepeatedField( updateRepeatedField(
reflection->GetMutableRepeatedFieldRef<uint64_t>( reflection->GetMutableRepeatedFieldRef<uint64_t>(
message, field), _message, _field),
index, _index,
toUint64(value)); toUint64(value));
break; break;
case google::protobuf::FieldDescriptor::TYPE_STRING: case google::protobuf::FieldDescriptor::TYPE_STRING:
updateRepeatedField( updateRepeatedField(
reflection->GetMutableRepeatedFieldRef<std::string>( reflection->GetMutableRepeatedFieldRef<std::string>(
message, field), _message, _field),
index, _index,
value); value);
break; break;
case google::protobuf::FieldDescriptor::TYPE_BOOL: case google::protobuf::FieldDescriptor::TYPE_BOOL:
updateRepeatedField( updateRepeatedField(
reflection->GetMutableRepeatedFieldRef<bool>( reflection->GetMutableRepeatedFieldRef<bool>(
message, field), _message, _field),
index, _index,
parseBoolean(value)); parseBoolean(value));
break; break;
case google::protobuf::FieldDescriptor::TYPE_ENUM: case google::protobuf::FieldDescriptor::TYPE_ENUM:
updateRepeatedField( updateRepeatedField(
reflection->GetMutableRepeatedFieldRef<int32_t>( reflection->GetMutableRepeatedFieldRef<int32_t>(
message, field), _message, _field),
index, _index,
parseEnum(field, value)); parseEnum(_field, value));
break; break;
case google::protobuf::FieldDescriptor::TYPE_MESSAGE:
if (field->message_type() == RangeProto::descriptor())
{
setRange((RangeProto*)reflection->MutableRepeatedMessage(
message, field, index),
value);
break;
}
/* fall through */
default: default:
error("can't set this config value type"); error("can't set this config value type");
} }
} }
else else
{ {
if (index != -1) if (_index != -1)
error("field '{}' is not repeated but an index is provided"); error("field '{}' is not repeated but an index is provided");
switch (field->type()) switch (_field->type())
{ {
case google::protobuf::FieldDescriptor::TYPE_FLOAT: case google::protobuf::FieldDescriptor::TYPE_FLOAT:
reflection->SetFloat(message, field, toFloat(value)); reflection->SetFloat(_message, _field, toFloat(value));
break; break;
case google::protobuf::FieldDescriptor::TYPE_DOUBLE: case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
reflection->SetDouble(message, field, toDouble(value)); reflection->SetDouble(_message, _field, toDouble(value));
break; break;
case google::protobuf::FieldDescriptor::TYPE_INT32: case google::protobuf::FieldDescriptor::TYPE_INT32:
reflection->SetInt32(message, field, toInt64(value)); reflection->SetInt32(_message, _field, toInt64(value));
break; break;
case google::protobuf::FieldDescriptor::TYPE_INT64: case google::protobuf::FieldDescriptor::TYPE_INT64:
reflection->SetInt64(message, field, toInt64(value)); reflection->SetInt64(_message, _field, toInt64(value));
break; break;
case google::protobuf::FieldDescriptor::TYPE_UINT32: case google::protobuf::FieldDescriptor::TYPE_UINT32:
reflection->SetUInt32(message, field, toUint64(value)); reflection->SetUInt32(_message, _field, toUint64(value));
break; break;
case google::protobuf::FieldDescriptor::TYPE_UINT64: case google::protobuf::FieldDescriptor::TYPE_UINT64:
reflection->SetUInt64(message, field, toUint64(value)); reflection->SetUInt64(_message, _field, toUint64(value));
break; break;
case google::protobuf::FieldDescriptor::TYPE_STRING: case google::protobuf::FieldDescriptor::TYPE_STRING:
reflection->SetString(message, field, value); reflection->SetString(_message, _field, value);
break; break;
case google::protobuf::FieldDescriptor::TYPE_BOOL: case google::protobuf::FieldDescriptor::TYPE_BOOL:
reflection->SetBool(message, field, parseBoolean(value)); reflection->SetBool(_message, _field, parseBoolean(value));
break; break;
case google::protobuf::FieldDescriptor::TYPE_ENUM: case google::protobuf::FieldDescriptor::TYPE_ENUM:
reflection->SetEnumValue(message, field, parseEnum(field, value)); reflection->SetEnumValue(
_message, _field, parseEnum(_field, value));
break; break;
case google::protobuf::FieldDescriptor::TYPE_MESSAGE:
if (field->message_type() == RangeProto::descriptor())
{
setRange(
(RangeProto*)reflection->MutableMessage(message, field),
value);
break;
}
if (field->containing_oneof() && value.empty())
{
reflection->MutableMessage(message, field);
break;
}
/* fall through */
default: default:
error("can't set this config value type"); error("can't set this config value type");
} }
} }
} }
std::string getProtoFieldValue(ProtoField& protoField) std::string ProtoField::get() const
{ {
auto& [message, field, index] = protoField; const auto* reflection = _message->GetReflection();
if (_field->label() == google::protobuf::FieldDescriptor::LABEL_REPEATED)
{
if (_index == -1)
error("field '{}' is repeated but no index is provided",
_field->name());
const auto* reflection = message->GetReflection(); switch (_field->type())
switch (field->type())
{ {
case google::protobuf::FieldDescriptor::TYPE_FLOAT: case google::protobuf::FieldDescriptor::TYPE_FLOAT:
return fmt::format("{}", reflection->GetFloat(*message, field)); return fmt::format("{:g}",
reflection->GetRepeatedFloat(*_message, _field, _index));
case google::protobuf::FieldDescriptor::TYPE_DOUBLE: case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
return fmt::format("{}", reflection->GetDouble(*message, field)); return fmt::format("{:g}",
reflection->GetRepeatedDouble(*_message, _field, _index));
case google::protobuf::FieldDescriptor::TYPE_INT32: case google::protobuf::FieldDescriptor::TYPE_INT32:
return std::to_string(reflection->GetInt32(*message, field)); return std::to_string(
reflection->GetRepeatedInt32(*_message, _field, _index));
case google::protobuf::FieldDescriptor::TYPE_INT64: case google::protobuf::FieldDescriptor::TYPE_INT64:
return std::to_string(reflection->GetInt64(*message, field)); return std::to_string(
reflection->GetRepeatedInt64(*_message, _field, _index));
case google::protobuf::FieldDescriptor::TYPE_UINT32: case google::protobuf::FieldDescriptor::TYPE_UINT32:
return std::to_string(reflection->GetUInt32(*message, field)); return std::to_string(
reflection->GetRepeatedUInt32(*_message, _field, _index));
case google::protobuf::FieldDescriptor::TYPE_UINT64: case google::protobuf::FieldDescriptor::TYPE_UINT64:
return std::to_string(reflection->GetUInt64(*message, field)); return std::to_string(
reflection->GetRepeatedUInt64(*_message, _field, _index));
case google::protobuf::FieldDescriptor::TYPE_STRING: case google::protobuf::FieldDescriptor::TYPE_STRING:
return reflection->GetString(*message, field); return reflection->GetRepeatedString(*_message, _field, _index);
case google::protobuf::FieldDescriptor::TYPE_MESSAGE:
error("cannot fetch message value");
default:
error("unknown field type when fetching repeated field '{}'",
_field->name());
}
}
else
{
if (_index != -1)
error("field '{}' is not repeated but an index is provided",
_field->name());
switch (_field->type())
{
case google::protobuf::FieldDescriptor::TYPE_FLOAT:
return fmt::format(
"{:g}", reflection->GetFloat(*_message, _field));
case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
return fmt::format(
"{:g}", reflection->GetDouble(*_message, _field));
case google::protobuf::FieldDescriptor::TYPE_INT32:
return std::to_string(reflection->GetInt32(*_message, _field));
case google::protobuf::FieldDescriptor::TYPE_INT64:
return std::to_string(reflection->GetInt64(*_message, _field));
case google::protobuf::FieldDescriptor::TYPE_UINT32:
return std::to_string(reflection->GetUInt32(*_message, _field));
case google::protobuf::FieldDescriptor::TYPE_UINT64:
return std::to_string(reflection->GetUInt64(*_message, _field));
case google::protobuf::FieldDescriptor::TYPE_STRING:
return reflection->GetString(*_message, _field);
case google::protobuf::FieldDescriptor::TYPE_BOOL: case google::protobuf::FieldDescriptor::TYPE_BOOL:
return std::to_string(reflection->GetBool(*message, field)); return std::to_string(reflection->GetBool(*_message, _field));
case google::protobuf::FieldDescriptor::TYPE_ENUM: case google::protobuf::FieldDescriptor::TYPE_ENUM:
{ {
const auto* enumvalue = reflection->GetEnum(*message, field); const auto* enumvalue = reflection->GetEnum(*_message, _field);
return enumvalue->name(); return enumvalue->name();
} }
@@ -410,7 +434,8 @@ std::string getProtoFieldValue(ProtoField& protoField)
error("cannot fetch message value"); error("cannot fetch message value");
default: default:
error("unknown field type when fetching"); error("unknown field type when fetching '{}'", _field->name());
}
} }
} }
@@ -418,15 +443,13 @@ void setProtoByString(google::protobuf::Message* message,
const std::string& path, const std::string& path,
const std::string& value) const std::string& value)
{ {
ProtoField protoField = makeProtoPath(message, path); makeProtoPath(message, path).set(value);
setProtoFieldFromString(protoField, value);
} }
std::string getProtoByString( std::string getProtoByString(
google::protobuf::Message* message, const std::string& path) google::protobuf::Message* message, const std::string& path)
{ {
ProtoField protoField = findProtoPath(message, path); return findProtoPath(message, path).get();
return getProtoFieldValue(protoField);
} }
std::set<unsigned> iterate(unsigned start, unsigned count) std::set<unsigned> iterate(unsigned start, unsigned count)

View File

@@ -14,9 +14,29 @@ public:
} }
}; };
typedef std::tuple<google::protobuf::Message*, class ProtoField
const google::protobuf::FieldDescriptor*, int> {
ProtoField; public:
ProtoField(google::protobuf::Message* message,
const google::protobuf::FieldDescriptor* field,
int index):
_message(message),
_field(field),
_index(index)
{
}
void set(const std::string& value);
std::string get() const;
bool operator==(const ProtoField& other) const = default;
std::strong_ordering operator<=>(const ProtoField& other) const = default;
private:
google::protobuf::Message* _message;
const google::protobuf::FieldDescriptor* _field;
int _index;
};
extern ProtoField makeProtoPath( extern ProtoField makeProtoPath(
google::protobuf::Message* message, const std::string& path); google::protobuf::Message* message, const std::string& path);

View File

@@ -162,7 +162,6 @@ static void test_fields(void)
"r[]", "r[]",
"r[].r[]", "r[].r[]",
"r[].s", "r[].s",
"range",
"secondoption", "secondoption",
"secondoption.r[]", "secondoption.r[]",
"secondoption.s", "secondoption.s",
@@ -201,11 +200,6 @@ static void test_findallfields(void)
secondoption { secondoption {
s: "2" s: "2"
} }
range {
start: 1
step: 2
end: 3
}
)M"; )M";
TestProto proto; TestProto proto;
@@ -225,7 +219,6 @@ static void test_findallfields(void)
"m.s", "m.s",
"r[0].s", "r[0].s",
"r[1].s", "r[1].s",
"range",
"secondoption.s", "secondoption.s",
"u32", "u32",
"u64"})); "u64"}));