c++templatesc++17if-constexpr

How can a constructor signal to caller, whether an exception it is throwing is fatal?


I'm dealing with an application, that needs to read in 10+ CSV-files (of different kind) as input. The data is read into a container -- std::map or vector.

Previously each kind had its own parsing function, and I'm working on unifying that into a single templatized function: to have save on future code-maintenance and provide uniform error-reporting for broken files.

This function reads each line, discerns, whether the container type has the concept of a key (like map) and uses emplace for those, and emplace_back for the others (like vector).

The only expectation from the value class of the container is that its constructor can instantiate from a CSV-line. Any exception by the constructor is a fatal error -- the input filename and line-number are reported, and the program exits:

try {
        if constexpr (is_associative_container<Container>(NULL)) {
                result->emplace(typename Container::key_type(
                    key, keylen), value);
        } else {
                result->emplace_back(value);
        }
} catch (const std::exception &e) {
        fprintf(stderr, "%s:%zd: %s\n", path, line, e.what());
        goto failure;
}

This all works and I'm happy -- about 75% of the CSV-parsing is now done by this function.

I'm now facing the remaining quarter of the CSV-files, which are less straightforward: because certain rows in them require special treatment and their content isn't supposed to be stored in the container.

How can the value_type's constructor signal to the function, that the exception it is throwing should not considered fatal? One way is to pick one of the standard exceptions (std::bad_function_call?) as a signal, but that'd mean, the picked exception mustn't occur unexpectedly -- which is unreliable...

Anything else?


Solution

  • A question edit overlapped with writing this answer. The original answer is below.

    How can the value_type's constructor signal to the function, that the exception it is throwing should not considered fatal?

    Note that in your current code you already do make a distinction between exceptions inherited from std::exception and those that do not inherit from std::exception. Good style is to inherit all exceptions from std::exception but reality is usually different.

    You could introduce some special type of exception to be thrown:

                try {
                     //...
                } catch (const non_fatal_exception &e) {
                        // do something
                } catch (...) { // all other exceptions are "fatal"
                        fprintf(stderr, "%s:%zd: %s\n", path, line, e.what());
                        goto failure;
                }
    

    Old answer about dynamic exception specifications...

    As mentiond in comments, dynamic exception specifications are removed from C++17.

    Before C++17 a function that did throw an exception not listed in the exception specification did the following (from cppreference):

    If the function throws an exception of the type not listed in its exception specification, the function std::unexpected is called. The default function calls std::terminate, but it may be replaced by a user-provided function (via std::set_unexpected) which may call std::terminate or throw an exception. If the exception thrown from std::unexpected is accepted by the exception specification, stack unwinding continues as usual. If it isn't, but std::bad_exception is allowed by the exception specification, std::bad_exception is thrown. Otherwise, std::terminate is called.

    There is no way out unless you know some exception that would be accepted from the exception specification, but in general you do not know that. I am not aware of deducing the "allowed" exceptions in generic code. Anyhow, the feature is removed. And in some sense it was already "fatal" to throw an exception not listed in the exception specification, its not something you have to do extra.