I assume you are right about C++ exceptions (the little C++ I do doesn't use them). Modern managed languages incorporate the stack trace in the exception (including Java).
I'm not sure how you propose to have both return code and error message in C++, return a struct with an error and a message? This also does not answer my points 2 and 3 about handling errors occurring at different layers and compile-time checking.
Errors can be handled at different layers (using normal control flow the same way as I already demonstrated -- simply writing ifs) and the error declaration can be checked at compile time in C++ exactly as C++ has (who would have thought that!) classes. Java also uses class infrastructure for that, exceptions are only "out of normal flow path" mechanism.
import com.foo.SomeOtherException;
public class MyException extends SomeOtherException {
public MyException(String message) {
super(message);
}
}
That's about as complicated as it gets (considering the example extends a custom exception instead of inheriting from java.lang.Exception). I'll note that your example doesn't declare any return code anywhere. Now, I'm curious to know how you would do the equivalent of this with error codes: public class MigrationException extends Exception {
public MigrationException(String msg) { super(msg); }
}
public class SqlException extends Exception (
public SqlException(String msg) { super (msg); }
}
public class Migration {
public void migrate(Statement stat, int migrationNumber)
throws MigrationException, SqlException{
if (migrationNumber <= 5) throw new MigrationException("Wrong migration number " + migrationNumber, should be > 5");
// throws SqlException
stat.executeNonQuery("INSERT foo INTO bar");
}
}
public class MigrationRunner {
public void run(Statement stat, Migration migration, int migrationNumber) {
try {
migration.migrate(stat, migrationNumber);
}
catch(MigrationException ex) {
migrationLogger.error("Bad migration", ex);
}
catch(SqlException ex) {
migrationLogger.error("DB error", ex);
sqlLogger.error("Error during migration", ex);
}
}
}
I'm on purpose not reimplementing an SQL driver here :)I'm not quite sure why you are arguing about "error declarations which can be checked at compile time in C++". Sure, C++ has exceptions, but I thought you were trying to make a point about return codes, not C++ exceptions vs language X exception. My point, on the other hand, is that it's very easy to do this:
#define SQL_ERROR 1
// whoops, same error code due to copy-paste
#define MIGRATION_ERROR 1
void handleError(int errorCode) {
// Too bad, this was actually an SQL error
if (errorCode == MIGRATION_ERROR) {
...
}
else {
// We'll never enter this branch
...
}
}
No compile-time check for that. Or even: #define MIGRATION_ERROR 1
#define SQL_ERROR 2
void handleError(int errorCode) {
// I actually meant 2 here
if (errorCode == 1) {
logSqlError();
}
}