Hacker News new | ask | show | jobs
by dgfgfdagasdfgfa 3289 days ago
RAII is incredibly attractive, especially if you disable exceptions.
2 comments

Why would RAII be especially attractive if disabling exceptions?
Probably because exceptions are the biggest source of pain with RAII.
I'd say that exceptions are the biggest source of pain if you don't have RAII.

Or, rephrased: RAII is incredibly attractive, especially when exceptions are being used.

RAII is necessary for exceptions. Why the hell would you do that to yourself?
How do you deal with constructors that might fail?
You don't have constructors that 'fail'.
So, allocating memory for objects is not part of construction? Again, why not just stick with C?
Memory for objects is allocated structurally as part of the object, not dynamically, wherever possible. And it's usually very possible. If you can't do that, provide an initialization method. (In almost all cases, I would personally prefer hiding that two-phase initialization within a factory function.)

As for "why not stick with C"--all of the other reasons still hold true, from templates on down. The simple existence of dtors with viable scope guards that are guaranteed to fire when exiting scope is reason enough for me to never write C and to look with a default skepticism on any codebase that thinks its developers are perfect enough not to need them.

> Again, why not just stick with C?

The thing that is hard to replicate in C is destructors. Automatic deinitialization when leaving scope in very convenient. It allows you to have multiple exists from the scope without preceding each of them with prologue of dinit_*() calls or creating single exit point and jumping to it.

Coupling allocation with initialization is trivial to do without C++ constructors (which I find to be very poorly designed).

C doesn't let you have destructors that automatically clean up whatever got initialised in the Init method (if it was called), but C++ does.
If you disable exceptions hoe do you handle failures in constructors?
I mean it's kind of a smartass answer, but—don't fail during constructors. Move all possible code that can fail into an initialize method; check explicitly for allocation failure and/or put things on the stack instead of heap when possible; consider failing hard with a stack trace or core dump over catching and processing exceptions before (likely) failing anyway.
Instead of using a constructor you can use a constructor method e.g.:

    class Foo {
       public:
          static std::optional<Foo> create();

       private:
          Foo();
    };
In general if you're not using exceptions, you're not going to be using features that haven't actually been published in a formal standard (optional).

This now means that you can't use any constructors, so how do you have Containers of foo?

> you're not going to be using features that haven't actually been published in a formal standard (optional).

So you then have things like:

  class Foo
  {
  public:
    static Foo* create();
    ...
  };
  ...
  Foo* foo = Foo::create()
  if ( foo != nullptr ) ...
> so how do you have Containers of foo?

std::vector<Foo*>

Not saying either of those are better than the alternative (I prefer using exceptions and RAII), just pointing out what I've seen in real world projects.

I don't think this is the proposal. The proposal is that the object contains a genuine constructor that only does the bare-bones "safe" stuff, and then it has a separate non-static method that does the might-fail initialization. So:

  class Foo
  {
  public:
    Foo();
    bool initialize();  // returns success
    ...
  };
  ...
  Foo foo;
  if ( !foo->initialize() ) { // handle error }
This also means you can break up your initialization so that you drive the risky pieces from outside the object, rather than monolithically from within.

This has a further benefit for testing, since you can use your major objects without fully initializing the entire world that they depend on.

It was almost the exact proposal specified by my grandparent post except using a pointer rather than an optional.

It's also a technique that is widely used. See for example the cocos2d-x game library.

The benefit of such a technique is that you can then make the constructor private, making it impossible to create an object and not also call the initialize() method.

My bad, I thought std::optional is part of C++14, it seems to be part of the next standard C++17, but there's still boost::optional.

About the container issue: if you have objects that might fail during the creation it seems like a bad idea to allow things like:

    std::vector<Foo> foos(10);
Having a separate initialization method which might fail - like proposed by others - is another option, but this means your objects need some kind of internal initialization state, and whenever you're handling such an object you never can be absolute sure that it's in a valid state.

I'm quite a big fan of making invalid state not representable in an object and handling failure cases as early as possible.

What the create method returns depends heavily on your use case. If the returned objects can always be allocated on the heap, then a pointer or unique_ptr can be returned.

How would you handle failure in copy constructors?
Most likely having an explicit copy method instead of a copy constructor.