Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TryGetExtension API (with TryGetOption) #7282

Conversation

ObsidianMinor
Copy link
Contributor

Adds:

  • IExtendableMessage.TryGetExtension
  • *Descriptor.TryGetOption

@jtattermusch jtattermusch self-assigned this Apr 22, 2020
/// Gets the value of the specified extension.
/// Unlike <see cref="IExtendableMessage{T}.GetExtension{TValue}(Extension{T, TValue})"/>, this doesn't return the default value for the extension if it's not set.
/// </summary>
bool TryGetExtension<TValue>(Extension<T, TValue> extension, out TValue value);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like the GetExtension method was a mistake from the very beginning, since it doesn't allow simple handling of the case where the extension is not present (which is a very common case in practice).

The problem seems to be that ExtensionSet.Get returns default(T) if the value is not found, which makes it impossible to distinguish the case where extension is not present at all vs where extension is present, but is set to the default value. (e.g for int, bool, float, etc..)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem seems to be that ExtensionSet.Get returns default(T) if the value is not found,

It returns the default value of the field which I believe matches up with get methods in other implementations.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you provide and example how this is handled e.g. in java?
How can I distinguish between the extension value not being present at all from extension value being present, but set to its default value? Unless I'm missing something, it really feels that the API should allow you to do that. (does e.g. java allow that?).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// in file scope
optional int32 bar = 99999 [default = 3];
// default
int value = msg.getExtension(FooFile.Bar); // 3

msg.setExtension(FooFile.Bar, 3);
if (msg.hasExtension(FooFile.Bar)) { // true
    value = msg.getExtension(FooFile.Bar); // 3
}

Custom options don't work the same way for us since java has builders and readers so they can just return the descriptor options reader and get the same API. We could add a HasOption API, but TryGet fixes the need for that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Ok so to me it looks like we basically have two ways of handling this:

  • introduce TryGetExtension method, at which point GetOption becomes pretty much useless (because it can return default value for an extension that isn't present, which seems semantically wrong), but the issue is we already have introduced GetExtension, so getting rid of it shortly after introducing is odd.

  • we can keep GetExtension (because it already exists) and introduce a new HasExtension method which allows checking whether the value is present or not. If GetExtension() is called for a value that is not present, we can throw / return the default value (what the is getExtension method's behavior in java?).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getExtension returns the default value.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I didn't notice that we already have a "HasExtension" method so my last comment is partially wrong.
Because we already have the HasExtension/GetExtension pair, I don't think we need TryGetExtension at all (why increase the API surface if all the necessary functionality is already there?).

In terms of options, I think both GetOption and TryGetOption are unnecessary and we should get rid of them. See my PR: #7434

/// <summary>
/// Gets a single value enum value option for this descriptor
/// </summary>
public bool TryGetOption<T>(Extension<EnumValueOptions, T> extension, out T value)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding this method is basically just duplicating the Extension API and we should avoid that.
See #7434 and let me know what you think.

@jtattermusch
Copy link
Contributor

Superseded by #7434 (and GetOption API is now obsolete).

@ObsidianMinor
Copy link
Contributor Author

Do you think we could still use the IExtendableMessage.TryGetExtension API even though we aren't adding *Descriptor.TryGetOption with it?

@jtattermusch
Copy link
Contributor

I think we should have either "GetExtension and HasExtension" or "TryGetExtension" model, not both. We should try to keep the API surface as small as possible.
We already have GetExtension and HasExtension, so I don't think we need TryGetExtension.
Besides that, adding methods to an existing interface is problematic (it's not backward compatible).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants