7

Take C++ constructors for example, we know they don't return values. Why did Bjarne Stroustrup in the beginning decide not to allow constructor returning 'false' to indicate it fails, so that the run time system can destruct its constructed members just like throwing an exception? What are the concerns that make OO language designers decide not to do so? The "new" operator can therefore return "nullptr" if it see constructor returns false. For static objects (in .data/.bss area or on .stack) the compiler generated construction code can still detect and signal, abort or exit accordingly.

Consider the following two coding paradigms, using dynamic allocation as an example:

objp = new object; // constructor returns 'false', 'new' returns 'nullptr'.
if (objp != nullptr) {
    do_something(objp);
} else {
    error_handling();
}

compare to:

try {
    objp = new object; // object throw exception when construction failed
    do_something(objp);
} catch (const typedException &e) {
    error_handling();
}

If we need to encode the reason of construction failure, the latter using exception is of course more helpful as we can't use 'objp' to pass any information once construction failed. But if the reason is simple (say 'out of memory'), or when skipping error handling does no harm (do_something() might just do decorating), do we really need to involve exception handling in such simple cases? How about allowing both paradigms exist in C++?

(Another example is, for small embedded C++ compilers if they don't support exceptions, they can still support "construction fail" handling in this way.)

Well, maybe my comparison is misleading, I am not against structural exception handling, on the contrary, I LOVE exceptions especially for big systems. But when it comes to small embedded systems where both code and data space are scarce, I think twice.

The exception handling frames are things similar to setjmp() and longjmp() which are quite expensive in both execution time and memory use; while the (objp==nullptr) comparison takes only a comparison and a jump. Not to mention some of the compilers don't even support exception handling. In those cases, construction fail can only be dealt with other methods. That reminds me in the old days Turbo Pascal 5.5 OOP can call "Fail" on construction fail, and the newly allocated object will be null.

What about other languages? Does all OOP languages use exception on construction fail cases?

Actually, at the time I learnt C++ there is no exception handling available at all (Turbo C++ 3.0/Borland C++ 3.1). Before that I learnt Turbo Pascal 5.5 which supported "Fail" on dynamic construction fail. But when I move to C++ at that time this make me a bit upset since there is no way to test construction fail without defining an Init() function that actually do the inits. Since then I wondered, why can't constructors return values?

Now I think maybe returning a value from constructor will make the language "inconsistent". If we return a value from a constructor and the runtime system test it, this kind of behavior is a lot different from a normal function call since our code can test it. Maybe this kind of inconsistency is not a good idea when designing a programming language? What do you think?


In the early days of C++ there was no exception handling, yet Bjarne Stroustrup still didn't let constructors to return error conditions. Same did the other OO languages at that time (correct me if I am wrong). Therefore, using exception handling was not their original intention to take care of constructor fails. Then why? I think I found the answer, please refer to my own answer here. Thanks.

12
  • 1
    Possible duplicate of Is catching general exceptions really a bad thing?
    – Erik Eidt
    Oct 18, 2016 at 4:36
  • 4
    If you really need this construction pattern, write a Factory method. Oct 18, 2016 at 4:56
  • 1
    I was always under the impression that the constructor returned the instantiated object.
    – Pieter B
    Oct 18, 2016 at 6:33
  • 3
    @LukeLee: How would your proposal work for objects that are not allocated with new? For those objects, constructors also get called, but you won't have any pointer to them that could be tested. Oct 18, 2016 at 7:21
  • 1
    @LukeLee True, you can do that. But if you are ok with constructors of automatic objects just calling _exit(), why not do that generally? Then you don't need return values. Oct 18, 2016 at 9:10

7 Answers 7

14

Your question indicates a fundamental misunderstanding of the value of the exception mechanism. If the language in question didn't have exceptions, then of course, error codes would be the only way to go. But using error codes in a language that supports exceptions is not often the right answer.

Why?

As it turns out, very often, code really close to the source of a show stopping problem doesn't know what to do when that happens.

Since the code close to the error often doesn't know what to do, there is nothing for that code to do to handle the error except let someone else do that.

With error code returns, every caller must check for errors, even though there is usually nothing they can do but to further propagate the error to their callers (lather, rinse, repeat...)

Enter Exception Handling

Exception handling is a mechanism that allows a separation of the callees that have problems from truly capable error handlers, which are typically rather removed from the source of an error.

Good handlers know how to abort or retry the broader computation being performed, not simply log the error and propagate the error up the call chain. A sign of a good handler is that it doesn't need to propagate errors up the call chain because it handled them.

With exception handling, we typically don't surround individual method calls with exception handling, and that includes constructors and new operations.

Exception handling allows better definition of an exception handler, and at a considerable distance away from the source of the exception, and this is an advantage because such distance usually means a better understanding of what to do in case of errors.

(Exception handling also allows structuring errors, but we'll leave that for another discussion.)


If we haven't convinced you yet compare these code sequences:

objp = new object (); // constructor returns 'false', 'new' returns 'nullptr'.
if (objp != nullptr) {
    do_something(objp);
} else {
    error_handling();
}

with:

objp = new object (); // object throw exception when construction failed

The latter, using exceptions, is much cleaner and more appropriate to an arguable majority of use cases.

9
  • Well, I just edited my question. I am not here to criticize the value of exception handling. Back to my topic, I am just asking why constructor don't provide a simple mechanism to indicate its failure without too much overhead.
    – user249859
    Oct 18, 2016 at 5:46
  • What makes you think that throwing an exception isn't that simple, low overhead mechanism? Much of the work is statically diagnosed at compile time and put into tables that are never even consuled on success, which means that success cases actually avoid overhead compared to constant error code checking. I'm not saying it is a huge performancec gain, but it isn't the overhead you're painting it as.
    – Erik Eidt
    Oct 18, 2016 at 5:53
  • And why I consider this question as a duplicate is that it only makes sense to ask under the assumption that most exception throwing calls should be surrounded by try, i.e., should we catch general exceptions...
    – Erik Eidt
    Oct 18, 2016 at 6:08
  • I got that idea from here: monoinfinito.wordpress.com/series/exception-handling-in-c
    – user249859
    Oct 18, 2016 at 6:11
  • 2
    This exception stuff is like a fire in your room. If you can handle it, handle it. If you can't, give notice higher up the hierarchy. The fire fighters receive that call and if they can handle it, they handle it. Or maybe they realise that you called from a nuclear power plant, in which case they will give notice higher up the hierarchy… and so on and so forth.
    – null
    Oct 18, 2016 at 11:56
4

You certainly could define a language that worked this way, but it would end up being quite a different language than C++.

For example, in C++ a constructor can be invoked to create a temporary object in the process of evaluating an expression:

MyInteger a = 2;
MyInteger b = 3;
MyInteger c;
c = a + b;

When we do this, a + b creates a temporary object (presumably of type MyInteger). That object is then assigned to c.

If, however, the constructor returned a Boolean result, this wouldn't work out so well. a+b would (apparently) result in either true or false. Those would convert to 1 and 0 respectively, so the result of 2+3 would be either 0 or 1 (likewise, all other math would give results of 0 or 1).

There are ways you could eliminate this problem, of course. For example, you could just prohibit assignment of objects. With this restriction, you could write the preceding something like:

MyInteger a{2};
MyInteger b{3};
MyInteger c{a};
c.add(b);

As long as we're only adding two numbers, this isn't a major problem. In more complex expressions, however, it seems to me that the result would be unnatural and almost unbearably verbose. The programmer would be forced to explicitly create every temporary object, initialized from one of the input operands, and then modify it based on the other operand. For example a = (b+c)*(d/e); would end up something like:

MyInteger temp1{b};
temp1.add(c);
MyInteger temp2{d};
temp2.div(e);
temp1.mul(temp2);
MyInteger a{temp1};

It's certainly possible to do things like that. In fact, there's a language that has provided a similar capability (using similar syntax) for years:

 mov ebx, b
 add ebx, c
 mov eax, d
 xor edx, edx
 mov ecx, e
 div ecx
 mul eax, ebx

The intent with C++ was to move to a higher level of abstraction. This language you've invented goes in the opposite direction, reinventing assembly language, but with more verbose syntax.

Summary

What you're advocating could certainly be done. It would be fairly easy to design and implement. The result would be so clumsy that I (for one) find it hard to imagine that anybody would use it though.

1
  • I just posted my answer. Constructor return values was for "new" operator for dynamic objects, or the C++ runtime initialization code before main() function for static objects. However your simple MyInteger example do enlighten me for my answer. Thanks!
    – user249859
    Oct 19, 2016 at 3:08
2

Both approaches (exceptions or error codes) are already supported in these languages. To use the error code approach, just never throw any exceptions or call anything that might throw. Write constructors that do nothing except copy pure values into fields. Move any might-fail operations into separate methods.

In the bad old days Microsoft used to actually recommend this approach (possibly because their early C++ compiler didn't implement exception stack unwinding at all).

The challenge with this is that the "pure error code approach" is often impossible because almost anything you do in any of your code has some potential to throw. Anything which allocates memory, for example, which includes almost any operation on a string.

So really the choice is between:

  • dealing with exceptions and also checking returned error codes
  • just dealing with exceptions

If you have to deal with exceptions anyway, you might as well use them consistently.

The resource constraint argument is taken seriously by some communities, e.g. very constrained embedded environments. But that's a tiny niche. Phones used to be resource constrained, now most phone software is Java-based using GC, exceptions etc.

4
  • 1
    +1 about the old Microsoft compiler, indeed they didn't support good exception handling. Even C++ language itself at that time was not that well-defined.
    – user249859
    Oct 18, 2016 at 8:50
  • "Write constructors that do nothing except copy pure values into fields. Move any might-fail operations into separate methods." I was going to write this as its own answer. Constructors generally should never fail, which is why a return code would be redundant and an exception is reasonable for...ahem...exceptional cases.
    – plast1k
    Oct 19, 2016 at 16:18
  • @plast1k but that isn't really general advice. Certainly not across any of C++, Java, C#, where exception handling is the appropriate mechanism to use. Any operation, for which the implementation could not carry out the request, can throw. This means functions (including constructor calls) can be composed in expressions. Oct 21, 2016 at 17:22
  • except that if you "just deal with exceptions" you can't use many standard libraries, either (std::error_code, anyone?). And then there are things that throw non-exceptions, and things that return invalid results to indicate errors, and so on. Other things just become incredibly complex, like replacing snprintf("%-08x") (returns an error code) with a stream-based equivalent (that throws on error).
    – Móż
    Oct 26, 2016 at 2:38
0

The exception handling frames are things similar to setjmp() and longjmp() which are quite expensive in both execution time and memory use; while the (objp==nullptr) comparison takes only a comparison and a jump

They are a comparison and a jump in every case, including the success case. EH can be zero cost of an exception isn't thrown, so it can offer better time in the normal success case.

As Erik aptly pointed out, the whole point of exceptions is that you don't check for exception everywhere.

0

You can do this already with no language changes.

Add a bool& output parameter to each constructor, and set it instead of throwing exceptions.

You can allocate heap objects with nothrow new

But honestly you almost might as well be writing c at this point.

2
0

It happens (kind of) in Objective-C. Newly created objects are always on the heap, so the init method (kind of like the constructor in C++) returns a reference to the object. If the init method fails, it should return nil.

You can annotate an init method as "nonnull" which means it isn't allowed to return nil; in that case it is assumed that the init method never fails if it is given correct parameters, and that incorrect parameters are a programmer error, so if the init method wanted to return nil then that would be a programming error and an exception should be raised - in Objective C, exceptions kill the program in most situations (they are raised for programming errors, so you rarely try / catch them).

Of course returning nil or not nil is as good as a boolean parameter. That's what factory methods in C++ may also do; you call them, and you either get a pointer to a newly created object, or you get a null pointer.

-1

The terribly named language Go at http://golang.org offers multiple return values, the second often being an error Status. Instead of exceptions. Look also at defer for exception like catching. It probably would be suited for embedded Systems - my sole doubt is of strategic considerations.

3
  • There are plenty of other languages that also offer this, but this question is asking about C++ not other languages. Oct 18, 2016 at 17:29
  • @PeriataBreatta What about other languages, the OP asked. Though one might in C++ create ones own unstdlib. A comment on Go I did not feel sufficient, as Go has some provisions to not use exceptions
    – Joop Eggen
    Oct 18, 2016 at 19:55
  • +1 for providing information about languages other than C++.
    – user249859
    Oct 19, 2016 at 7:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.