"David B. Held" <[EMAIL PROTECTED]> writes: > "David Abrahams" <[EMAIL PROTECTED]> wrote in message > [EMAIL PROTECTED]">news:[EMAIL PROTECTED]... >> [...] >> Which is why the first "I" in RAII stands for "is". Each acquired >> resource should initialize exactly one (sub)object. >> >> > Or acquiring a resource in any other context when members can >> > throw will have the same problem. >> >> I can't picture what you mean. > > foo::foo(resource const& r) > : r_(r), my_data_member_whose_ctor_can_throw() > { } > > Here, r_ can't tell if foo is being destructed, or just r_, and that > could be a very important difference.
It usually isn't. I'm tempted to assert that it shouldn't be. When does that distinction ever matter? > Even if only one (sub)object is being initialized with a resource, I don't think I ever suggested that should be the case. However, one (sub)object should have responsibility for managing the resource at any given time. > you may still require other possibly throwing members in your > object. I don't see a problem with that. >> > but that seems draconian. So what is the real solution? >> >> To this particular problem? I'd say "redesign or respecify". That >> is, move the design or move the goalposts. > > I'd rather change the rulebook. ;) That's one approach which guarantees no practical solution in the next few years ;-) >> [...] >> It's very easy to blame the language for not allowing us to easily >> implement DWIM semantics, > DWIM == "Do What I Mean." My sense was that you weren't yet sure what you really wanted, and that if you wrote it down carefully at least one of the two following things would become apparent: 1. How to do it in the current language 2. That if you changed C++ to make it adjust for this case it would undermine important guarantees the language currently provides. Please excuse the brashness of this interpretation; it's just my intuitive sense. >> especially when we're not sure exactly what those semantics should >> be. Have you got a clear idea of what should happen during >> construction if an exception is thrown? Can you write it as >> pseudocode? > > I think I did later on. Allow users to define an alternative > destructor for the case of member destruction during a constructor > call. No, no, that's not what I meant! I meant, "do you know what behavior you want from your smart_ptr design?" Have you specified it carefully? >> The language gives us a very coherent mechanism for dividing up >> the units of work that must get done when a constructor throws an >> exception. See page 53 of "more effective C++" >> [...] Any "fix" to the language will only be an improvement if it doesn't >> undermine that coherence. > > That mechanism only works with a trivial acquire/release pattern. In > particular, the pattern when you acquire in the c'tor and release in the > d'tor. You can always apply that pattern. You just need to slice and dice your members and bases in such a way as to make it possible. > While that works quite well for a great number of uses, it seems > unlikely to me that smart pointers are the only situation in which you > need to do cleanup someplace besides the d'tor. I think you're getting trapped by those last 2 words: "the d'tor". You can always arrange for cleanup to happen in _a_ d'tor. Indeed some people make a justifiable claim that cleanup should never happen in catch blocks in portable code. So many real systems wind "asynchronous" non-C++ exceptions into the C++ EH mechanism that catch(...) is often not safe in practice. > And when that cleanup cannot get activated because of an exceptional > path, we need some assistance from the compiler to tell us that the > exception occurred. I still don't buy this, but I'm willing to be convinced. >> [...] >> One language change I could imagine which would help would be >> the ability to declare an auto variable in the class' initializer list: >> >> template<class P, class D> shared_count(P p, D d) >> : auto deleter<P,D> del(d, p) >> , pi_(new sp_counted_base_impl<P, D>(p, d)) >> { >> del.release(); >> } > > Like I said, this case is already covered by function try blocks, so I > don't think it needs to be resolved. I agree that the case above is probably equivalent to something you can do with a function-try-block, however I envisioned being able to re-use the variable during initialization, or declare it somewhere in the middle of base or member initialization. That's a bit different. But anyway, I only got started down this road of considering language extensions as a way to find a solution in the current language, which I hope it led me to... >> [...] >> So, does that help with your problem? > > Hmm...I don't like having to create an impl base I think you're now referring to my reply to Andrei which you don't cite here (the one with pb_impl)? > but if I have to specialize for move semantics anyway, I might be > able to kill two birds with one stone. Don't know what that's about, but I'm not sure I need to. > It's interesting that your trick uses a kind of negative logic. It > holds the axe over the neck of the resource until the c'tor body > yells: "Don't do it!" I don't think this is a new idea. It's a simple for of transfer-of-ownership. > >> [...] >> > One syntax would be to pass a dummy int parameter to a normal >> > destructor, like so: >> > >> > scalar_storage::scalar_storage() >> > { >> > // normal destruction, do nothing >> > } >> > >> > scalar_storage::~scalar_storage(int) >> > { >> > // something bad happened, clean up with extreme prejudice >> > destroy(); >> > } >> >> That would mean the usual destructor wouldn't be called in case of an >> exception? > > Correct. I think this breaks what is a very clean model, and I'm tempted to assert that classes which would take advantage of this are badly designed. Why should scalar_storage be destroyed differently based on whether some arbitrary sibling member or derived class constructor throws an exception? <snip> >> Yikes! Very complicated! Probably misguided, as most things which >> distinguish different exception types near the throw point tend to be. > > I'm not really interested in distinguishing the exception types as much as > being able to call a different d'tor for exceptional cases. It just seems > that we might want to be able to call more than one type of d'tor, and > that seemed the most logical way to classify them. But all still cart-before-horse IMO. It still looks like you're redesigning the language without deeply understanding the problem you're trying to solve by doing so. I could be wrong about that, but it would sure help to see a precise description of what you want to happen in smart_ptr. > P.S. This isn't thread safe, but maybe this is a direction to think in: <snip> You clearly have policies-on-the-brain <wink> I can't tell which part of what you're doing here you think is worthy of note, but it seems like it has more problems than just thread safety, and I don't see it solving many problems... I could be wrong though. It wouldn't be the first time ;-) -- David Abrahams [EMAIL PROTECTED] * http://www.boost-consulting.com Boost support, enhancements, training, and commercial distribution _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost