Topic: [C++0x] Call a method last (close file conundrum)


Author: f5xz2bk02@sneakemail.com (Davide Bolcioni)
Date: Sat, 18 Sep 2004 13:13:07 GMT
Raw View
Paul Evans ha scritto:
> f5xz2bk02@sneakemail.com (Davide Bolcioni) wrote in message news:<7trj12-bd3.ln1@interbusiness.it>...
> [snip]
>
>>The conundrum is that the POSIX close() returns an error code, so a C++
>>class wrapping a file descriptor has a number of unpleasant approaches:
>>- close() is called only in the destructor, and the error code is lost;
>>- you have a member function calling close(), but this forces you to
>>   check that the file descriptor is valid before attempting any other
>>   operation in all methods or otherwise handle the possibility that
>>   the caller calls close() followed by some other method operating on
>>   the now defunct file descriptor.
>>
>>Although neither option is going to cause the end of civilization as we
>>know it, both are inelegant at best. There is no way in C++ to have the
>>compiler enforce the notion of 'this method, if called, must be called
>>last' - a notion which I see as intrisic at least to the Builder in the
>>Builder pattern, for example.
>
> There is a third way:
> Simply handle the error in the dtor.  You've released the resource so
> this object's work is done save logging the error in someway.  Either
> to a log file, to the user or to the controller/manager that owns this
> resource (which may inturn log or even throw an exception.)  All this
> design requires is associating a 'Logging' resource (who life-cycle
> exceeds that of the file) with the file wrapper object.

There are many approaches, for example something along the lines of:

   template <class T>
   struct destruction_observer {
     T observed_result;
     void remember(const T& t) { observed_result = t; }
   }

   class file {
      destruction_observer<int> & observer_;
      int fd_;
   public:
     file(const char* name, destruction_observer<int> & observer);

     ~file() {
       observer_.remember(close(fd_));
     }
   }

   destruction_observer<int> f_observer;
   file f("/etc/passwd", f_observer);

Having to write the 'file' class with the observer in mind is what I
should have to do anyway, but I see some problems:
- each file instance must keep a reference to the observer, while
   my proposal uses block scope to make this unnecessary;
- if an exception occurs in remembering the result, it must be
   swallowed as throwing in destructors is a no-no ... maybe we
   could store the exception unless this also throws;
- the observer might, although it should not, disappear.

A small problem, for sure, but one I cannot find a satisfying solution
for.

Davide Bolcioni
--
Paranoia is a survival asset.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: =?ISO-8859-1?Q?=22Daniel_Kr=FCgler_=28ne_Spangenberg=29=22?= <dsp@bdal.de>
Date: Mon, 20 Sep 2004 23:22:24 CST
Raw View
Hello Peter Dimov,

Peter Dimov schrieb:

>In fact close() is very similar to a C++ destructor because it never
>fails. That is, after close() is invoked on a file descriptor, this
>file descriptor is no longer valid. A destructor never fails, either;
>after a destructor, there is no object, just raw memory.
>
>close() can fail in the sense that the operation may not complete
>without errors, but this does not impact the logic of the program
>beyond that point, regardless of the error code (ignoring EBADF which
>"should never happen" in a bug-free program.) Errors returned by
>close() are "informative". In a typical filesystem with a write-back
>cache, you'll never get EIO anyway.
>
>fclose() is a bit different because it flushes the internal buffer;
>even so, you have the option to fflush beforehand and deal with the
>error.
>
>In theory free() can also fail (it just doesn't report any error
>back), but nobody tends to lose much sleep over it. It's close() that
>gets all the publicity. :-)
>

The possible problems I see with close is its specification in
27.8.1.7/p. 4:

"Effects: Calls rdbuf()->close() and, if that function returns false,
calls setstate(failbit)
(which may throw ios_base::failure (27.4.4.3))."

Thus close might throw an exception and this one should be caught in the
d'tor.

I am not sure whether I correctly understand your argumentation
regarding fclose, but
basic_filebuf must behave as if it had called fclose (and will report an
error, if that
fails).

Otherwise a absolutely agree to your mentioned points.

Greetings from Bremen,

Daniel


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: f5xz2bk02@sneakemail.com (Davide Bolcioni)
Date: Wed, 15 Sep 2004 03:01:58 GMT
Raw View
Jonathan Turkanis ha scritto:
> "Davide Bolcioni" <f5xz2bk02@sneakemail.com> wrote in message
> news:uu0b12-kg2.ln1@interbusiness.it...
>
>>Greetings,
>>having again faced the conundrum of close() of a file descriptor which
>>can return an error code, the following occurred to me.
>
>
> Would you describe the conundrum, please?

The conundrum is that the POSIX close() returns an error code, so a C++
class wrapping a file descriptor has a number of unpleasant approaches:
- close() is called only in the destructor, and the error code is lost;
- you have a member function calling close(), but this forces you to
   check that the file descriptor is valid before attempting any other
   operation in all methods or otherwise handle the possibility that
   the caller calls close() followed by some other method operating on
   the now defunct file descriptor.

Although neither option is going to cause the end of civilization as we
know it, both are inelegant at best. There is no way in C++ to have the
compiler enforce the notion of 'this method, if called, must be called
last' - a notion which I see as intrisic at least to the Builder in the
Builder pattern, for example.

Davide Bolcioni
--
There is no place like /home.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: f5xz2bk02@sneakemail.com (Davide Bolcioni)
Date: Wed, 15 Sep 2004 03:03:47 GMT
Raw View
Alberto Barbati ha scritto:

> So, in practice, you want to have destructors to return values.

Sort of. My proposal purposefully leaves destructors alone, introducing
a notion of "last method": if called, it must be called last.

> While
> your desire is noble and your attempt to solve the problem is
> appreciable, I believe that the cost/value ratio of your solution is
> unfavourable. Let's compare your proposed syntax:
>
>   T x = delete { C c; /* ... */ };       // (1)
>
> with the following idiom, currently valid in C++:
>
>   T x; { C c; /* ... */ x = c.last(); }  // (2)
>
> Syntax (1) has the following advantages:
>
> * consistent naming of the "last function"
>
> * it's impossible to forget to call the last function

As stated above, it is entirely possible not to call it; but if you call it,
it gets called last.

> and the following disadvantages:
>
> * the presence of the keyword delete is very counter-intuitive. The
> meaning of delete is quite radicated and immediately makes me think "I
> am going to explicitly destroy and deallocate a dynamically allocated
> object". No dynamic allocation is involved here and the destructor would
> be called anyway.

Yes, the syntax is rather unappealing.

> * understanding the object for which the "last function" is called
> requires a bit of practice.
>
> * the observation you make at point 7 about blocks like "delete { C c1;
> C c2; ... }" is a serious annoyance, whether you allow them (code would
> be confusing) or disallow them (too restrictive, IMHO).
>
> OTOH, idiom (2) has the following advantages:
>
> * call to the last method is explicit, the programmer's intent is
> expressed more clearly, in a way that is already familiar to the reader.
>
> * no restrictions on the number of "last methods". Why should a class
> have only one "last method"?

For the same reason we have only one constructor: it gets called by the
compiler, so I wanted the compiler's choice to be easy and easily
understood by the programmer.

> and the following disadvantages:
>
> * you may forget to call the "last method". However, unless T is an UDT
> with a default ctor, the compiler may be able to detect that x is
> declared/used without having being assigned a value.

Forgetting the last method is not what I wanted to address; in the case
of a Builder if you forget to call it you get no built object, and in
the case of close() it is entirely possible that you have no interest
in the resulting error.

I also considered introducing __attribute__(last) to decorate methods
which can only be called last, but my intent was also to restrict the
idiom in order to make a compile time solution workable: if I generalize
the notion using e.g. __attribute__(last) and allow the following

   struct C {
     C();
     int last_method() __attribute__(last);
   }

   C* pc = new C();
   C* pc = pq;

   pc->last_method();
   pq->last_method();

how is the compiler supposed to prohibit the second invocation of
last_method() without extra state information in each instance of C ?

Tying the last method to a scope (using a block) allows a compile-time
only implementation. An alternative could have been

   T x = { C c; ... };

but I gather this syntax is already in use and under extension for
aggregate initialization. Maybe

   T x = last { C c; ... }

which is beginning to look like Perl :-)

Davide Bolcioni
--
There is no place like /home.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: Alberto Barbati <AlbertoBarbati@libero.it>
Date: Wed, 15 Sep 2004 16:09:49 CST
Raw View
Davide Bolcioni wrote:
> Alberto Barbati ha scritto:
>
>> So, in practice, you want to have destructors to return values.
>
> Sort of. My proposal purposefully leaves destructors alone, introducing
> a notion of "last method": if called, it must be called last.
>

Yes, I understand that your proposal allows the last method to be called
only if requested by the programmer (that is: in presence of a
delete-block) and not on every destructor call. My remark was just to
make the problem domain clearer as the sentence "close file conundrum"
was not very self-explanatory.

>>
>> Syntax (1) has the following advantages:
>>
>> * consistent naming of the "last function"
>>
>> * it's impossible to forget to call the last function
>
> As stated above, it is entirely possible not to call it; but if you call
> it, it gets called last.

If I write a delete-block, the last method *will* be called (last)? So
it's impossible to forget to call it. In order not to call it you have
to remove the delete-block (or avoid writing it in the first place).
Correct?

>>
>> * no restrictions on the number of "last methods". Why should a class
>> have only one "last method"?
>
> For the same reason we have only one constructor: it gets called by the
> compiler, so I wanted the compiler's choice to be easy and easily
> understood by the programmer.
>

But we don't have only one constructor! We have at most one (notice, not
"only one") default constructor and I can I have as many non-default
constructors as I want.

Maybe you meant to say destructor? In that case there is a very good
reason to have only one them, because it must be possible to call it
from contexts where parameters cannot be provided. In case of the last
method, the fact that I cannot provide parameters is a deficiency of the
syntax rather than an intrinsic problem.

>> and the following disadvantages:
>>
>> * you may forget to call the "last method". However, unless T is an
>> UDT with a default ctor, the compiler may be able to detect that x is
>> declared/used without having being assigned a value.
>
>
> Forgetting the last method is not what I wanted to address; in the case
> of a Builder if you forget to call it you get no built object, and in
> the case of close() it is entirely possible that you have no interest
> in the resulting error.

As I already said, this is perfectly clear to me. Here, I am just
comparing the delete-block syntax you propose with a nearly equivalent
and currently valid idiom. I am not trying to compare the delete-block
case with the non-delete-block case.

> I also considered introducing __attribute__(last) to decorate methods
> which can only be called last, but my intent was also to restrict the
> idiom in order to make a compile time solution workable: if I generalize
> the notion using e.g. __attribute__(last) and allow the following
>
>   struct C {
>     C();
>     int last_method() __attribute__(last);
>   }

In fact, this solution is even less appealing than the other one.

>
>   C* pc = new C();
>   C* pc = pq;
>
>   pc->last_method();
>   pq->last_method();
>
> how is the compiler supposed to prohibit the second invocation of
> last_method() without extra state information in each instance of C ?

Wait a minute. You describe the last method semantic only in presence of
delete-blocks and I don't see any delete-block here. So the question
here is not "how can the compiler enforce the last method semantic in
this case" but rather "do the compiler have to enforce the last method
semantic in absence of a delete-block"? If the answer is yes, you still
have a few options:

1) you can disallow explicit calls of a last method
2) the compiler can maintains extra state information. I don't see it as
a big problem a priori. It may be a small price to pay but you may get
into multi-threading issues, I guess

If the answer is no, you can just declare it undefined behaviour (as
explicitly calling the destructor twice for the same object).

>
> Tying the last method to a scope (using a block) allows a compile-time
> only implementation. An alternative could have been
>
>   T x = { C c; ... };
>
> but I gather this syntax is already in use and under extension for
> aggregate initialization. Maybe

I understand the need for the scope block. That's clever, because to
ensure that the method is called last, the only reliable way is to call
it just before destroying the object. In C++ destruction of automatic
variables happens at a block scope boundary, so it makes sense. I also
understand the need to put a keyword before '{' to disambiguate from
aggregate initialization. For these reasons I doubt that the syntax can
be made any better. Unfortunately, it's still doesn't look good to me.

>
>   T x = last { C c; ... }
>
> which is beginning to look like Perl :-)
>

Adding a new keyword does not make the syntax more appealing... The
committee is always reluctant about adding new keywords, especially when
they resemble words that are very commonly used for variables names ;-)

I forgot to comment about exception handling. You said:

> 5. The syntax does not allow try/catch clauses, but exceptions can be
>    handled inside the block if desired: e.g. use a class consisting of
>    just a single constructor and the last method, both non-throwing.

I don't understand the example... if the class consists of just a single
constructor and the last method, both non-throwing, what exceptions are
there to be handled?

Anyway, you should be more precise about what happens if an exception is
thrown during the execution of the delete-block as you cannot enforce in
*any way* that it can't happen. I guess the last method is not invoked
in that case. There would be no reason to call it, because the
destination object x is going to be destroyed right away.

Last thing, could you explain why would the syntax disallow try/catch
clauses? I don't seen any problem with that. I mean both:

   try { T x = delete { /*...*/ }; } catch(/*...*/) { /*...*/ }

and

   T x = delete { try { /*...*/ } catch(/*...*/) { /*...*/ } };

looks good to me.

Regards,

Alberto

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: paul_evans7@hotmail.com (Paul Evans)
Date: Thu, 16 Sep 2004 15:19:19 GMT
Raw View
f5xz2bk02@sneakemail.com (Davide Bolcioni) wrote in message news:<7trj12-bd3.ln1@interbusiness.it>...
[snip]
> The conundrum is that the POSIX close() returns an error code, so a C++
> class wrapping a file descriptor has a number of unpleasant approaches:
> - close() is called only in the destructor, and the error code is lost;
> - you have a member function calling close(), but this forces you to
>    check that the file descriptor is valid before attempting any other
>    operation in all methods or otherwise handle the possibility that
>    the caller calls close() followed by some other method operating on
>    the now defunct file descriptor.
>
> Although neither option is going to cause the end of civilization as we
> know it, both are inelegant at best. There is no way in C++ to have the
> compiler enforce the notion of 'this method, if called, must be called
> last' - a notion which I see as intrisic at least to the Builder in the
> Builder pattern, for example.


There is a third way:
Simply handle the error in the dtor.  You've released the resource so
this object's work is done save logging the error in someway.  Either
to a log file, to the user or to the controller/manager that owns this
resource (which may inturn log or even throw an exception.)  All this
design requires is associating a 'Logging' resource (who life-cycle
exceeds that of the file) with the file wrapper object.

Hope this helps,
Paul.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: richtesi@informatik.tu-muenchen.de (Simon Richter)
Date: Fri, 17 Sep 2004 06:14:48 GMT
Raw View
Hi,

> The conundrum is that the POSIX close() returns an error code, so a C++
> class wrapping a file descriptor has a number of unpleasant approaches:

I believe closing a file in a destructor is simply wrong, as the
destructor has no way of stopping the object's destruction, and there
could be state attached to the open file that should not yet be flushed.

The destructor should flag an error if it is called without closing the
file first.

    Simon

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: dsp@bdal.de (=?ISO-8859-1?Q?=22Daniel_Kr=FCgler_=28ne_Spangenberg=29=22?=)
Date: Fri, 17 Sep 2004 16:26:14 GMT
Raw View
Hello Simon Richter,

Simon Richter schrieb:

>> The conundrum is that the POSIX close() returns an error code, so a C+=
+
>> class wrapping a file descriptor has a number of unpleasant approaches=
:
>
>
> I believe closing a file in a destructor is simply wrong, as the=20
> destructor has no way of stopping the object's destruction, and there=20
> could be state attached to the open file that should not yet be flushed.
>
> The destructor should flag an error if it is called without closing=20
> the file first.


To my opinion the wording "closing a file in a destructor is simply=20
wrong" is too strong. Just because of the fact that
a close can fail is no reason, that e.g. iostream's shouldn't close in=20
the destructor. A similar problems occurs in
the sentry classes by the way.

Fact is, that it is actually not so unusual that at least part of the=20
destructor activities of a given class **could** fail
under special circumstances. There are at least two possibilities, how a=20
destructor could report **exception-free**
such a failure without enforcing a special report method (like writing=20
into a file) under the current C++ object destruction
model:

1) The class instance contains a reference to a failure reporter, and=20
informs the failure reporter in case of failure
(say that 3 times quickly ;-))

2) The class instance informs a globally avaliable failure reporter=20
(usually a singleton) concerning such an
error.

Greetings from Bremen,

Daniel Kr=FCgler


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: Michiel.Salters@logicacmg.com (Michiel Salters)
Date: Fri, 17 Sep 2004 17:21:32 GMT
Raw View
f5xz2bk02@sneakemail.com (Davide Bolcioni) wrote in message news:<uu0b12-kg2.ln1@interbusiness.it>...
> Greetings,
> having again faced the conundrum of close() of a file descriptor which
> can return an error code

Throw an error from the destructor. That is annoying, and may result
in some problems, but it is standard C++.
It seems to me your proposed syntax doesn't address those issues
anyway. How do you deal with multiple exceptions/error codes when
destroying multiple objects?

Regards,
Michiel Salters

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: pdimov@gmail.com (Peter Dimov)
Date: Sat, 18 Sep 2004 04:57:19 GMT
Raw View
dsp@bdal.de ("Daniel Kr   gler (ne Spangenberg)") wrote in message news:<414AA826.2060302@bdal.de>...
> Hello Simon Richter,
>
> Simon Richter schrieb:
>
> >> The conundrum is that the POSIX close() returns an error code, so a C++
> >> class wrapping a file descriptor has a number of unpleasant approaches:
> >
> >
> > I believe closing a file in a destructor is simply wrong, as the
> > destructor has no way of stopping the object's destruction, and there
> > could be state attached to the open file that should not yet be flushed.
> >
> > The destructor should flag an error if it is called without closing
> > the file first.
>
>
> To my opinion the wording "closing a file in a destructor is simply
> wrong" is too strong.

Yes, agreed.

> Just because of the fact that
> a close can fail is no reason, that e.g. iostream's shouldn't close in
> the destructor.

In fact close() is very similar to a C++ destructor because it never
fails. That is, after close() is invoked on a file descriptor, this
file descriptor is no longer valid. A destructor never fails, either;
after a destructor, there is no object, just raw memory.

close() can fail in the sense that the operation may not complete
without errors, but this does not impact the logic of the program
beyond that point, regardless of the error code (ignoring EBADF which
"should never happen" in a bug-free program.) Errors returned by
close() are "informative". In a typical filesystem with a write-back
cache, you'll never get EIO anyway.

fclose() is a bit different because it flushes the internal buffer;
even so, you have the option to fflush beforehand and deal with the
error.

In theory free() can also fail (it just doesn't report any error
back), but nobody tends to lose much sleep over it. It's close() that
gets all the publicity. :-)

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: f5xz2bk02@sneakemail.com (Davide Bolcioni)
Date: Sat, 11 Sep 2004 17:25:07 GMT
Raw View
Greetings,
having again faced the conundrum of close() of a file descriptor which
can return an error code, the following occurred to me.

1. Extend class syntax to allow the definition of a "last method"

   class C {
     // ...

     T !C() { /* ... */ return t; } // Last method, no overloads.
   }

2. Introduce a "delete block expression", e.g.

   T x = delete { C c; /* ... */ };

3. If an object with a "last method" is declared in a scope other than
    the scope of a delete block expression, the "last method" is ignored.

4. A "delete block expression" is implemented as

      T x; { C c; /* ... */ x <- c.!C() }

    where <- stands for compiler magic equivalent to initialization or
    assignment as appropriate. The result of a "delete block expr" is
    therefore the result of calling the "last method" of the first
    object declared in the block, before calling destructors.

5. The syntax does not allow try/catch clauses, but exceptions can be
    handled inside the block if desired: e.g. use a class consisting of
    just a single constructor and the last method, both non-throwing.

6. The "last method" cannot be invoked except through the use of a
    "delete block expr". Beyond this, it is an ordinary method; unlike
    a destructor, it can throw with no ill effects.

7. It is probably advisable to consider this

    T x = delete { C c1; C c2; ... }

    an error: having multiple "last method" candidates might be
    confusing.

This addresses the "file close" use case, but also the Builder GoF
pattern: the "last method" would return the object being built, or
throw if unable to.

The compiler would guarantee that the last method is called at most
once in the lifetime of an instance: the implementer of a class would
not have to worry about a number of things, e.g. in a Builder I would
not have to worry about the object being built being requested more
than once with additional build methods being invoked in between.

Davide Bolcioni
--
There is no place like /home.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: technews@kangaroologic.com ("Jonathan Turkanis")
Date: Sun, 12 Sep 2004 16:55:59 GMT
Raw View
"Davide Bolcioni" <f5xz2bk02@sneakemail.com> wrote in message
news:uu0b12-kg2.ln1@interbusiness.it...
> Greetings,
> having again faced the conundrum of close() of a file descriptor which
> can return an error code, the following occurred to me.

Would you describe the conundrum, please?

Jonathan


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: AlbertoBarbati@libero.it (Alberto Barbati)
Date: Mon, 13 Sep 2004 11:07:18 GMT
Raw View
Davide Bolcioni wrote:
> Greetings,
> having again faced the conundrum of close() of a file descriptor which
> can return an error code, the following occurred to me.
>

So, in practice, you want to have destructors to return values. While
your desire is noble and your attempt to solve the problem is
appreciable, I believe that the cost/value ratio of your solution is
unfavourable. Let's compare your proposed syntax:

   T x = delete { C c; /* ... */ };       // (1)

with the following idiom, currently valid in C++:

   T x; { C c; /* ... */ x = c.last(); }  // (2)

Syntax (1) has the following advantages:

* consistent naming of the "last function"

* it's impossible to forget to call the last function

and the following disadvantages:

* the presence of the keyword delete is very counter-intuitive. The
meaning of delete is quite radicated and immediately makes me think "I
am going to explicitly destroy and deallocate a dynamically allocated
object". No dynamic allocation is involved here and the destructor would
be called anyway.

* understanding the object for which the "last function" is called
requires a bit of practice.

* the observation you make at point 7 about blocks like "delete { C c1;
C c2; ... }" is a serious annoyance, whether you allow them (code would
be confusing) or disallow them (too restrictive, IMHO).

OTOH, idiom (2) has the following advantages:

* call to the last method is explicit, the programmer's intent is
expressed more clearly, in a way that is already familiar to the reader.

* no restrictions on the number of "last methods". Why should a class
have only one "last method"?

and the following disadvantages:

* you may forget to call the "last method". However, unless T is an UDT
with a default ctor, the compiler may be able to detect that x is
declared/used without having being assigned a value.

* can't be used if T is an UDT without default ctor.

Just my 2 eurocent,

Alberto

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]