December 29, 2014 Re: const Propagation | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Julian Kranz Attachments: | On Mon, 29 Dec 2014 15:36:57 +0000
Julian Kranz via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
> Uuuhm, you're right, it works :-D I don't completely understand why the compiler does not require the function to be sonst any longer...
we must get our big red letters and write somewhere: "template is not XXX", where XXX is anything of function, method, struct, class, etc. ;-)
eponymous template syntax make people believe that `a()() {...}` is a
function, while it's not. this is template, and it has template magic in
in.
this is not your fault though. this is not a fault of anyone for that matter: it was designed to look like function, so it does. alas, we can't have it looking as a function and not confusing newcomers in the same time.
eventually you will start to easily recognize such "non-XXX" templates.
i daresay that D is all about templates, so you have no other
choice. ;-)
| |||
December 29, 2014 Re: const Propagation | ||||
|---|---|---|---|---|
| ||||
Posted in reply to ketmar | Thanks again for all answers :-).
On Monday, 29 December 2014 at 19:57:20 UTC, ketmar via Digitalmars-d wrote:
> On Mon, 29 Dec 2014 15:36:57 +0000
> Julian Kranz via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
>
>> Uuuhm, you're right, it works :-D I don't completely understand why the compiler does not require the function to be sonst any longer...
> we must get our big red letters and write somewhere: "template is not
> XXX", where XXX is anything of function, method, struct, class, etc. ;-)
>
> eponymous template syntax make people believe that `a()() {...}` is a
> function, while it's not. this is template, and it has template magic in
> in.
>
> this is not your fault though. this is not a fault of anyone for that
> matter: it was designed to look like function, so it does. alas, we
> can't have it looking as a function and not confusing newcomers in the
> same time.
>
> eventually you will start to easily recognize such "non-XXX" templates.
> i daresay that D is all about templates, so you have no other
> choice. ;-)
Well, of course you're right; but the thing is - does it really make sense to have a less powerful semantic for functions here? Does it help in any way? I mean, if something works just because you're using a template, it should maybe also work if you're not...
| |||
December 29, 2014 Re: const Propagation | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Julian Kranz | On Monday, 29 December 2014 at 16:48:46 UTC, Julian Kranz wrote:
> Is that really cool? I mean, is wise to have the compiler treat templates and non-templates differently? C++ has tons of such inconsistencies which is the main reason I don't really like C++...
Well, it is reasonable in light of the fact that templates require the source to be available (which guarantees the compiler can analyze it) while regular functions might not be (e.g. if they are in a precompiled library). In this sense, making a function into a zero-param template is equivalent to telling the compiler that it is free to analyze the source.
That being said, I sympathize with the sentiment - it would be more consistent if all functions whose source was available could be auto-annotated. I'm not sure what the technical impediments to this might be, though. Still, adding an extra () to the function signature is not too inconvenient, and carries some additional benefits.
I find annotations and qualifiers to be part of the "ugly" side of D, and try to avoid using them (took me awhile to figure out that C++ style const-correctness doesn't work in D, due to transitivity) but I'm afraid don't know enough about compilers to make a more informed judgement than "that's just how it is." Maybe someone with more experience in this area could weigh in?
| |||
December 29, 2014 Re: const Propagation | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Julian Kranz Attachments: | On Mon, 29 Dec 2014 16:48:45 +0000
Julian Kranz via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
> Is that really cool? I mean, is wise to have the compiler treat templates and non-templates differently? C++ has tons of such inconsistencies which is the main reason I don't really like C++...
ah. "templates are not functions!" ;-)
this is completely unpractical to forbid compiler doing attribute inference on templates: it's not always possible to manually attribute template. for example:
void a(T) (T n) {
...some code 1
static if (isThisSomething!T) {
...some code 2
}
...some code 3
}
'...some code 2' may be used only for specific types and it can be
non-safe, and '...some code 1' and '...some code 2' is always safe. yet
we can't mark the template with `@safe` here, we must copy-paste the
whole template code: once for `@system` variant and once for `@safe`
variant. this will effectively kill the template handyness.
as for functions... function attributes are the part of it's signature. see: `void a ();` is not the same function as `void a () @safe;`. they has different signatures and different name mangling. this can't be avoided, as linker has to link the correct implementation, and linker has no clue about D, it sees only mangled function names. so technically compiler can do attribute inference on functions, but practically it is impossible, as attributes depends of the call site, and compiler has to generate function signature BEFORE it is called.
that's why i'm emphasising "template is not XXX" sentense. template is a template, it's not a function. don't let it trick you! ;-)
| |||
December 29, 2014 Re: const Propagation | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Julian Kranz Attachments: | On Mon, 29 Dec 2014 20:01:59 +0000
Julian Kranz via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
> Well, of course you're right; but the thing is - does it really make sense to have a less powerful semantic for functions here? Does it help in any way? I mean, if something works just because you're using a template, it should maybe also work if you're not...
see my another reply. internally templates generates functions with different signatures, 'cause compiler must know the signature to tell the caller what exactly it is calling. ;-) templates was born to overcome this limitation. ;-)
| |||
December 29, 2014 Re: const Propagation | ||||
|---|---|---|---|---|
| ||||
Posted in reply to anonymous | On 12/29/14 2:07 PM, anonymous wrote:
> On Monday, 29 December 2014 at 13:20:39 UTC, Julian Kranz wrote:
>> Thank you for your answer. This kind of thing also works for C++, but
>> that would mean that I would implement the whole visitor twice - one
>> const and one non-const version. Is that really the only way? Can't I
>> tell the D compiler that "the argument of that lambda shares the
>> const-ness of the current object"?
>>
>> D offers "inout"; this actually aims into the right directing, but I
>> guess it does not help here.
>>
>> Is there any "static if"-something construct to check the const-ness
>> of an object?
>
> There's a pattern I suggested before[1] that I'd like to mention
> in addition to the template solutions Steven Schveighoffer and
> Daniel Kozak gave:
>
> Call the non-const overload from the const overload and cast
> accordingly.
>
> In your case:
>
> void blah(void function(Hugo h) f) {
> f(this);
> }
> void blah(void function(const Hugo h) f) const {
> (cast(Hugo) this).blah(cast(void function(Hugo)) f);
> }
The problem here is, you lose your compiler checks. It's not so much that "I know at this moment, mutable blah does not change anything", it's "I know at this moment, and anytime in the future, mutable blah does not change anything".
Hm... I did think of another solution, using delegates:
private void blahImpl(scope void delegate() f) const {
... // do things that don't change this
f();
... // do things that don't change this
}
void blah(void function(Hugo h) f) {
blahImpl((){f(this);});
}
And this time, I did test it, because I was curious enough :)
While you still need the boilerplate of repeating blah for const and others, you don't need to repeat the larger implementation. One thing you could do is make blah a template which takes Hugo as a template type, and then any function that accepts a Hugo (including a base class) would be usable, and it then allows you to avoid repetition:
void blah(T)(void function(T t) f) {
blahImpl((){f(this);});
}
except... drat, it doesn't compile, can't deduce T when it's const(Hugo) (that doesn't make much sense). I'll look and see if there is an open bug report.
-Steve
| |||
December 29, 2014 Re: const Propagation | ||||
|---|---|---|---|---|
| ||||
Posted in reply to evenex Attachments: | On Mon, 29 Dec 2014 20:07:08 +0000
evenex via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
> > Is that really cool? I mean, is wise to have the compiler treat templates and non-templates differently? C++ has tons of such inconsistencies which is the main reason I don't really like C++...
>
> Well, it is reasonable in light of the fact that templates
> require the source to be available (which guarantees the compiler
> can analyze it) while regular functions might not be (e.g. if
> they are in a precompiled library). In this sense, making a
> function into a zero-param template is equivalent to telling the
> compiler that it is free to analyze the source.
> That being said, I sympathize with the sentiment - it would be
> more consistent if all functions whose source was available could
> be auto-annotated. I'm not sure what the technical impediments to
> this might be, though. Still, adding an extra () to the function
> signature is not too inconvenient, and carries some additional
> benefits.
> I find annotations and qualifiers to be part of the "ugly" side
> of D, and try to avoid using them (took me awhile to figure out
> that C++ style const-correctness doesn't work in D, due to
> transitivity) but I'm afraid don't know enough about compilers to
> make a more informed judgement than "that's just how it is."
> Maybe someone with more experience in this area could weigh in?
you get it right. ;-)
| |||
December 29, 2014 Re: const Propagation | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On 12/29/14 3:11 PM, Steven Schveighoffer wrote:
> On 12/29/14 2:07 PM, anonymous wrote:
>> On Monday, 29 December 2014 at 13:20:39 UTC, Julian Kranz wrote:
>>> Thank you for your answer. This kind of thing also works for C++, but
>>> that would mean that I would implement the whole visitor twice - one
>>> const and one non-const version. Is that really the only way? Can't I
>>> tell the D compiler that "the argument of that lambda shares the
>>> const-ness of the current object"?
>>>
>>> D offers "inout"; this actually aims into the right directing, but I
>>> guess it does not help here.
>>>
>>> Is there any "static if"-something construct to check the const-ness
>>> of an object?
>>
>> There's a pattern I suggested before[1] that I'd like to mention
>> in addition to the template solutions Steven Schveighoffer and
>> Daniel Kozak gave:
>>
>> Call the non-const overload from the const overload and cast
>> accordingly.
>>
>> In your case:
>>
>> void blah(void function(Hugo h) f) {
>> f(this);
>> }
>> void blah(void function(const Hugo h) f) const {
>> (cast(Hugo) this).blah(cast(void function(Hugo)) f);
>> }
>
> The problem here is, you lose your compiler checks. It's not so much
> that "I know at this moment, mutable blah does not change anything",
> it's "I know at this moment, and anytime in the future, mutable blah
> does not change anything".
>
> Hm... I did think of another solution, using delegates:
>
> private void blahImpl(scope void delegate() f) const {
> ... // do things that don't change this
> f();
> ... // do things that don't change this
> }
>
> void blah(void function(Hugo h) f) {
> blahImpl((){f(this);});
> }
>
> And this time, I did test it, because I was curious enough :)
>
> While you still need the boilerplate of repeating blah for const and
> others, you don't need to repeat the larger implementation. One thing
> you could do is make blah a template which takes Hugo as a template
> type, and then any function that accepts a Hugo (including a base class)
> would be usable, and it then allows you to avoid repetition:
>
> void blah(T)(void function(T t) f) {
> blahImpl((){f(this);});
> }
>
> except... drat, it doesn't compile, can't deduce T when it's const(Hugo)
> (that doesn't make much sense). I'll look and see if there is an open
> bug report.
OK, it's not inferring the const on 'this'. It ONLY does this if you have a 'this' template parameter in the template list. This works:
void blah(T, this _)(void function(T t) f)
interesting, can someone explain this requirement?
-Steve
| |||
December 30, 2014 Re: const Propagation | ||||
|---|---|---|---|---|
| ||||
Posted in reply to anonymous Attachments:
| On 12/29/2014 02:07 PM, anonymous wrote: > On Monday, 29 December 2014 at 13:20:39 UTC, Julian Kranz wrote: >> Thank you for your answer. This kind of thing also works for C++, but that would mean that I would implement the whole visitor twice - one const and one non-const version. Is that really the only way? Can't I tell the D compiler that "the argument of that lambda shares the const-ness of the current object"? >> >> D offers "inout"; this actually aims into the right directing, but I guess it does not help here. >> >> Is there any "static if"-something construct to check the const-ness of an object? > > There's a pattern I suggested before[1] that I'd like to mention in addition to the template solutions Steven Schveighoffer and Daniel Kozak gave: > > Call the non-const overload from the const overload and cast accordingly. > > In your case: > > void blah(void function(Hugo h) f) { > f(this); > } > void blah(void function(const Hugo h) f) const { > (cast(Hugo) this).blah(cast(void function(Hugo)) f); > } > > This is safe as long as the non-const overload does not mutate the object when f doesn't. BUT you have to make sure of that yourself; the compiler can't help anymore. > > [1] http://stackoverflow.com/questions/22442031/how-to-make-a-template-function-const-if-the-template-is-true/22442425#22442425 Wait, is there a reason that you cast away const instead of adding it? I haven't done too much with D const lately, but in C++ I know that I would have the non-const version call the const version instead - seems a bit safer, and the compiler can check it a bit beter. -- Matt Soucy http://msoucy.me/ | |||
December 30, 2014 Re: const Propagation | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Matt Soucy | On 12/29/14 9:12 PM, Matt Soucy wrote:
> On 12/29/2014 02:07 PM, anonymous wrote:
>> On Monday, 29 December 2014 at 13:20:39 UTC, Julian Kranz wrote:
>>> Thank you for your answer. This kind of thing also works for C++, but that would mean that I would implement the whole visitor twice - one const and one non-const version. Is that really the only way? Can't I tell the D compiler that "the argument of that lambda shares the const-ness of the current object"?
>>>
>>> D offers "inout"; this actually aims into the right directing, but I guess it does not help here.
>>>
>>> Is there any "static if"-something construct to check the const-ness of an object?
>>
>> There's a pattern I suggested before[1] that I'd like to mention
>> in addition to the template solutions Steven Schveighoffer and
>> Daniel Kozak gave:
>>
>> Call the non-const overload from the const overload and cast
>> accordingly.
>>
>> In your case:
>>
>> void blah(void function(Hugo h) f) {
>> f(this);
>> }
>> void blah(void function(const Hugo h) f) const {
>> (cast(Hugo) this).blah(cast(void function(Hugo)) f);
>> }
>>
>> This is safe as long as the non-const overload does not mutate
>> the object when f doesn't. BUT you have to make sure of that
>> yourself; the compiler can't help anymore.
>>
>> [1]
>> http://stackoverflow.com/questions/22442031/how-to-make-a-template-function-const-if-the-template-is-true/22442425#22442425
> Wait, is there a reason that you cast away const instead of adding it? I haven't done too much with D const lately, but in C++ I know that I would have the non-const version call the const version instead - seems a bit safer, and the compiler can check it a bit beter.
>
It would still require a cast. Remember the function pointer passed to non-const blah requires a non-const Hugo. So you would have to cast at least the function pointer to void function(const(Hugo)). This doesn't sit as well as casting the other way (you can always call a function that takes a const Hugo with a non-const Hugo), although practically speaking, there isn't any damage done. However, the call of f inside the const blah function would violate const. If f were marked as pure, this could be actually damaging.
Either solution looks ugly and error-prone to me. I like the template solution the best, either as a template this member or a UFCS template function call. The delegate solution I posted earlier is neat, but seems too inelegant.
-Steve
| |||
Copyright © 1999-2021 by the D Language Foundation
Permalink
Reply