digitalmars.D - const Propagation
- Julian Kranz (37/37) Dec 29 2014 Hi all,
- Peter Alexander (27/27) Dec 29 2014 You need to overload on const, and also pass in a correctly typed
- Julian Kranz (9/9) Dec 29 2014 Thank you for your answer. This kind of thing also works for C++,
- Steven Schveighoffer (15/24) Dec 29 2014 inout is not smart enough to know what the inout on a lambda means. It
- Steven Schveighoffer (6/10) Dec 29 2014 um...
- Julian Kranz (18/18) Dec 29 2014 Thank you for your answers. All of your suggestions go into the
- Daniel Kozak (2/9) Dec 29 2014 Did you try my solutions? It doesn`t need blah to be const
- Julian Kranz (4/15) Dec 29 2014 Uuuhm, you're right, it works :-D I don't completely understand
- Julian Kranz (19/19) Dec 29 2014 No, actually I don't at all understand why the function does not
- Julian Kranz (17/17) Dec 29 2014 Sorry, copy+paste error, I of course meant the following :-/...
- Steven Schveighoffer (8/22) Dec 29 2014 Not in my solution, because blah is not a "method". T will become const
- Julian Kranz (4/7) Dec 29 2014 Ah, thanks, this explains it ;-). However, it's kind of uncool
- Daniel N (6/14) Dec 29 2014 If you wish to dig into the details, there's a free ebook with a
- Julian Kranz (3/18) Dec 29 2014 Thank you, I should do that :-) Actually, I've read "The D
- Vlad Levenfeld (11/19) Dec 29 2014 I've gotten into the habit of templating every function, even if
- Julian Kranz (5/25) Dec 29 2014 Is that really cool? I mean, is wise to have the compiler treat
- evenex (19/23) Dec 29 2014 Well, it is reasonable in light of the fact that templates
- ketmar via Digitalmars-d (3/26) Dec 29 2014 you get it right. ;-)
- ketmar via Digitalmars-d (28/32) Dec 29 2014 ah. "templates are not functions!" ;-)
- ketmar via Digitalmars-d (14/17) Dec 29 2014 we must get our big red letters and write somewhere: "template is not
- Julian Kranz (8/34) Dec 29 2014 Thanks again for all answers :-).
- ketmar via Digitalmars-d (6/11) Dec 29 2014 see my another reply. internally templates generates functions with
- anonymous (18/28) Dec 29 2014 There's a pattern I suggested before[1] that I'd like to mention
- Steven Schveighoffer (27/51) Dec 29 2014 The problem here is, you lose your compiler checks. It's not so much
- Steven Schveighoffer (6/60) Dec 29 2014 OK, it's not inferring the const on 'this'. It ONLY does this if you
- Julian Kranz (4/90) Jan 07 2015 I'd really like to know that, too ;-). Thank you again for all
- Steven Schveighoffer (12/22) Jan 08 2015 Yeah, I think this is a missed opportunity.
- Matt Soucy (15/44) Dec 29 2014 that would mean that I would implement the whole visitor twice - one con...
- Steven Schveighoffer (14/45) Dec 30 2014 It would still require a cast. Remember the function pointer passed to
- Julian Kranz (3/3) Dec 29 2014 So I hope you understand; I've got no problem with changing the
- Daniel Kozak (22/25) Dec 29 2014 So you can write something like this:
- anonymous (3/28) Dec 29 2014 So template methods deduce const now? Sweet.
Hi all, I've got a little problem regarding const. In fact, my problem originates from C++ where I've tried to implement a visitor that deals with both const and non-const objects. As this did not work out, I checked whether it would work in D. Unfortunately, I could not figure out how. Maybe someone can help. Basically, I would like to propagate constness to a lambda function. Here's the example code: import std.stdio; class Hugo { public int x = 42; void blah(void function(Hugo h) f) { f(this); } } void main() { Hugo hugo = new Hugo(); void function(Hugo h) f = function(Hugo h) { h.x = 99; }; hugo.blah(f); const Hugo inge = hugo; void function(Hugo h) g = function(Hugo h) { writeln("foobar"); }; inge.blah(g); } This does not compile. The D compiler complains that I must not call a mutable method on a const object (inge). However, if I make the method const, I have to also make the parameter of the lambda const which results in the compiler rejecting the call "hugo.blah(f)" since this lambda actually modifies the object. I hope you get what I want. I want to be able to call blah() on a const instance of Hugo with a lambda that does not modify its argument and otherwise call blah() with any function, even one that modifies the object. Is that possible? Thank you very much!
Dec 29 2014
You need to overload on const, and also pass in a correctly typed function as the argument (you can't call a function with a mutable parameter with a const object. import std.stdio; class Hugo { public int x = 42; void blah(void function(Hugo h) f) { f(this); } // OVERLOAD void blah(void function(const Hugo h) f) const { f(this); } } void main() { Hugo hugo = new Hugo(); void function(Hugo h) f = function(Hugo h) { h.x = 99; }; hugo.blah(f); const Hugo inge = hugo; // CHANGE TYPE HERE void function(const Hugo h) g = function(const Hugo h) { writeln("foobar"); }; inge.blah(g); }
Dec 29 2014
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?
Dec 29 2014
On 12/29/14 8:20 AM, 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.inout is not smart enough to know what the inout on a lambda means. It also can be confusing to understand that during the entire execution of an inout function, the inout parameters are treated as const. This would preclude using inout to do what you want.Is there any "static if"-something construct to check the const-ness of an object?Static-if applies to generic coding, i.e. templates. A template function may work, but I don't think you even need static if: class Hugo { ... } void blah(T)(T obj, void function(T t) f) if(T : Hugo) { f(obj);} This should work with all iterations of Hugo, and is callable via UFCS: hugo.blah(f); // should work I don't know if a template member function would be possible, as I'm pretty sure templates do not infer const. (admittedly, I have not tested any of this) -Steve
Dec 29 2014
On 12/29/14 9:18 AM, Steven Schveighoffer wrote:Static-if applies to generic coding, i.e. templates. A template function may work, but I don't think you even need static if: class Hugo { ... } void blah(T)(T obj, void function(T t) f) if(T : Hugo) { f(obj);}um... blah(T : Hugo)(T obj, void function(T t) f) { f(obj); } Maybe this will work better, but still untested :) You may need T : const(Hugo). -Steve
Dec 29 2014
Thank you for your answers. All of your suggestions go into the right direction, however there's still one thing left that breakes it: the method itself (blah()) needs to be marked as const to be callable on a const object. Therefore, I need something like void blah(...)(...) if(this ia const object) const : nothing { } I would also like to discuss the underlying problem. Even if we end up finding some syntactic monster to deal with this, there obviously is no sneaky syntax for it. Am I wrong thinking that is some very basic type checking problem? I mean, D already implements const transitively. Thus, if I cast some object to its const version, all its members magically turn to be const as well. Wouldn't it be natural to apply this here as well? This way, I would not even need to declare the lambda's parameter as const or anything; the compiler would only fail if I actually pass in something of which the compiler is unsure whether it changes the passed-in object or not...
Dec 29 2014
On Monday, 29 December 2014 at 15:17:30 UTC, Julian Kranz wrote:Thank you for your answers. All of your suggestions go into the right direction, however there's still one thing left that breakes it: the method itself (blah()) needs to be marked as const to be callable on a const object. Therefore, I need something like void blah(...)(...) if(this ia const object) const : nothing { }Did you try my solutions? It doesn`t need blah to be const
Dec 29 2014
On Monday, 29 December 2014 at 15:25:13 UTC, Daniel Kozak wrote:On Monday, 29 December 2014 at 15:17:30 UTC, Julian Kranz 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...Thank you for your answers. All of your suggestions go into the right direction, however there's still one thing left that breakes it: the method itself (blah()) needs to be marked as const to be callable on a const object. Therefore, I need something like void blah(...)(...) if(this ia const object) const : nothing { }Did you try my solutions? It doesn`t need blah to be const
Dec 29 2014
No, actually I don't at all understand why the function does not need to be const any longer since this code does not compile: import std.stdio; class Hugo { public int x = 42; void blah(this T)(void function(const Hugo h) f) { f(this); } } void main() { Hugo hugo = new Hugo(); const Hugo inge = hugo; void function(const Hugo h) g = function(const Hugo h) { write("foobar "); writeln(h.x); }; inge.blah(g); } Sorry guys, I know I'm a bloody D beginner, ...
Dec 29 2014
Sorry, copy+paste error, I of course meant the following :-/... import std.stdio; class Hugo { public int x = 42; void blah(void function(const Hugo h) f) { f(this); } } void main() { Hugo hugo = new Hugo(); const Hugo inge = hugo; void function(const Hugo h) g = function(const Hugo h) { write("foobar "); writeln(h.x); }; inge.blah(g); }
Dec 29 2014
On 12/29/14 10:36 AM, Julian Kranz wrote:On Monday, 29 December 2014 at 15:25:13 UTC, Daniel Kozak wrote:Not in my solution, because blah is not a "method". T will become const if the callee is const. As I said, you may have to do T : const(Hugo).On Monday, 29 December 2014 at 15:17:30 UTC, Julian Kranz wrote:Thank you for your answers. All of your suggestions go into the right direction, however there's still one thing left that breakes it: the method itself (blah()) needs to be marked as const to be callable on a const object. Therefore, I need something like void blah(...)(...) if(this ia const object) const : nothing { }I think Daniel's solution would work if the compiler infers const. I'm not sure that it does, but if it works, it does :) The compiler can infer attributes if a function is a template. Not all attributes, but some of them. -SteveUuuhm, you're right, it works :-D I don't completely understand why the compiler does not require the function to be sonst any longer...Did you try my solutions? It doesn`t need blah to be const
Dec 29 2014
On Monday, 29 December 2014 at 15:53:25 UTC, Steven Schveighoffer wrote:The compiler can infer attributes if a function is a template. Not all attributes, but some of them. -SteveAh, thanks, this explains it ;-). However, it's kind of uncool that this only works for templates...
Dec 29 2014
On Monday, 29 December 2014 at 16:03:41 UTC, Julian Kranz wrote:On Monday, 29 December 2014 at 15:53:25 UTC, Steven Schveighoffer wrote:If you wish to dig into the details, there's a free ebook with a good explanation here: http://ddili.org/ders/d.en/index.html Search for "this template parameters for member functions" in http://ddili.org/ders/d.en/templates_more.htmlThe compiler can infer attributes if a function is a template. Not all attributes, but some of them. -SteveAh, thanks, this explains it ;-). However, it's kind of uncool that this only works for templates...
Dec 29 2014
On Monday, 29 December 2014 at 16:09:33 UTC, Daniel N wrote:On Monday, 29 December 2014 at 16:03:41 UTC, Julian Kranz wrote:Thank you, I should do that :-) Actually, I've read "The D Programming Language" so time ago...On Monday, 29 December 2014 at 15:53:25 UTC, Steven Schveighoffer wrote:If you wish to dig into the details, there's a free ebook with a good explanation here: http://ddili.org/ders/d.en/index.html Search for "this template parameters for member functions" in http://ddili.org/ders/d.en/templates_more.htmlThe compiler can infer attributes if a function is a template. Not all attributes, but some of them. -SteveAh, thanks, this explains it ;-). However, it's kind of uncool that this only works for templates...
Dec 29 2014
On Monday, 29 December 2014 at 16:03:41 UTC, Julian Kranz wrote:On Monday, 29 December 2014 at 15:53:25 UTC, Steven Schveighoffer wrote:I've gotten into the habit of templating every function, even if its a zero-parameter template, because trying to manage all the pure/safe/const/nothrow annotations myself always winds up putting me in a corner once the code starts to grow. It's not so bad, really, one advantage is that you can define functions that may or may not compile, but will only trigger an error if they are called on a type that doesn't support it. You can issue compile time branches using "static if (is (typeof(function_name)))" to gain an extra degree of control over this process.The compiler can infer attributes if a function is a template. Not all attributes, but some of them. -SteveAh, thanks, this explains it ;-). However, it's kind of uncool that this only works for templates...
Dec 29 2014
On Monday, 29 December 2014 at 16:13:03 UTC, Vlad Levenfeld wrote:On Monday, 29 December 2014 at 16:03:41 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++...On Monday, 29 December 2014 at 15:53:25 UTC, Steven Schveighoffer wrote:I've gotten into the habit of templating every function, even if its a zero-parameter template, because trying to manage all the pure/safe/const/nothrow annotations myself always winds up putting me in a corner once the code starts to grow. It's not so bad, really, one advantage is that you can define functions that may or may not compile, but will only trigger an error if they are called on a type that doesn't support it. You can issue compile time branches using "static if (is (typeof(function_name)))" to gain an extra degree of control over this process.The compiler can infer attributes if a function is a template. Not all attributes, but some of them. -SteveAh, thanks, this explains it ;-). However, it's kind of uncool that this only works for templates...
Dec 29 2014
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?
Dec 29 2014
On Mon, 29 Dec 2014 20:07:08 +0000 evenex via Digitalmars-d <digitalmars-d puremagic.com> wrote:you get it right. ;-)Is that really cool? I mean, is wise to have the compiler treat=20 templates and non-templates differently? C++ has tons of such=20 inconsistencies which is the main reason I don't really like=20 C++...=20 Well, it is reasonable in light of the fact that templates=20 require the source to be available (which guarantees the compiler=20 can analyze it) while regular functions might not be (e.g. if=20 they are in a precompiled library). In this sense, making a=20 function into a zero-param template is equivalent to telling the=20 compiler that it is free to analyze the source. That being said, I sympathize with the sentiment - it would be=20 more consistent if all functions whose source was available could=20 be auto-annotated. I'm not sure what the technical impediments to=20 this might be, though. Still, adding an extra () to the function=20 signature is not too inconvenient, and carries some additional=20 benefits. I find annotations and qualifiers to be part of the "ugly" side=20 of D, and try to avoid using them (took me awhile to figure out=20 that C++ style const-correctness doesn't work in D, due to=20 transitivity) but I'm afraid don't know enough about compilers to=20 make a more informed judgement than "that's just how it is."=20 Maybe someone with more experience in this area could weigh in?
Dec 29 2014
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=20 templates and non-templates differently? C++ has tons of such=20 inconsistencies which is the main reason I don't really like=20 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! ;-)
Dec 29 2014
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=20 why the compiler does not require the function to be sonst any=20 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. ;-)
Dec 29 2014
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: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...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. ;-)
Dec 29 2014
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=20 make sense to have a less powerful semantic for functions here?=20 Does it help in any way? I mean, if something works just because=20 you're using a template, it should maybe also work if you're=20 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. ;-)
Dec 29 2014
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]
Dec 29 2014
On 12/29/14 2:07 PM, anonymous wrote:On Monday, 29 December 2014 at 13:20:39 UTC, Julian Kranz wrote: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. -SteveThank 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); }
Dec 29 2014
On 12/29/14 3:11 PM, Steven Schveighoffer wrote:On 12/29/14 2:07 PM, anonymous wrote: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? -SteveOn Monday, 29 December 2014 at 13:20:39 UTC, Julian Kranz wrote: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.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); }
Dec 29 2014
On Monday, 29 December 2014 at 20:24:13 UTC, Steven Schveighoffer wrote:On 12/29/14 3:11 PM, Steven Schveighoffer wrote:I'd really like to know that, too ;-). Thank you again for all your answers!On 12/29/14 2:07 PM, anonymous wrote: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? -SteveOn Monday, 29 December 2014 at 13:20:39 UTC, Julian Kranz wrote: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.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); }
Jan 07 2015
On 1/7/15 1:27 PM, Julian Kranz wrote:On Monday, 29 December 2014 at 20:24:13 UTC, Steven Schveighoffer wrote:Yeah, I think this is a missed opportunity. 1. A template is (most of the time) automatically not virtual. 2. If a template can be inferred const or inout, it can be called with more instances for free. 3. In some cases, when the template parameters itself may determine the ability of the function to be const or not, it's not possible to slap a "const" on there, or split it up. I notice there are already a couple of reports about this: https://issues.dlang.org/show_bug.cgi?id=7521 https://issues.dlang.org/show_bug.cgi?id=8407 -StevOK, 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?I'd really like to know that, too ;-). Thank you again for all your answers!
Jan 08 2015
On 12/29/2014 02:07 PM, anonymous wrote:On Monday, 29 December 2014 at 13:20:39 UTC, Julian Kranz wrote:that would mean that I would implement the whole visitor twice - one cons= t and one non-const version. Is that really the only way? Can't I tell th= e D compiler that "the argument of that lambda shares the const-ness of t= he current object"?Thank you for your answer. This kind of thing also works for C++, but =uess it does not help here.D offers "inout"; this actually aims into the right directing, but I g=f an object?Is there any "static if"-something construct to check the const-ness o==20 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: =20 Call the non-const overload from the const overload and cast accordingly. =20 In your case: =20 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); } =20 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. =20 [1] http://stackoverflow.com/questions/22442031/how-to-make-a-template-func=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. --=20 Matt Soucy http://msoucy.me/
Dec 29 2014
On 12/29/14 9:12 PM, Matt Soucy wrote:On 12/29/2014 02:07 PM, anonymous wrote: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. -SteveOn Monday, 29 December 2014 at 13:20:39 UTC, Julian Kranz wrote: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.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]
Dec 30 2014
So I hope you understand; I've got no problem with changing the type of the function g as you supposed. The bad thing is the additional overload that results in duplicated code.
Dec 29 2014
On Monday, 29 December 2014 at 13:22:41 UTC, Julian Kranz wrote:So I hope you understand; I've got no problem with changing the type of the function g as you supposed. The bad thing is the additional overload that results in duplicated code.So you can write something like this: import std.stdio; class Hugo { public int x = 42; void blah(this T)(void function(T h) f) { f(this); } } void main() { Hugo hugo = new Hugo(); void function(Hugo h) f = function(Hugo h) { h.x = 99; }; hugo.blah(f); const Hugo inge = hugo; void function(const Hugo h) g = function(const Hugo h) { writeln("foobar"); }; inge.blah(g); }
Dec 29 2014
On Monday, 29 December 2014 at 14:13:20 UTC, Daniel Kozak wrote:On Monday, 29 December 2014 at 13:22:41 UTC, Julian Kranz wrote:So template methods deduce const now? Sweet. I see one wart on this: `inge.blah((h) {});` doesn't work.So I hope you understand; I've got no problem with changing the type of the function g as you supposed. The bad thing is the additional overload that results in duplicated code.So you can write something like this: import std.stdio; class Hugo { public int x = 42; void blah(this T)(void function(T h) f) { f(this); } } void main() { Hugo hugo = new Hugo(); void function(Hugo h) f = function(Hugo h) { h.x = 99; }; hugo.blah(f); const Hugo inge = hugo; void function(const Hugo h) g = function(const Hugo h) { writeln("foobar"); }; inge.blah(g); }
Dec 29 2014