www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - const Propagation

reply "Julian Kranz" <kranzj in.tum.de> writes:
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
parent reply "Peter Alexander" <peter.alexander.au gmail.com> writes:
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
next sibling parent reply "Julian Kranz" <kranzj in.tum.de> writes:
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
next sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
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
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
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
parent reply "Julian Kranz" <kranzj in.tum.de> writes:
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
parent reply "Daniel Kozak" <kozzi11 gmail.com> writes:
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
parent reply "Julian Kranz" <kranzj in.tum.de> writes:
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:
 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
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...
Dec 29 2014
next sibling parent reply "Julian Kranz" <kranzj in.tum.de> writes:
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
parent "Julian Kranz" <kranzj in.tum.de> writes:
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
prev sibling next sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 12/29/14 10:36 AM, Julian Kranz wrote:
 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:
 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 {
 }
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).

 Did you try my solutions? It doesn`t need blah to be const
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...
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. -Steve
Dec 29 2014
parent reply "Julian Kranz" <kranzj in.tum.de> writes:
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.

 -Steve
Ah, thanks, this explains it ;-). However, it's kind of uncool that this only works for templates...
Dec 29 2014
next sibling parent reply "Daniel N" <ufo orbiting.us> writes:
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:
 The compiler can infer attributes if a function is a template. 
 Not all attributes, but some of them.

 -Steve
Ah, thanks, this explains it ;-). However, it's kind of uncool that this only works for templates...
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.html
Dec 29 2014
parent "Julian Kranz" <kranzj in.tum.de> writes:
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:
 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.

 -Steve
Ah, thanks, this explains it ;-). However, it's kind of uncool that this only works for templates...
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.html
Thank you, I should do that :-) Actually, I've read "The D Programming Language" so time ago...
Dec 29 2014
prev sibling parent reply "Vlad Levenfeld" <vlevenfeld gmail.com> writes:
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:
 The compiler can infer attributes if a function is a template. 
 Not all attributes, but some of them.

 -Steve
Ah, thanks, this explains it ;-). However, it's kind of uncool that this only works for templates...
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.
Dec 29 2014
parent reply "Julian Kranz" <kranzj in.tum.de> writes:
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:
 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.

 -Steve
Ah, thanks, this explains it ;-). However, it's kind of uncool that this only works for templates...
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.
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++...
Dec 29 2014
next sibling parent reply "evenex" <vlevenfeld gmail.com> writes:
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
parent ketmar via Digitalmars-d <digitalmars-d puremagic.com> writes:
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=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?
you get it right. ;-)
Dec 29 2014
prev sibling parent ketmar via Digitalmars-d <digitalmars-d puremagic.com> writes:
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
prev sibling parent reply ketmar via Digitalmars-d <digitalmars-d puremagic.com> writes:
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
parent reply "Julian Kranz" <kranzj in.tum.de> writes:
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...
Dec 29 2014
parent ketmar via Digitalmars-d <digitalmars-d puremagic.com> writes:
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
prev sibling parent reply "anonymous" <anonymous example.com> writes:
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
next sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
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
Dec 29 2014
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
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
Dec 29 2014
parent reply "Julian Kranz" <kranzj in.tum.de> writes:
On Monday, 29 December 2014 at 20:24:13 UTC, Steven Schveighoffer 
wrote:
 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
I'd really like to know that, too ;-). Thank you again for all your answers!
Jan 07 2015
parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 1/7/15 1:27 PM, Julian Kranz wrote:
 On Monday, 29 December 2014 at 20:24:13 UTC, Steven Schveighoffer 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?
I'd really like to know that, too ;-). Thank you again for all your answers!
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 -Stev
Jan 08 2015
prev sibling parent reply Matt Soucy <msoucy csh.rit.edu> writes:
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 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"?
 D offers "inout"; this actually aims into the right directing, but I g=
uess it does not help here.
 Is there any "static if"-something construct to check the const-ness o=
f an object?
=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
parent Steven Schveighoffer <schveiguy yahoo.com> writes:
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]
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
Dec 30 2014
prev sibling parent reply "Julian Kranz" <kranzj in.tum.de> writes:
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
parent reply "Daniel Kozak" <kozzi11 gmail.com> writes:
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
parent "anonymous" <anonymous example.com> writes:
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 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); }
So template methods deduce const now? Sweet. I see one wart on this: `inge.blah((h) {});` doesn't work.
Dec 29 2014