 |
 |
|
 |
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> By the way, I honestly wonder what's the "proper" way of doing that
> in Java, given that Java has exceptions and no scope-bound lifetime of
> objects.
try {
handle = open(...);
} finally {
if (handle != null) handle.close();
}
Even uglier because you obviously have to declare the handle outside the
block, check to see if the assignment worked, etc.
> Do you always have to follow file handle creation with a 'try'
> block to guard against exceptions? Or is there some other trick to make
> such code exception-safe?
try/finally is what you use for making code exception safe.
> (I understand that in C# this is handled with a 'using' block, which
> automatically and immediately disposes of objects when the block is
> exited. Is that the proper way of making file handles exception-safe
> there?)
Yep. You're basically supposed to put anything with a "Dispose" method
inside a using block. (Well, unless you're assigning it to something more
global, etc.) More elegant than Java, but certainly not elegant, especially
since it's not always obvious what classes have a Dispose method without
reading the documentation, so it's easy to miss something if you're working
with a new library.
--
Darren New, San Diego CA, USA (PST)
Serving Suggestion:
"Don't serve this any more. It's awful."
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Darren New wrote:
> and everything in between would be unable to overwrite it or
> copy it or whatever (outside of, of course, passing it to subroutines,
> etc).
Well, not quite. I mean, there are a lot of rules making it usable, but
they're the obvious rules. (You can overwrite the value with a newly
allocated value if you dispose of the old value in the same routine, etc.)
--
Darren New, San Diego CA, USA (PST)
Serving Suggestion:
"Don't serve this any more. It's awful."
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Darren New <dne### [at] san rr com> wrote:
> Yeah, I was trying to remember some of the others. Something perl-like,
> certanily (python, Perl, bash, Tcl, etc).
python, perl and tcl are very unlike each other despite the scripting moniker.
While I dig python better for the crystal-crisp syntax and iteration constructs,
perl is much more formidable as far as plain linguistic flexibility goes... it
truly is a marvel to behold in all of its C, shell, Lisp, forth summarizing of
ideas goes...
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
nemesis wrote:
> Darren New <dne### [at] san rr com> wrote:
>> Yeah, I was trying to remember some of the others. Something perl-like,
>> certanily (python, Perl, bash, Tcl, etc).
>
> python, perl and tcl are very unlike each other despite the scripting moniker.
Fairly so, yes. But the concepts of a "scripting language" (i.e., the
primary data structure being a hashtable, the idea that "evaluating a
definition" is pretty much what creates code rather than strictly
compilation, the baroque scoping rules, etc) is what's more important than
the fact that (for example) regex is a literal in Perl and not in the others.
> truly is a marvel to behold in all of its C, shell, Lisp, forth summarizing of
> ideas goes...
Errr, Perl has something to do with FORTH? You'll have to clarify that one,
because, like, it really doesn't, as far as I can tell.
--
Darren New, San Diego CA, USA (PST)
Serving Suggestion:
"Don't serve this any more. It's awful."
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Darren New <dne### [at] san rr com> wrote:
> nemesis wrote:
> > Darren New <dne### [at] san rr com> wrote:
> >> Yeah, I was trying to remember some of the others. Something perl-like,
> >> certanily (python, Perl, bash, Tcl, etc).
> >
> > python, perl and tcl are very unlike each other despite the scripting moniker.
>
> Fairly so, yes. But the concepts of a "scripting language" (i.e., the
> primary data structure being a hashtable, the idea that "evaluating a
> definition" is pretty much what creates code rather than strictly
> compilation, the baroque scoping rules, etc) is what's more important than
> the fact that (for example) regex is a literal in Perl and not in the others.
regex is a literal in ruby too. I also like perl scoping with my and our. :)
> > truly is a marvel to behold in all of its C, shell, Lisp, forth summarizing of
> > ideas goes...
>
> Errr, Perl has something to do with FORTH? You'll have to clarify that one,
> because, like, it really doesn't, as far as I can tell.
like forth, it doesn't need to operate on named arguments, which is taken then
to be the first in the "stack"... :)
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Darren New <dne### [at] san rr com> wrote:
> The destructor semantics is one of the things I think C++ really did get
> just right.
The proper name for that mechanism would be "RAII" (which might be
somewhat of a misnomer, as it doesn't fully express what it means).
(In principle RAII is not incompatible with garbage collection, so
conceivably you could have both in the same language.)
According to wikipedia, C++ is not the only language using RAII, and
mentions Ada as another one. I didn't know that.
--
- Warp
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
nemesis wrote:
> like forth, it doesn't need to operate on named arguments, which is taken then
> to be the first in the "stack"... :)
Uh, OK. That's, like, the absolute least important bit of FORTH there, but
OK. :-) I think that's more shell syntax than FORTH syntax, really.
--
Darren New, San Diego CA, USA (PST)
Serving Suggestion:
"Don't serve this any more. It's awful."
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> Darren New <dne### [at] san rr com> wrote:
>> The destructor semantics is one of the things I think C++ really did get
>> just right.
>
> The proper name for that mechanism would be "RAII" (which might be
> somewhat of a misnomer, as it doesn't fully express what it means).
I think the two are separate, but "RAII" is the best (maybe even "intended")
way to use the C++ destructor semantics. Certainly the semantics don't
change if you screw up your RAII. :-)
> (In principle RAII is not incompatible with garbage collection, so
> conceivably you could have both in the same language.)
Indeed. Especially if you use something like reference counting and deal
with circular references specially or something. Unfortunately, the fastest
garbage collectors are the ones that never touch the garbage, so it's hard
to do this well without having basically a separate heap for objects with
non-memory destructors, which I think is what we'll eventually start seeing
in some of these run-time implementations, if we don't start seeing GC-smart
operating systems first. (And I'd even count Erlang in that latter
categorization, since all interaction outside the Erlang semantics goes thru
a "port" kind of construct rather than a function call kind of construct.)
> According to wikipedia, C++ is not the only language using RAII, and
> mentions Ada as another one. I didn't know that.
I think that's ... stretching it a bit. Sounds like Ada folks trying to
convince C++ folks they should switch or something. :-)
Ada is very non-orthogonal in its data structures (even more so than C++).
Objects are basically declared as "records with a vtable" or so, and if your
data type isn't a type of record (in the Pascal/Algol sense of the word, or
what C would call a struct), then you don't get to make it an object.
Ah. They're called "controlled types."
http://www.adaic.org/docs/95style/html/sec_9/9-2-3.html
Basically, an object type that inherits from Ada.Finalization.Controlled.
Inheriting from that type gives you three methods: Initialize, Finalize and
Adjust. Finalize is like the destructor, and Adjust is sort of like a
copy/assignment constructor, only more limited. (When you assign to a
controlled type, it copies all the hardware bits, then invokes Adjust on the
copy. You don't get to change what you assigned *from*, because that would
be confusing. Use a procedure with two in/out arguments for that. :-)
However, this only applies to objects derived from Controlled. It doesn't
work with strings, arrays, any of the built-in collections, files, tasks,
loadable packages, pointers, etc etc etc. Ada is really pretty
non-orthogonal in that sense. You can't mix and match. (Yes, that sucks. :-)
I.e., much like you can't have constructors and destructors on pointers or
integers in C++, except even for some very complicated types in Ada.
Note that any type can be declared "limited" too, which basically means the
assignment operator is private. A record that can do inheritance (i.e., that
has a vtable) is called a "tagged type". In case you get interested and read
some of the following pages. :-) Note that "class type X" means "X and all
its descendants" as opposed to "type X" which means just type X. Declaring a
procedure with an argument that is a class type is how you get run-time
dynamic inheritance-based dispatch as opposed to overloading. The "with
private" declaration is like declaring something "struct xyz;" in C or C++.
A "protected" type is basically a monitor in the multitasking definition of
the word.
Blah'goop is a way of getting the goop property or type out of the blah
variable or type. So myarray'length, or mytaggedtype'parent, or
localvariable'address (&localvariable) or something like that.
Ada has some very unusual yet precise terminology. It's fun to read a
sentence talking about a controlled atomic volatile protected limited class
type and have an idea of what that means. ;-)
--
Darren New, San Diego CA, USA (PST)
Serving Suggestion:
"Don't serve this any more. It's awful."
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Darren New <dne### [at] san rr com> wrote:
> > (In principle RAII is not incompatible with garbage collection, so
> > conceivably you could have both in the same language.)
> Indeed. Especially if you use something like reference counting and deal
> with circular references specially or something.
What I meant is that, for example in C++, it's possible to have value
semantics for objects as well as reference (well, pointer) semantics for
objects, and these can be kept pretty much separate. Then you can have
RAII (iow. scope-based lifetime) semantics on the objects and GC on the
references. There are, in fact, GC engines for C++ which work like this
(basically, anything you allocate dynamically with 'new' can be GC'd,
while anything you allocate without it uses regular RAII semantics).
On a different note, one example where I think the RAII mechanism is
better than the Java-style GC mechanism is that RAII allows you to
implement copy-on-write semantics (ie. "lazy copying") for objects.
In other words, if you have some object with potentially lots of data,
and you want to use copy semantics for it (ie. if you assign the object
to another, the latter gets a copy of the data rather than sharing the
data), copy-on-write makes the copying lazily, only if needed (ie. only
if one of the copies tries to modify the data).
For instance, assume that std::string used copy-on-write (which it does
in some implementations). If you write this:
std::vector<std::string> strings(1000, "hello there");
you will have a vector of 1000 strings, each one with the value "hello
there". However, the actual "hello there" data is shared among all the
strings and hence stored in memory only once (which may become a significant
saving if it would be kilobytes or megabytes of data instead of just 11
characters). However, if you now do something like:
strings[250] += ", world";
only the 251st string in the vector will be converted to "hello there, world"
rather than all of them. The rest of the string will still share that one
and same data, and only the 251st string will now have its own copy of the
data, with more data appended.
What happened there is that the 251st string made a deep-copy of the data
before modifying it, thus preventing any of the other strings from changing.
Of course if you now modify it again:
strings[250] += "!";
it will *not* perform a needless deep-copy of the data because the current
data is not shared. In other words, it will deep-copy the data only when
needed.
And most importantly, it will do that completely transparently. You can't
see that from the outside. From the outside std::string simply has copy
semantics and that's it. You could compile the program with a different
implementation of std::string which does not use CoW, and it would still
work the same (except, obviously, now consuming more memory).
Of course CoW requires reference counting of the data (or, more precisely,
it needs a way to tell if the data is being shared or not). I don't know
how you would do that in Java (transparently, or at all).
This is possible transparently in C++ because of RAII: When objects are
created, copied, assigned and destroyed, you can specify what happens.
This allows you to keep a reference count on the data handled by the class.
(And note that I'm not saying there aren't advantages with a GC system
like the one in Java, including efficiency benefits in some situations.)
--
- Warp
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> Darren New <dne### [at] san rr com> wrote:
>> Indeed. Especially if you use something like reference counting and deal
>> with circular references specially or something.
>
> What I meant is that,
Oh, I see. Yes, I guess that would work. Probably an excellent way to do it,
especially if you store enough info about things that you can do compacting
collections.
> On a different note, one example where I think the RAII mechanism is
> better than the Java-style GC mechanism is that RAII allows you to
> implement copy-on-write semantics (ie. "lazy copying") for objects.
I understand what you're saying, but this is a bad example for Java because
that's exactly how it would work in Java, because strings are immutable, so
your "+=" returns a brand new string. :-) That's why Java has a
StringBuilder as well as a String.
> Of course CoW requires reference counting of the data (or, more precisely,
> it needs a way to tell if the data is being shared or not). I don't know
> how you would do that in Java (transparently, or at all).
Very difficult in Java to think of a way, offhand. I don't think you can
overload pure assignment in C#, so I don't think you could do the same sort
of thing there easily either.
> This is possible transparently in C++ because of RAII: When objects are
> created, copied, assigned and destroyed, you can specify what happens.
I think it's more because you can overload the assignment operator, not the
RAII as such. Maybe you count that as part of RAII.
--
Darren New, San Diego CA, USA (PST)
Serving Suggestion:
"Don't serve this any more. It's awful."
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|
 |