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)
fail();
return std::make_tuple(message, field, index);
return ProtoField(message, field, index);
}
ProtoField makeProtoPath(
@@ -210,207 +210,232 @@ static void updateRepeatedField(
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");
switch (field->type())
switch (_field->type())
{
case google::protobuf::FieldDescriptor::TYPE_FLOAT:
updateRepeatedField(
reflection->GetMutableRepeatedFieldRef<float>(
message, field),
index,
_message, _field),
_index,
toFloat(value));
break;
case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
updateRepeatedField(
reflection->GetMutableRepeatedFieldRef<double>(
message, field),
index,
_message, _field),
_index,
toDouble(value));
break;
case google::protobuf::FieldDescriptor::TYPE_INT32:
updateRepeatedField(
reflection->GetMutableRepeatedFieldRef<int32_t>(
message, field),
index,
_message, _field),
_index,
(int32_t)toInt64(value));
break;
case google::protobuf::FieldDescriptor::TYPE_INT64:
updateRepeatedField(
reflection->GetMutableRepeatedFieldRef<int64_t>(
message, field),
index,
_message, _field),
_index,
toInt64(value));
break;
case google::protobuf::FieldDescriptor::TYPE_UINT32:
updateRepeatedField(
reflection->GetMutableRepeatedFieldRef<uint32_t>(
message, field),
index,
_message, _field),
_index,
(uint32_t)toUint64(value));
break;
case google::protobuf::FieldDescriptor::TYPE_UINT64:
updateRepeatedField(
reflection->GetMutableRepeatedFieldRef<uint64_t>(
message, field),
index,
_message, _field),
_index,
toUint64(value));
break;
case google::protobuf::FieldDescriptor::TYPE_STRING:
updateRepeatedField(
reflection->GetMutableRepeatedFieldRef<std::string>(
message, field),
index,
_message, _field),
_index,
value);
break;
case google::protobuf::FieldDescriptor::TYPE_BOOL:
updateRepeatedField(
reflection->GetMutableRepeatedFieldRef<bool>(
message, field),
index,
_message, _field),
_index,
parseBoolean(value));
break;
case google::protobuf::FieldDescriptor::TYPE_ENUM:
updateRepeatedField(
reflection->GetMutableRepeatedFieldRef<int32_t>(
message, field),
index,
parseEnum(field, value));
_message, _field),
_index,
parseEnum(_field, value));
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:
error("can't set this config value type");
}
}
else
{
if (index != -1)
if (_index != -1)
error("field '{}' is not repeated but an index is provided");
switch (field->type())
switch (_field->type())
{
case google::protobuf::FieldDescriptor::TYPE_FLOAT:
reflection->SetFloat(message, field, toFloat(value));
reflection->SetFloat(_message, _field, toFloat(value));
break;
case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
reflection->SetDouble(message, field, toDouble(value));
reflection->SetDouble(_message, _field, toDouble(value));
break;
case google::protobuf::FieldDescriptor::TYPE_INT32:
reflection->SetInt32(message, field, toInt64(value));
reflection->SetInt32(_message, _field, toInt64(value));
break;
case google::protobuf::FieldDescriptor::TYPE_INT64:
reflection->SetInt64(message, field, toInt64(value));
reflection->SetInt64(_message, _field, toInt64(value));
break;
case google::protobuf::FieldDescriptor::TYPE_UINT32:
reflection->SetUInt32(message, field, toUint64(value));
reflection->SetUInt32(_message, _field, toUint64(value));
break;
case google::protobuf::FieldDescriptor::TYPE_UINT64:
reflection->SetUInt64(message, field, toUint64(value));
reflection->SetUInt64(_message, _field, toUint64(value));
break;
case google::protobuf::FieldDescriptor::TYPE_STRING:
reflection->SetString(message, field, value);
reflection->SetString(_message, _field, value);
break;
case google::protobuf::FieldDescriptor::TYPE_BOOL:
reflection->SetBool(message, field, parseBoolean(value));
reflection->SetBool(_message, _field, parseBoolean(value));
break;
case google::protobuf::FieldDescriptor::TYPE_ENUM:
reflection->SetEnumValue(message, field, parseEnum(field, value));
reflection->SetEnumValue(
_message, _field, parseEnum(_field, value));
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:
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();
switch (field->type())
const auto* reflection = _message->GetReflection();
if (_field->label() == google::protobuf::FieldDescriptor::LABEL_REPEATED)
{
case google::protobuf::FieldDescriptor::TYPE_FLOAT:
return fmt::format("{}", reflection->GetFloat(*message, field));
if (_index == -1)
error("field '{}' is repeated but no index is provided",
_field->name());
case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
return fmt::format("{}", 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:
return std::to_string(reflection->GetBool(*message, field));
case google::protobuf::FieldDescriptor::TYPE_ENUM:
switch (_field->type())
{
const auto* enumvalue = reflection->GetEnum(*message, field);
return enumvalue->name();
case google::protobuf::FieldDescriptor::TYPE_FLOAT:
return fmt::format("{:g}",
reflection->GetRepeatedFloat(*_message, _field, _index));
case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
return fmt::format("{:g}",
reflection->GetRepeatedDouble(*_message, _field, _index));
case google::protobuf::FieldDescriptor::TYPE_INT32:
return std::to_string(
reflection->GetRepeatedInt32(*_message, _field, _index));
case google::protobuf::FieldDescriptor::TYPE_INT64:
return std::to_string(
reflection->GetRepeatedInt64(*_message, _field, _index));
case google::protobuf::FieldDescriptor::TYPE_UINT32:
return std::to_string(
reflection->GetRepeatedUInt32(*_message, _field, _index));
case google::protobuf::FieldDescriptor::TYPE_UINT64:
return std::to_string(
reflection->GetRepeatedUInt64(*_message, _field, _index));
case google::protobuf::FieldDescriptor::TYPE_STRING:
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_MESSAGE:
error("cannot fetch message value");
case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
return fmt::format(
"{:g}", reflection->GetDouble(*_message, _field));
default:
error("unknown field type when fetching");
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:
return std::to_string(reflection->GetBool(*_message, _field));
case google::protobuf::FieldDescriptor::TYPE_ENUM:
{
const auto* enumvalue = reflection->GetEnum(*_message, _field);
return enumvalue->name();
}
case google::protobuf::FieldDescriptor::TYPE_MESSAGE:
error("cannot fetch message value");
default:
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& value)
{
ProtoField protoField = makeProtoPath(message, path);
setProtoFieldFromString(protoField, value);
makeProtoPath(message, path).set(value);
}
std::string getProtoByString(
google::protobuf::Message* message, const std::string& path)
{
ProtoField protoField = findProtoPath(message, path);
return getProtoFieldValue(protoField);
return findProtoPath(message, path).get();
}
std::set<unsigned> iterate(unsigned start, unsigned count)

View File

@@ -14,9 +14,29 @@ public:
}
};
typedef std::tuple<google::protobuf::Message*,
const google::protobuf::FieldDescriptor*, int>
ProtoField;
class 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(
google::protobuf::Message* message, const std::string& path);

View File

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