auto, var, raii,scope, banana

Chad J gamerChad at _spamIsBad_gmail.com
Wed Aug 2 16:09:57 PDT 2006


Regan Heath wrote:
> On Fri, 28 Jul 2006 04:44:44 -0400, Chad J  
> <gamerChad at _spamIsBad_gmail.com> wrote:
> 
>> Regan Heath wrote:
>>
>>>  To expand on why I think it's intuitive let look at 2 scenarios, 
>>> first  the  C++ programmer trying D. Likely their first program 
>>> involving  classes will  look like this:
>>>  class A {
>>>   int a;
>>> }
>>>  void main() {
>>>   A a = A();
>>>   writefln(a.a);
>>> }
>>>  currently, this crashes with an access violation.
>>
>>
>> Actually, it doesn't compile.
>>
>> main.d(6): function expected before (), not A of type main.A
>> main.d(6): cannot implicitly convert expression ((A)()) of type int to
>> main.A
>>
>> Maybe not the best error messages for this case, but better than an
>> access violation.
> 
> 
> Err.. oops.. my example was in fact assuming the new "omit 'new'" 
> syntax  was added, then I went and confused it with:
> 
> A a;
> writefln(a.a);
> 
> which is actually what a C++ programmer would write to allocate a class 
> on  the stack and have it destroyed at end of scope.
> 
> The syntax:
> 
> A a;
> writefln(a.a);
> 
> will crash, the new syntax isn't invloved at all, sorry.
> 
>>> Add the new RAII syntax  and not only does it not crash, but it 
>>> behaves  just like a C++ program  would with A being destroyed at the 
>>> end of  scope.
>>
>>
>> I hear that such syntax, in C++, means that 'A' will be stack allocated,
>> which AFAIK does not imply RAII.
> 
> 
> No, but the resulting behaviour is almost identical. In C++ the syntax:
> 
> A a;
> 
> causes 'a' to be allocated on the stack and the destructor called at 
> end  of scope. Apart from being allocated on the stack (which Walter 
> indicates  in the docs is something he plans to implement at a later 
> date for 'auto')  it's identical to the behaviour of D's 'auto'.
> 
> http://www.digitalmars.com/d/memory.html#stackclass
> 
> "To have a class allocated on the stack that has a destructor, this is 
> the  same as a declaration with the auto attribute. Although the 
> current  implementation does not put such objects on the stack, future 
> ones can."
> 
>> One example I can think of that would
>> break RAII is if the function gets inlined, then the stack allocated
>> object may not be deleted until some point outside of the scope's end.
>> It will /probably/ be deleted at the end of the function that the
>> enclosing function was inlined into.  I could be wrong about that though.
> 
> 
> I'm not sure what happens when something is inlined either.
> 
>> Also, does that C++ syntax allow class references to leak into code that
>> is executed after the class is deleted?
> 
> 
> I would expect not. They should be destroyed when the scope ends and 
> the  stack space is released.
> 
>>>  Now, take a Java/C# programmer, their first program with classes 
>>> will  be:
>>>  class A {
>>>   int a;
>>> }
>>>  void main() {
>>>   A a = new A();
>>>   writefln(a.a);
>>> }
>>>  No crash, and the behaviour they expect.
>>>  Then imagine the same programmers looking at each others code, the   
>>> C++ programmer will understand 'new' to indicate heap allocation and  
>>> the  Java/C# programmer will need to ask the C++ programmer what the  
>>> absense of  'new' means, he'll reply that the class is destroyed at 
>>> the  end of scope.  He might also say it's stack allocated, but 
>>> that's fine,  both of them have  something to learn, and it has no 
>>> effect on the way  the program behaves.
>>
>>
>> That learning process would be nice, but it assumes an extra programmer
>> being around.  As I learn D, I am alone.  Either I go to the spec or I
>> ask on the newsgroup.  I'm not sure how many others are in the same
>> situation.
> 
> 
> In the case where you're not looking at someone elses code you'll never  
> write:
> 
> A a = A();
> 
> so you will not learn about it until you go looking for RAII. There 
> you'll  find the syntax, learn it, and carry on. It's no harder to learn 
> than a  keyword 'local'.
> 

When reading other people's code, I find that most often I don't know or 
work with the other people.  I'd have to contact them to learn from 
them.  The easier option, for me, and probably them too, is to do a 
search in the spec, even if that is a bit difficult as-is.

>> I've explained below why I still don't think it's very obvious, which
>> means more potential newsgroup asking.  That doesn't bug me too much
>> though.
> 
> 
> I'm not especially bothered which syntax is chosen, I just like 
> discussing  stuff.
> 
>>>  Simple, intuitive and elegant no matter what your background. Of  
>>> course,  if you're a Java/C# programmer you have to 'learn' what the  
>>> absense of  'new' means. Both have to learn that D might not stack  
>>> allocate the RAII  class instance.
>>>  If we use a 'scope' keyword then both sets of programmers have 
>>> some   learning to do. It's not much worse, but this makes it my 2nd 
>>> choice.
>>>
>>
>> "Both have to learn that D might not stack allocate the RAII  class
>> instance."
>> "If we use a 'scope' keyword then both sets of programmers have some
>> learning to do."
>>
>> If they both have learning to do anyways, why not just have them learn
>> 'scope'?
> 
> 
> Right back at ya.. why not have them both learn to omit 'new'?
> 

Point being it doesn't give you the win-lose, you're still stuck with a 
lose-lose.  If both coders have to learn, then we lose the advantage of 
chosing this syntax to make it so C++ coders don't have to learn.  You 
still have a point wrt similarity, though I dislike subtly different 
behaviours as it can lead to bad assumptions.

>> I suppose it wouldn't be too bad otherwise, except for one problem:
>> The unfortunate C++ programmer's program worked as expected.
> 
> 
> Actually, my example was wrong and it doesn't. This poor guy still gets 
> an  access violation. Perhaps the new 'auto' syntax should just be:
> 
> A a;
> 
> then, my previous example and point would be valid.
> 
>> I say problem and unfortunate, because they may just assume that D
>> behaves like C++ in all of the corner cases, and if I'm correct about
>> the RAII vs stack alloc above, it may not.
> 
> 
> One of the things Walter tends to do (which bothers some D users) is to  
> emulate C/C++ where appropriate. This makes porting C/C++ to D easier  
> because there aren't corner cases where it does something subtley  
> different. Of course I may be wrong, there may be a corner case, but I  
> believe Walter intentionally tries to reduce these.
> 
> In our case, I believe the D syntax:
> 
> auto A a = new A();
> 
> and the C++ syntax:
> 
> A a;
> 
> behave almost identically. The difference being that D current does not  
> allocate on the stack, but, that may change and if it does they will  
> behave identically.
> 

OK I'd like some clarification about the C++ side of things.
It seems to me that both syntaxes such as

A a = A();

and

A a;

have been said to cause stack allocation in C++.  Is this true?


A a;
Currently that is the syntax for declaring a variable.  It will break 
any code that relies on 'a' being initialized to null.
If that's alright, you could change it to mean RAII/stackalloc in the 
context of a function, but then you lose consistancy with D's practice 
of initializing variables to knownly invalid values.

>>>> For a D newbie reading code, having a keyword is good because it  
>>>> gives  them a keyword to search the spec for. Better yet if it is  
>>>> 'scope',  because that gives them an idea of where to look.  This 
>>>> is  also one of  those things that is used seldom enough that the  
>>>> succinctness gained  from implicit things is only slightly helpful.
>>>
>>>   The C++ programmer will already know what it does.
>>> The Java/C++ programmer will search for 'new' (because it's absent).
>>> In either case they'll find the docs on RAII at the same time as 
>>> they  find  the docs on 'new'.
>>
>>
>> Searching for 'new' is a good idea.  What if they don't think of that?
> 
> 
> Then they'll look for the section on variables, or class references, or  
> declaring variables in function, or .. It shouldn't be all that hard to  
> find, assuming half decent documentation.
> 
>> The only reason I would associate the absence of 'new' with allocation
>> is because of the talk from C++ programmers on this newsgroup.
>> Otherwise it's some kind of wacky cool feature of D that they have not
>> read about yet (as it is now!).  This is why I prefer 'scope' - it would
>> make scope, unambiguously, THE keyword to search for to find out what's
>> going on.
> 
> 
> Have you tried searching for an existing D keyword in the docs? First 
> you  have to decide which section to go to; is it a declaration, is it a 
> type,  is it a property, is it .. Sure it could be made easier with 
> different  documentation but that's all this boils down to, ensuring the 
> information  is in a logical place, if that's the case it makes no 
> difference whether  your searching for a specific keyword or 'RAII' or 
> 'new' or 'class' etc.  In an ideal world whatever you search for should 
> find the results you need.
> 

Yeah.  I find the current situation wrt searching the D spec pretty 
clumsy.  It would really help to be able to search the spec, and just 
the spec, and not the forums or anything else at the same time.  I think 
once that is handled, then using keywords searches will narrow things 
down to a very small number of results that someone could read through. 
  Having to figure out what section something is in is good in some 
cases, but in others is a real drag IMO :(

>>>> In a nutshell: assume a programmer of undefined language 
>>>> background,  and  I believe 'scope' will be much more informative 
>>>> (and therefore   intuitive) than a blank space.
>>>
>>>   I've assumed programmers from 2 types of background above, I still  
>>> think  removing 'new' is better than adding 'scope'.
>>>
>>
>> That's fine as long as you clear up the two things:
>> - What tells a non-C++ coder to look up 'new'? (besides another coder)
> 
> 
> See above. This is a documentation issue.
> 
>> - The possible assumption by a C++ coder that RAII syntax means stack
>> allocation.  (and subsequent whammy when they hit a pointy corner case)
>>
>> Proving that there are no corner cases in 'stack allocation implies
>> RAII' or me being wrong about that meaning in C++ should clear up that
>> second one.
> 
> 
> See above. I believe the behaviour is almost identical (and may become  
> identical in the future). I believe Walter intentionally avoids corner  
> cases WRT C++ behavior. That's the best I can do.. if I knew of a 
> corner  case I'd let you know ;)
> 
>>
>>
>> OK I think I have not explained my example very well.
>> First I'll simplify my original example and try harder to explain.  I've
>> added comments this time.
>> Here it is:
>>
>> import std.stdio;
>>
>> // class used as a function
>> class f
>> {
>>      int result;
>>      int tempVar1; // this is your non-static variable
>>
>>      static int opCall( int x )
>>      {
>>          // what do you know, not so 'static' after all!
>>          f instance = new f(x);
>>          int result = instance.result;
>>
>>          // cleanup
>>          delete instance;
>>          return result;
>>      }
>>
>>      this( int x )
>>      {
>>          // If this were a function with nested functions,
>>          //   then the main execution would occur here.
>>          result = x + internalFunc();
>>      }
>>
>>      // this is a non-static function
>>      int internalFunc()
>>      {
>>          return 314;
>>      }
>> }
>>
>> void main()
>> {
>>      int x = 42;
>>      x = f(x);
>>      writefln(x);
>> }
>>
>> Now I'll rewrite it into a free function:
>>
>> import std.stdio;
>>
>> int f( int x )
>> {
>>       // Error! internalFunc is not defined.
>>      return x + internalFunc();
>>
>>      // oh but here it is... nested functions don't allow this
>>      int internalFunc()
>>      {
>>          return 314;
>>      }
>> }
> 
> 
> Err.. you've re-written your constructor as a free function? not the  
> static opCall. Why?
> 
> I would have expected this free function:
> 
> int free_function( int x )
> {
>           // what do you know, not so 'static' after all!
>           f instance = new f(x);
>           int result = instance.result;
>           // cleanup
>           delete instance;
>           return result;
> }
> 
> void main()
> {
>      int x = 42;
>      x = free_function(x);
>      writefln(x);
> }
> 

That does work.  Forces you to put the function outside of the class, 
but meh, it's just aesthetics/syntax sugar.

> I've gotta go.. so, I'll just post what I've said so far and see what 
> you  respond with.. I may reply again to the stuff below.. :)
> 
> Regan
> 
>> In a trivial case like this all you have to do is move the nested
>> function internalFunc to the top.  In other cases though, where you want
>> to be able to places the functions where they intuitively belong, this
>> becomes annoying.  This is that forward referencing issue I was talking
>> about.
>>
>> Also, if 'f' were derived from another class, then the code executed 
>> in  f's constructor would have access to the super class's members.  
>> That's  more to do with OOP, but it's all hidden behind a static opCall.
>>
>> I suppose you could use a module function to construct and delete a  
>> private class that gets used for it's own scope and inheritance.  I  
>> worry that I am missing some detail that stopped me from just doing 
>> that  before.
>>
>> Sean, perhaps you could share some of your uses for static opCall 
>> since  I'm doing such a bad job at this?
>>
>>
>> Only slightly related - I wonder if D's 'new' even implies heap  
>> allocation as it is.  That might just be a dmd thing, but I am 
>> looking  in the spec at Classes -> ctors/dtors, and it doesn't say 
>> where they get  allocated.  Maybe someone who is good at searching the 
>> spec can find if  it says where classes get allocated?
>> My thought, as xs0 has mentioned as well, is that 'new' could be 
>> defined  independant of those implementation details.  For example, 
>> just say that  'new' will allocate a class instance, and that the 
>> instance will be  cleaned up only after there are no more references 
>> to that class.  The  only exception being explicit deletion.  Since 
>> RAII gaurantees, AFAIK,  that there are no references leaked into code 
>> that gets executed after  the scope is exited, then stack allocation 
>> would work here.  I'm finding  it hard to imagine code that would 
>> require an object to be in the heap's  address range or in the stack's 
>> address range.  xs0 says there are other  cases in which a class can 
>> be allocated on the stack without breaking  the gaurantees of no 
>> premature deletion, and I wonder what they are.
> 
> 



More information about the Digitalmars-d mailing list