Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question about fmt::format_error with -fno-exceptions #2146

Closed
alexezeder opened this issue Feb 23, 2021 · 3 comments
Closed

Question about fmt::format_error with -fno-exceptions #2146

alexezeder opened this issue Feb 23, 2021 · 3 comments

Comments

@alexezeder
Copy link
Contributor

First of all, I need to say that I have close to zero experience with C++ without exceptions (-fno-exceptions), so I may be somehow wrong here. Also, I want to note that currently {fmt} should not be used with exceptions disabled (in my opinion) because it does not provide a proper error handling interface, like error_code or result<T>/outcome. But we have what we have.

Looking at #2145, I realized that there is no need to derive format_error from std::runtime_error when exceptions are disabled. Of course, we are able to do this, but std exception classes are kinda heavy even in this case. Maybe some custom class that provides constructor (only const char* needed) and what() method is enough.

Here is a simplified example on Compiler Explorer, and sizes are 190kB for derived exception vs. 44kB for custom exception. I also prepared a benchmark for that example on QuickBench. But this benchmark can only show that there is no performance regression for correct usages and that incorrect usages with custom exception results in faster program failure, nothing interesting.

Is it worth changing format_error that way?

@NobodyXu
Copy link
Contributor

I would say it is better to completely avoid use of any std::exception at all.

Use of any class derived from std::exception requires compiler to pull in at least 3 symbols from c++ standard library:

  • ctor
  • dtor
  • e.what()

Using library functions takes more space in .dynsym and also hinders potential compiler optimization, so I think it's better to do it without use of std::exception at all.

@NobodyXu
Copy link
Contributor

NobodyXu commented Feb 24, 2021

It will be nice to have something like

enum class Error_type {
    format_error,
    internal_bug,
    // ...
};

#ifdef  FMT_HAS_EXCEPTION
namespace detail {
[[noreturn]] void do_throw(Error_type error_type, int line, const char *func, const char *file, const char *msg, const chr *fmt = "\"\"")
{
    switch (error_type) {
        case format_error:
            throw format_error{line, file, func, msg, fmt};
        case internal_bug:
            throw internal_bug{line, file, func, msg};
    }
}
} /* namespace detail */
# define FMT_ERROR(error_type, msg, ...) \
    detail::do_throw((error_type), __LINE__, __PRETTY_FUNCTION__, __FILE__, (msg), ## __VA_ARGS__)

#else /* Exception is disabled */
template <Error_type error_type>
struct error_handler_t {
    [[noreturn]] void operator () (int line, const char *func, const char *file, const char *msg, const chr *fmt = "\"\"") noexcept
    {
        switch (error_type) {
            case format_error:
                std::fprintf(stderr, "In %d of %s, %s, failed to format %s: %s", line, func, file, fmt, msg);
                std::terminate();
            case internal_bug:
                std::fprintf(stderr, "Internal bug in %d of %s, %s: %s", line, func, file, msg);
                std::terminate();
        }
    }
};

# define FMT_ERROR(error_type, msg, ...)                                                                                  \
    do {                                                                                                                                             \
        error_handler_t<(error_type)> error_handler;                                                                        \
        error_handler(__LINE__, __PRETTY_FUNCTION__, __FILE__, (msg), ## __VA_ARGS); \
    } while (0);

#endif

@vitaut
Copy link
Contributor

vitaut commented Feb 25, 2021

Is it worth changing format_error that way?

That's an interesting idea but I'd rather not do it unless folks who are using -fno-exceptions confirm that it actually helps on their platforms. It might as well be that on the platforms where it matters the overhead is much smaller, e.g. I had reports of people being able to compile {fmt} into 30K.

@vitaut vitaut closed this as completed Feb 25, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants