Thread overview | |||||||||
---|---|---|---|---|---|---|---|---|---|
|
August 27, 2013 Prevention of UFCS hijacking | ||||
---|---|---|---|---|
| ||||
I've run into a bit of an issue with UFCS today, take a look at the reduced test-case: ----- import std.conv; class Label { // @property string text() { } // @property void text(string nText) { } } void main() { auto label = new Label; label.text = "My label"; assert(label.text == "My label"); // fails at runtime } ----- Because of the UFCS feature std.conv.text is called even though I expected compilation to fail since I've commented out the .text properties in my class. This is a problem since some of my classes might have a .text field, while others might not. If the user imports std.conv he could easily end up calling std.conv.text by mistake when a class object doesn't implement such a property. Anyway, there is a cure. I can work around this using @disabled fields in the base class. For example: ----- import std.conv; class Widget { @disable string text; } class Label : Widget { @property string text() { return "My label"; } @property void text(string nText) { } } class Image : Widget { } void main() { auto frame = new Label; frame.text = "My label"; assert(frame.text == "My label"); auto image = new Image; image.text = "foobar"; // ok, fails to compile } ----- Note that I've specifically made the base class declare a public variable rather than a @property, otherwise the derived class would have to override a virtual @property function. Maybe this code might even become a case of accepts-invalid at some point, since the properties do actually shadow the base class variable. I'm not sure how long this workaround will work. Has anyone else run into this sort of issue? I'm wondering whether we should have some language support (through __traits, @disable, or something else), to enable writing APIs which are safer to use, where you can't by mistake call a UFCS function like that if there's an equally named function introduced in another derived class. --- Btw, why exactly is the following allowed to compile?: ----- import std.conv; import std.stdio; struct S { } void main() { S s; writeln(s.text = "foo"); } ----- This translates into std.conv.text(s, "foo"). I'm not a fan, especially since 'text' is not a @property. But we've already reached the consensus that all functions can be called like properties (for some reason..), so I guess it's too late to change this. |
August 27, 2013 Re: Prevention of UFCS hijacking | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrej Mitrovic | On Tuesday, 27 August 2013 at 18:02:27 UTC, Andrej Mitrovic wrote: > I've run into a bit of an issue with UFCS today, take a look at the > reduced test-case: > > ----- > import std.conv; > > class Label > { > // @property string text() { } > // @property void text(string nText) { } > } > > void main() > { > auto label = new Label; > > label.text = "My label"; > assert(label.text == "My label"); // fails at runtime > } > ----- > > Because of the UFCS feature std.conv.text is called even though I > expected compilation to fail since I've commented out the .text > properties in my class. But commenting out "text" property is not order to compiler to ignore all UFCS involving "text". What commenting out has to do with UFCS? > This is a problem since some of my classes might have a .text field, > while others might not. If the user imports std.conv he could easily > end up calling std.conv.text by mistake when a class object doesn't > implement such a property. Then classes should implement member to disable UFCS hijacktion. If not, this is an allowance for such name hijacktion. This is by design. > Has anyone else run into this sort of issue? I'm wondering whether we > should have some language support (through __traits, @disable, or > something else), to enable writing APIs which are safer to use, where > you can't by mistake call a UFCS function like that if there's an > equally named function introduced in another derived class. @disable is a way to block this feature > --- > > Btw, why exactly is the following allowed to compile?: > > ----- > import std.conv; > import std.stdio; > > struct S { } > > void main() > { > S s; > writeln(s.text = "foo"); > } > ----- > > This translates into std.conv.text(s, "foo"). I'm not a fan, > especially since 'text' is not a @property. But we've already reached > the consensus that all functions can be called like properties (for > some reason..), so I guess it's too late to change this. Let's start from basics: writeln = 42; and this is also by design. |
August 27, 2013 Re: Prevention of UFCS hijacking | ||||
---|---|---|---|---|
| ||||
Posted in reply to Maxim Fomin | On 8/27/13, Maxim Fomin <maxim@maxim-fomin.ru> wrote:
> Let's start from basics
You've just repeated everything I've already said.
|
August 27, 2013 Re: Prevention of UFCS hijacking | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrej Mitrovic | On Tuesday, 27 August 2013 at 19:39:34 UTC, Andrej Mitrovic wrote:
> On 8/27/13, Maxim Fomin <maxim@maxim-fomin.ru> wrote:
>> Let's start from basics
>
> You've just repeated everything I've already said.
What should I tell except you are surprising of current state of things?
|
August 28, 2013 Re: Prevention of UFCS hijacking | ||||
---|---|---|---|---|
| ||||
Posted in reply to Maxim Fomin | On 2013-08-27 20:40, Maxim Fomin wrote: > Let's start from basics: > > writeln = 42; > > and this is also by design. I see no reasons for why this should be allowed. I say: * Getters marked with @property: call without parentheses * Getters not marked with @property: call with or without parentheses * Setters marked with @property: require assignment syntax * Setters not marked with @property: disallow assignment syntax -- /Jacob Carlborg |
August 28, 2013 Re: Prevention of UFCS hijacking | ||||
---|---|---|---|---|
| ||||
Posted in reply to Maxim Fomin | On 8/27/13, Maxim Fomin <maxim@maxim-fomin.ru> wrote:
> What should I tell except you are surprising of current state of things?
I'm just raising awareness of the issue. And it's surprising when you're coding and not expecting it.
Imagine adding an import to a 3rd party library and no longer getting errors at compile time when you assign to a field that doesn't exist. Anyone could be caught off-guard by this.
|
August 28, 2013 Re: Prevention of UFCS hijacking | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jacob Carlborg | On Wed, Aug 28, 2013 at 08:41:39AM +0200, Jacob Carlborg wrote: > On 2013-08-27 20:40, Maxim Fomin wrote: > > >Let's start from basics: > > > >writeln = 42; > > > >and this is also by design. > > I see no reasons for why this should be allowed. I say: > > * Getters marked with @property: call without parentheses > * Getters not marked with @property: call with or without parentheses > * Setters marked with @property: require assignment syntax > * Setters not marked with @property: disallow assignment syntax [...] +1, I agree with all of the above. T -- Старый друг лучше новых двух. |
Copyright © 1999-2021 by the D Language Foundation