Support compilation with exceptions disabled

This commit is contained in:
Victor Zverovich 2014-10-06 08:30:55 -07:00
parent 55c61d1abd
commit 8b76e97062
2 changed files with 79 additions and 41 deletions

110
format.cc
View File

@ -54,6 +54,33 @@ using fmt::LongLong;
using fmt::ULongLong; using fmt::ULongLong;
using fmt::internal::Arg; using fmt::internal::Arg;
// Check if exceptions are disabled.
#if __GNUC__ && !__EXCEPTIONS
# define FMT_EXCEPTIONS 0
#endif
#if _MSC_VER && !_HAS_EXCEPTIONS
# define FMT_EXCEPTIONS 0
#endif
#ifndef FMT_EXCEPTIONS
# define FMT_EXCEPTIONS 1
#endif
#if FMT_EXCEPTIONS
# define FMT_TRY try
# define FMT_CATCH(x) catch (x)
#else
# define FMT_TRY if (true)
# define FMT_CATCH(x) if (false)
#endif
#ifndef FMT_THROW
# if FMT_EXCEPTIONS
# define FMT_THROW(x) throw x
# else
# define FMT_THROW(x) assert(false)
# endif
#endif
#if _MSC_VER #if _MSC_VER
# pragma warning(push) # pragma warning(push)
# pragma warning(disable: 4127) // conditional expression is constant # pragma warning(disable: 4127) // conditional expression is constant
@ -185,14 +212,15 @@ int parse_nonnegative_int(const Char *&s) {
value = new_value; value = new_value;
} while ('0' <= *s && *s <= '9'); } while ('0' <= *s && *s <= '9');
if (value > INT_MAX) if (value > INT_MAX)
throw fmt::FormatError("number is too big"); FMT_THROW(fmt::FormatError("number is too big"));
return value; return value;
} }
inline void require_numeric_argument(const Arg &arg, char spec) { inline void require_numeric_argument(const Arg &arg, char spec) {
if (arg.type > Arg::LAST_NUMERIC_TYPE) { if (arg.type > Arg::LAST_NUMERIC_TYPE) {
throw fmt::FormatError( std::string message =
fmt::format("format specifier '{}' requires numeric argument", spec)); fmt::format("format specifier '{}' requires numeric argument", spec);
FMT_THROW(fmt::FormatError(message));
} }
} }
@ -201,8 +229,8 @@ void check_sign(const Char *&s, const Arg &arg) {
char sign = static_cast<char>(*s); char sign = static_cast<char>(*s);
require_numeric_argument(arg, sign); require_numeric_argument(arg, sign);
if (arg.type == Arg::UINT || arg.type == Arg::ULONG_LONG) { if (arg.type == Arg::UINT || arg.type == Arg::ULONG_LONG) {
throw fmt::FormatError(fmt::format( FMT_THROW(fmt::FormatError(fmt::format(
"format specifier '{}' requires signed argument", sign)); "format specifier '{}' requires signed argument", sign)));
} }
++s; ++s;
} }
@ -217,7 +245,7 @@ class WidthHandler : public fmt::internal::ArgVisitor<WidthHandler, unsigned> {
explicit WidthHandler(fmt::FormatSpec &spec) : spec_(spec) {} explicit WidthHandler(fmt::FormatSpec &spec) : spec_(spec) {}
unsigned visit_unhandled_arg() { unsigned visit_unhandled_arg() {
throw fmt::FormatError("width is not integer"); FMT_THROW(fmt::FormatError("width is not integer"));
} }
template <typename T> template <typename T>
@ -229,7 +257,7 @@ class WidthHandler : public fmt::internal::ArgVisitor<WidthHandler, unsigned> {
width = 0 - width; width = 0 - width;
} }
if (width > INT_MAX) if (width > INT_MAX)
throw fmt::FormatError("number is too big"); FMT_THROW(fmt::FormatError("number is too big"));
return static_cast<unsigned>(width); return static_cast<unsigned>(width);
} }
}; };
@ -238,13 +266,13 @@ class PrecisionHandler :
public fmt::internal::ArgVisitor<PrecisionHandler, int> { public fmt::internal::ArgVisitor<PrecisionHandler, int> {
public: public:
unsigned visit_unhandled_arg() { unsigned visit_unhandled_arg() {
throw fmt::FormatError("precision is not integer"); FMT_THROW(fmt::FormatError("precision is not integer"));
} }
template <typename T> template <typename T>
int visit_any_int(T value) { int visit_any_int(T value) {
if (!IntChecker<std::numeric_limits<T>::is_signed>::fits_in_int(value)) if (!IntChecker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
throw fmt::FormatError("number is too big"); FMT_THROW(fmt::FormatError("number is too big"));
return static_cast<int>(value); return static_cast<int>(value);
} }
}; };
@ -385,12 +413,12 @@ const uint64_t fmt::internal::POWERS_OF_10_64[] = {
void fmt::internal::report_unknown_type(char code, const char *type) { void fmt::internal::report_unknown_type(char code, const char *type) {
if (std::isprint(static_cast<unsigned char>(code))) { if (std::isprint(static_cast<unsigned char>(code))) {
throw fmt::FormatError( FMT_THROW(fmt::FormatError(
fmt::format("unknown format code '{}' for {}", code, type)); fmt::format("unknown format code '{}' for {}", code, type)));
} }
throw fmt::FormatError( FMT_THROW(fmt::FormatError(
fmt::format("unknown format code '\\x{:02x}' for {}", fmt::format("unknown format code '\\x{:02x}' for {}",
static_cast<unsigned>(code), type)); static_cast<unsigned>(code), type)));
} }
#ifdef _WIN32 #ifdef _WIN32
@ -400,18 +428,18 @@ fmt::internal::UTF8ToUTF16::UTF8ToUTF16(fmt::StringRef s) {
CP_UTF8, MB_ERR_INVALID_CHARS, s.c_str(), -1, 0, 0); CP_UTF8, MB_ERR_INVALID_CHARS, s.c_str(), -1, 0, 0);
static const char ERROR[] = "cannot convert string from UTF-8 to UTF-16"; static const char ERROR[] = "cannot convert string from UTF-8 to UTF-16";
if (length == 0) if (length == 0)
throw WindowsError(GetLastError(), ERROR); FMT_THROW(WindowsError(GetLastError(), ERROR));
buffer_.resize(length); buffer_.resize(length);
length = MultiByteToWideChar( length = MultiByteToWideChar(
CP_UTF8, MB_ERR_INVALID_CHARS, s.c_str(), -1, &buffer_[0], length); CP_UTF8, MB_ERR_INVALID_CHARS, s.c_str(), -1, &buffer_[0], length);
if (length == 0) if (length == 0)
throw WindowsError(GetLastError(), ERROR); FMT_THROW(WindowsError(GetLastError(), ERROR));
} }
fmt::internal::UTF16ToUTF8::UTF16ToUTF8(fmt::WStringRef s) { fmt::internal::UTF16ToUTF8::UTF16ToUTF8(fmt::WStringRef s) {
if (int error_code = convert(s)) { if (int error_code = convert(s)) {
throw WindowsError(error_code, FMT_THROW(WindowsError(error_code,
"cannot convert string from UTF-16 to UTF-8"); "cannot convert string from UTF-16 to UTF-8"));
} }
} }
@ -441,7 +469,7 @@ void fmt::WindowsError::init(
void fmt::internal::format_system_error( void fmt::internal::format_system_error(
fmt::Writer &out, int error_code, fmt::Writer &out, int error_code,
fmt::StringRef message) FMT_NOEXCEPT(true) { fmt::StringRef message) FMT_NOEXCEPT(true) {
try { FMT_TRY {
MemoryBuffer<char, INLINE_BUFFER_SIZE> buffer; MemoryBuffer<char, INLINE_BUFFER_SIZE> buffer;
buffer.resize(INLINE_BUFFER_SIZE); buffer.resize(INLINE_BUFFER_SIZE);
char *system_message = 0; char *system_message = 0;
@ -456,7 +484,7 @@ void fmt::internal::format_system_error(
break; // Can't get error message, report error code instead. break; // Can't get error message, report error code instead.
buffer.resize(buffer.size() * 2); buffer.resize(buffer.size() * 2);
} }
} catch (...) {} } FMT_CATCH(...) {}
format_error_code(out, error_code, message); format_error_code(out, error_code, message);
} }
@ -474,7 +502,7 @@ void fmt::internal::format_windows_error(
LPWSTR *ptr() { return &str_; } LPWSTR *ptr() { return &str_; }
LPCWSTR c_str() const { return str_; } LPCWSTR c_str() const { return str_; }
}; };
try { FMT_TRY {
String system_message; String system_message;
if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0,
@ -486,7 +514,7 @@ void fmt::internal::format_windows_error(
return; return;
} }
} }
} catch (...) {} } FMT_CATCH(...) {}
format_error_code(out, error_code, message); format_error_code(out, error_code, message);
} }
#endif #endif
@ -519,7 +547,7 @@ class fmt::internal::ArgFormatter :
return; return;
} }
if (spec_.align_ == ALIGN_NUMERIC || spec_.flags_ != 0) if (spec_.align_ == ALIGN_NUMERIC || spec_.flags_ != 0)
throw FormatError("invalid format specifier for char"); FMT_THROW(FormatError("invalid format specifier for char"));
typedef typename fmt::BasicWriter<Char>::CharPtr CharPtr; typedef typename fmt::BasicWriter<Char>::CharPtr CharPtr;
CharPtr out = CharPtr(); CharPtr out = CharPtr();
if (spec_.width_ > 1) { if (spec_.width_ > 1) {
@ -571,7 +599,7 @@ void fmt::BasicWriter<Char>::write_str(
std::size_t size = str.size; std::size_t size = str.size;
if (size == 0) { if (size == 0) {
if (!s) if (!s)
throw FormatError("string pointer is null"); FMT_THROW(FormatError("string pointer is null"));
if (*s) if (*s)
size = std::char_traits<StrChar>::length(s); size = std::char_traits<StrChar>::length(s);
} }
@ -583,8 +611,10 @@ inline Arg fmt::BasicFormatter<Char>::parse_arg_index(const Char *&s) {
const char *error = 0; const char *error = 0;
Arg arg = *s < '0' || *s > '9' ? Arg arg = *s < '0' || *s > '9' ?
next_arg(error) : get_arg(parse_nonnegative_int(s), error); next_arg(error) : get_arg(parse_nonnegative_int(s), error);
if (error) if (error) {
throw FormatError(*s != '}' && *s != ':' ? "invalid format string" : error); FMT_THROW(FormatError(
*s != '}' && *s != ':' ? "invalid format string" : error));
}
return arg; return arg;
} }
@ -647,7 +677,7 @@ Arg fmt::internal::PrintfFormatter<Char>::get_arg(
Arg arg = arg_index == UINT_MAX ? Arg arg = arg_index == UINT_MAX ?
next_arg(error) : FormatterBase::get_arg(arg_index - 1, error); next_arg(error) : FormatterBase::get_arg(arg_index - 1, error);
if (error) if (error)
throw FormatError(!*s ? "invalid format string" : error); FMT_THROW(FormatError(!*s ? "invalid format string" : error));
return arg; return arg;
} }
@ -763,7 +793,7 @@ void fmt::internal::PrintfFormatter<Char>::format(
// Parse type. // Parse type.
if (!*s) if (!*s)
throw FormatError("invalid format string"); FMT_THROW(FormatError("invalid format string"));
spec.type_ = static_cast<char>(*s++); spec.type_ = static_cast<char>(*s++);
if (arg.type <= Arg::LAST_INTEGER_TYPE) { if (arg.type <= Arg::LAST_INTEGER_TYPE) {
// Normalize type. // Normalize type.
@ -887,7 +917,7 @@ const Char *fmt::BasicFormatter<Char>::format(
if (p != s) { if (p != s) {
if (c == '}') break; if (c == '}') break;
if (c == '{') if (c == '{')
throw FormatError("invalid fill character '{'"); FMT_THROW(FormatError("invalid fill character '{'"));
s += 2; s += 2;
spec.fill_ = c; spec.fill_ = c;
} else ++s; } else ++s;
@ -931,7 +961,7 @@ const Char *fmt::BasicFormatter<Char>::format(
// and more efficient than checking if the next char is a digit. // and more efficient than checking if the next char is a digit.
spec.width_ = parse_nonnegative_int(s); spec.width_ = parse_nonnegative_int(s);
if (error) if (error)
throw FormatError(error); FMT_THROW(FormatError(error));
} }
// Parse precision. // Parse precision.
@ -941,17 +971,17 @@ const Char *fmt::BasicFormatter<Char>::format(
if ('0' <= *s && *s <= '9') { if ('0' <= *s && *s <= '9') {
spec.precision_ = parse_nonnegative_int(s); spec.precision_ = parse_nonnegative_int(s);
if (error) if (error)
throw FormatError(error); FMT_THROW(FormatError(error));
} else if (*s == '{') { } else if (*s == '{') {
++s; ++s;
const Arg &precision_arg = parse_arg_index(s); const Arg &precision_arg = parse_arg_index(s);
if (*s++ != '}') if (*s++ != '}')
throw FormatError("invalid format string"); FMT_THROW(FormatError("invalid format string"));
ULongLong value = 0; ULongLong value = 0;
switch (precision_arg.type) { switch (precision_arg.type) {
case Arg::INT: case Arg::INT:
if (precision_arg.int_value < 0) if (precision_arg.int_value < 0)
throw FormatError("negative precision"); FMT_THROW(FormatError("negative precision"));
value = precision_arg.int_value; value = precision_arg.int_value;
break; break;
case Arg::UINT: case Arg::UINT:
@ -959,24 +989,24 @@ const Char *fmt::BasicFormatter<Char>::format(
break; break;
case Arg::LONG_LONG: case Arg::LONG_LONG:
if (precision_arg.long_long_value < 0) if (precision_arg.long_long_value < 0)
throw FormatError("negative precision"); FMT_THROW(FormatError("negative precision"));
value = precision_arg.long_long_value; value = precision_arg.long_long_value;
break; break;
case Arg::ULONG_LONG: case Arg::ULONG_LONG:
value = precision_arg.ulong_long_value; value = precision_arg.ulong_long_value;
break; break;
default: default:
throw FormatError("precision is not integer"); FMT_THROW(FormatError("precision is not integer"));
} }
if (value > INT_MAX) if (value > INT_MAX)
throw FormatError("number is too big"); FMT_THROW(FormatError("number is too big"));
spec.precision_ = static_cast<int>(value); spec.precision_ = static_cast<int>(value);
} else { } else {
throw FormatError("missing precision specifier"); FMT_THROW(FormatError("missing precision specifier"));
} }
if (arg.type != Arg::DOUBLE && arg.type != Arg::LONG_DOUBLE) { if (arg.type != Arg::DOUBLE && arg.type != Arg::LONG_DOUBLE) {
throw FormatError( FMT_THROW(FormatError(
"precision specifier requires floating-point argument"); "precision specifier requires floating-point argument"));
} }
} }
@ -986,7 +1016,7 @@ const Char *fmt::BasicFormatter<Char>::format(
} }
if (*s++ != '}') if (*s++ != '}')
throw FormatError("missing '}' in format string"); FMT_THROW(FormatError("missing '}' in format string"));
start_ = s; start_ = s;
// Format argument. // Format argument.
@ -1008,7 +1038,7 @@ void fmt::BasicFormatter<Char>::format(
continue; continue;
} }
if (c == '}') if (c == '}')
throw FormatError("unmatched '}' in format string"); FMT_THROW(FormatError("unmatched '}' in format string"));
write(writer_, start_, s - 1); write(writer_, start_, s - 1);
Arg arg = parse_arg_index(s); Arg arg = parse_arg_index(s);
s = format(s, arg); s = format(s, arg);

View File

@ -58,6 +58,14 @@ if (HAVE_OPEN)
add_test(posix-test posix-test) add_test(posix-test posix-test)
endif () endif ()
# Test that the library can be compiled with exceptions disabled.
check_cxx_compiler_flag(-fno-exceptions HAVE_FNO_EXCEPTIONS_FLAG)
if (HAVE_FNO_EXCEPTIONS_FLAG)
add_library(noexception-test ../format.cc)
set_target_properties(noexception-test
PROPERTIES COMPILE_FLAGS -fno-exceptions)
endif ()
add_test(compile-test ${CMAKE_CTEST_COMMAND} add_test(compile-test ${CMAKE_CTEST_COMMAND}
--build-and-test --build-and-test
"${CMAKE_CURRENT_SOURCE_DIR}/compile-test" "${CMAKE_CURRENT_SOURCE_DIR}/compile-test"