POV-Ray : Newsgroups : povray.off-topic : Learning C# Server Time
29 Jul 2024 02:26:01 EDT (-0400)
  Learning C# (Message 23 to 32 of 32)  
<<< Previous 10 Messages Goto Initial 10 Messages
From: Darren New
Subject: Re: Learning C#
Date: 29 Sep 2012 19:42:46
Message: <50678776$1@news.povray.org>
On 9/29/2012 14:28, Orchid Win7 v1 wrote:
>  From what you're saying, if that code is already compiled to machine code
> but just never /runs/, then it won't increase the working set anyway.

Right. That's what distinguishes "working set" from "code size". Why would 
it have a special name?

> And Haskell generates one version of the code, and lets you request specific
> versions if you want them.

Well, all three do that, for some definition of "version" and "request" and 
"specific". :-) I think you'd have to be a little more precise if you were 
trying to make a point there.

-- 
Darren New, San Diego CA, USA (PST)
   "They're the 1-800-#-GORILA of the telecom business."


Post a reply to this message

From: Warp
Subject: Re: Learning C#
Date: 30 Sep 2012 02:13:39
Message: <5067e313@news.povray.org>
Darren New <dne### [at] sanrrcom> wrote:
> I prefer the Sing# method, wherein if you have a variable that needs to be 
> tracked, and you let it go out of scope without disposing it, the code 
> doesn't compile.

Why? Since the compiler can track when the object goes out of scope, it
can just as easily automatically add the destructor call. Why burden the
programmer with that task? It doesn't make any sense.

-- 
                                                          - Warp


Post a reply to this message

From: Orchid Win7 v1
Subject: Re: Learning C#
Date: 30 Sep 2012 06:06:07
Message: <5068198f$1@news.povray.org>
>> And Haskell generates one version of the code, and lets you request
>> specific versions if you want them.
>
> Well, all three do that, for some definition of "version" and "request"
> and "specific". :-) I think you'd have to be a little more precise if
> you were trying to make a point there.

Java does all polymorphism at run-time.

C++ does polymorphism at compile-time if you implement it with 
templates, and at run-time if you implement it with inheritance. (I.e., 
to switch from one to the other, you have to radically restructure your 
application.)

I have no idea what C# does.

Haskell does polymorphism at run-time. Unless you add a single compiler 
pragma, in which case you can selectively turn on compile-time 
polymorphism instead, on a per-function, per-type basis. (Usually only 
for types you use heavily, or for primitive types where it's going to 
allow lots of other optimisations to fire.)

And really, whether it's done at compile-time or run-time is an 
implementation detail. It /ought/ to be trivial to switch from one to 
the other...


Post a reply to this message

From: Darren New
Subject: Re: Learning C#
Date: 30 Sep 2012 14:09:35
Message: <50688adf$1@news.povray.org>
On 9/29/2012 23:13, Warp wrote:
> Why?

Separate compilation combined with actual enforcement of the rules. Sing# is 
designed to use the language, in part, to enforce security. You don't get 
stack-smash and buffer-overrun viruses because you can't smash the stack or 
overrun buffers, as an example.

> Since the compiler can track when the object goes out of scope,

But C++ can't do this, which is why you wind up with all kinds of smart 
pointer types. You can make it work, but you can also get it wrong.

If you take a mutex, and pass it to a subroutine by value, you've just 
screwed your lock, because you'll free the lock when you return from the 
subroutine, and you'll free the already-disposed lock a second time when you 
return from the call that passed it.

> it can just as easily automatically add the destructor call.

It also prevents you from calling the destructor twice.

For example, in one module, you have

void xyz(tracked one, tracked two) {
    destroy one;
}

In another, separately-compiled module, you have

void pdq() {
    tracked alpha = ...;
    tracked beta = ...;
    xyz(alpha, beta);
    alpha.use();
    beta.use(); // As written, this will crash.
    ...
}

How would you recommend the compiler deal with such a thing, if you don't 
have to annotate what's destructed and what isn't?

Singularity does it by making you declare the function like this:
void xyz([claimed] tracked one, tracked two) { destroy one; }
(or some such syntax)

Then, the compiler enforces at compile time that inside the body of xyz, you 
destroy item one exactly once, and that you don't destroy item two.

As another example:

void xyz(tracked one, tracked two) { destroy one; }

void abc(tracked three, tracked four) { xyz(three, four); }

void def() {
    tracked five = ...;
    tracked six = ...;
    abc(five, six);
    ...
}

Where does five go out of scope? Where does six go out of scope? Five and 
six are still in scope in def, but five is the same as three is the same as 
one, which is destroyed in xyz. So after the call to abc, five is no longer 
usable. Either that, or you have to say that xyz is incapable of actually 
discarding five, which means every tracked value has to be discarded in the 
same stack frame where it was created, which seems rather restrictive.

How does C++ do it? You create the file object at one stack frame, and then 
you only pass it by reference down thru the stack, right? So the thing 
really only gets destructed in the stack frame where you allocated it. Or 
you allocate it on the heap and hope that someone eventually cleans it up 
again, with smart pointers trying to automate that to some extent?



If "two" in "xyz" is declared as an "out" variable, you have to assign a new 
tracked item to it before returning, and the variable can't already have a 
value when you invoke xyz. If it's declared an "in out" variable (i.e., a 
pass-by-reference), then you can destroy it in the body, but only if you 
create a new one and assign it before you return. If it's an "in" variable 
and you destroy it, you have to put that in the type signature of the 
routine, so the caller knows it's gone.

The general technique is called "typestate", invented in the 80's as a 
general method by Robert Strom. :-)

(Sing# does some other cool things like that, too, such as letting you take 
pointers to the insides of structures on the stack, but tracking where you 
assign such pointers so they never outlast the stack frame they're pointing 
to. This lets you do things like handle network packet parsing with zero 
copying, while avoiding the possibility of leaking pointers or smashing the 
stack. It also tracks the state your communication channel state machine is 
in, making sure you don't try to send a message other than the one you 
declared you'd send at any given time, which eliminates a whole host of bugs 
you have to kludge around in languages like Hermes and Erlang where an 
unexpected packet type either gets ignored and accumulates forever in the 
buffer or causes the code to do something otherwise unexpected.)

Now, granted, in Singularity, the OS is garbage-collected also, so there are 
like 3 types that you actually have to destroy (shared buffers, pointers to 
shared buffers, and communication channels to pass those shared buffers 
over), so there's generally very little occasion to need to destroy 
something. You don't destroy threads, or locks, or memory allocations, or 
stuff like that. Just connections to other programs.

-- 
Darren New, San Diego CA, USA (PST)
   "They're the 1-800-#-GORILA of the telecom business."


Post a reply to this message

From: Warp
Subject: Re: Learning C#
Date: 1 Oct 2012 09:29:38
Message: <50699ac2@news.povray.org>
Darren New <dne### [at] sanrrcom> wrote:
> > Since the compiler can track when the object goes out of scope,

> But C++ can't do this

Of course it can. That's the very essence of RAII.

>, which is why you wind up with all kinds of smart 
> pointer types. You can make it work, but you can also get it wrong.

You are confusing tracking the scope of an object with tracking references
that point to a dynamically allocated object. Those are completely different
things.

> If you take a mutex, and pass it to a subroutine by value, you've just 
> screwed your lock

Which is why mutex objects cannot be passed by value (ie. they have their
copy constructors and copy assignment operators disabled).

(If you really need to pass such an object as a function parameter, then
you can do it by using *move* semantics rather than *copy* semantics.)

>, because you'll free the lock when you return from the 
> subroutine, and you'll free the already-disposed lock a second time when you 
> return from the call that passed it.

std::unique_ptr does not reference-count, it contains nothing more than
the pointer to the object (which itself doesn't need to contain anything
at all related to memory management), and you can give std::unique_ptrs
as function parameters (and use them eg. in data containers). You can also
eg. return one as the return value of a function. It it does not delete the
object twice.

(The reason is that it uses move semantics rather than copy semantics.)

> Singularity does it by making you declare the function like this:
> void xyz([claimed] tracked one, tracked two) { destroy one; }
> (or some such syntax)

What exactly stops the compiler from generating the destructor call
automatically? Why exactly do you have to write it explicitly?

-- 
                                                          - Warp


Post a reply to this message

From: Darren New
Subject: Re: Learning C#
Date: 2 Oct 2012 01:16:14
Message: <506a789e@news.povray.org>
On 10/1/2012 6:29, Warp wrote:
> Darren New<dne### [at] sanrrcom>  wrote:
>>> Since the compiler can track when the object goes out of scope,
>
>> But C++ can't do this
>
> Of course it can. That's the very essence of RAII.

Only for stuff I allocate on the stack and which I don't pass by value.

>> , which is why you wind up with all kinds of smart
>> pointer types. You can make it work, but you can also get it wrong.
>
> You are confusing tracking the scope of an object with tracking references
> that point to a dynamically allocated object. Those are completely different
> things.

I'm not confusing them at all. I'm simply pointing out that scope-tracking 
doesn't work for heap-allocated objects.

>> If you take a mutex, and pass it to a subroutine by value, you've just
>> screwed your lock
>
> Which is why mutex objects cannot be passed by value (ie. they have their
> copy constructors and copy assignment operators disabled).

And you deal with mutex objects in the heap how?

> (If you really need to pass such an object as a function parameter, then
> you can do it by using *move* semantics rather than *copy* semantics.)

And when you move it back...?

> (The reason is that it uses move semantics rather than copy semantics.)

But nothing enforces that.

>> Singularity does it by making you declare the function like this:
>> void xyz([claimed] tracked one, tracked two) { destroy one; }
>> (or some such syntax)
>
> What exactly stops the compiler from generating the destructor call
> automatically? Why exactly do you have to write it explicitly?

Because you might want to destruct something somewhere other than when 
returning from a block, I suppose. You don't want to destruct some object 
like that when you dispose the penultimate reference, but you do want to 
destruct it when you dispose the ultimate reference. You need some way of 
storing it in the heap, as well as the ability to reliably pass references 
to stack objects downward in the call tree but not return them upwards.

Remember that singularity supports multiple languages, too.

-- 
Darren New, San Diego CA, USA (PST)
   "They're the 1-800-#-GORILA of the telecom business."


Post a reply to this message

From: Warp
Subject: Re: Learning C#
Date: 2 Oct 2012 07:32:17
Message: <506ad0c1@news.povray.org>
Darren New <dne### [at] sanrrcom> wrote:
> On 10/1/2012 6:29, Warp wrote:
> > Darren New<dne### [at] sanrrcom>  wrote:
> >>> Since the compiler can track when the object goes out of scope,
> >
> >> But C++ can't do this
> >
> > Of course it can. That's the very essence of RAII.

> Only for stuff I allocate on the stack and which I don't pass by value.

"Scope" is defined by the stack. An object created inside a scope goes out
of scope when that scope ends, of course. If you create an object
dynamically, it can outlive the scope where it was created.

If you pass the object by value, you are creating a *new* object (with its
own scope). It's not the same object. That's what "copying" means.

> I'm not confusing them at all. I'm simply pointing out that scope-tracking 
> doesn't work for heap-allocated objects.

That's rather obvious, given that heap-allocated objects are not
scope-based.

> > (If you really need to pass such an object as a function parameter, then
> > you can do it by using *move* semantics rather than *copy* semantics.)

> And when you move it back...?

You don't "move it back". If you move a resource you relinquish it to
whatever you are moving it to.

> > (The reason is that it uses move semantics rather than copy semantics.)

> But nothing enforces that.

The class itself can enforce it.

> >> Singularity does it by making you declare the function like this:
> >> void xyz([claimed] tracked one, tracked two) { destroy one; }
> >> (or some such syntax)
> >
> > What exactly stops the compiler from generating the destructor call
> > automatically? Why exactly do you have to write it explicitly?

> Because you might want to destruct something somewhere other than when 
> returning from a block, I suppose.

And have the possibility of dereferencing a destroyed object?

-- 
                                                          - Warp


Post a reply to this message

From: Darren New
Subject: Re: Learning C#
Date: 2 Oct 2012 11:24:04
Message: <506b0714$1@news.povray.org>
On 10/2/2012 4:32, Warp wrote:
> "Scope" is defined by the stack.

Only for auto variables, yes.

> An object created inside a scope goes out
> of scope when that scope ends, of course.

Objects (i.e., values) don't have scopes. Variables have scopes. A scope is 
the portion of the program code over which the name of a variable is valid.

An object doesn't go out of scope when the scope ends. The variable 
referencing the object goes out of scope when the scope ends.

> If you create an object
> dynamically, it can outlive the scope where it was created.

And that's the problem.

> If you pass the object by value, you are creating a *new* object (with its
> own scope). It's not the same object. That's what "copying" means.

And that too is the problem.

>> I'm not confusing them at all. I'm simply pointing out that scope-tracking
>> doesn't work for heap-allocated objects.
>
> That's rather obvious, given that heap-allocated objects are not
> scope-based.

Well, you asked me why the compiler couldn't just add the destructor for 
you. This is your answer: it's rather obvious.

>>> (If you really need to pass such an object as a function parameter, then
>>> you can do it by using *move* semantics rather than *copy* semantics.)
>
>> And when you move it back...?
>
> You don't "move it back". If you move a resource you relinquish it to
> whatever you are moving it to.

So if I want to pass an object into a function as an argument, and then 
still use it when that function returns, I can't?

Also, how does the compiler know you've moved it, so you *don't* reference 
it upon return?

>>> (The reason is that it uses move semantics rather than copy semantics.)
>
>> But nothing enforces that.
>
> The class itself can enforce it.

"Can enforce" is not the same as "enforce".  it's not part of the semantics 
of the language.

>>>> Singularity does it by making you declare the function like this:
>>>> void xyz([claimed] tracked one, tracked two) { destroy one; }
>>>> (or some such syntax)
>>>
>>> What exactly stops the compiler from generating the destructor call
>>> automatically? Why exactly do you have to write it explicitly?
>
>> Because you might want to destruct something somewhere other than when
>> returning from a block, I suppose.
>
> And have the possibility of dereferencing a destroyed object?

No. That's exactly the point of the declarations like [claim] that I showed 
you. You can't dereference it after it's destroyed because the declarations 
prevent you from dereferencing it after someone else has claimed it, and 
destroying it claims it.


-- 
Darren New, San Diego CA, USA (PST)
   "They're the 1-800-#-GORILA of the telecom business."


Post a reply to this message

From: Orchid Win7 v1
Subject: Re: Learning C#
Date: 1 Dec 2012 09:15:50
Message: <50ba1116$1@news.povray.org>
On 28/09/2012 04:40 PM, Jim Henderson wrote:
> On Fri, 28 Sep 2012 15:51:34 +0100, Orchid Win7 v1 wrote:
>
>> Apparently the only way to do that is to download an ISO image. Which
>> means you have to burn it to a CD before you can access it. [Unless you
>> use Unix. :-P ]
>
> Daemon Tools.

Yes, apparently.


Post a reply to this message

From: Jim Henderson
Subject: Re: Learning C#
Date: 1 Dec 2012 23:45:16
Message: <50badcdc@news.povray.org>
On Sat, 01 Dec 2012 14:16:02 +0000, Orchid Win7 v1 wrote:

> On 28/09/2012 04:40 PM, Jim Henderson wrote:
>> On Fri, 28 Sep 2012 15:51:34 +0100, Orchid Win7 v1 wrote:
>>
>>> Apparently the only way to do that is to download an ISO image. Which
>>> means you have to burn it to a CD before you can access it. [Unless
>>> you use Unix. :-P ]
>>
>> Daemon Tools.
> 
> Yes, apparently.

Not just apparently - just "Yes". :)

Jim


Post a reply to this message

<<< Previous 10 Messages Goto Initial 10 Messages

Copyright 2003-2023 Persistence of Vision Raytracer Pty. Ltd.