Doc. no. N2415=07-0275
Date:
2007-09-09
Project: Programming Language C++
Reply to: Beman Dawes <bdawes@acm.org>
Christopher Kohlhoff <chris@kohlhoff.com>
Issue 1: Missing error_code::clear() specification
Issue 2: <system_error> should not force an implementation
to expose <cerrno> macros
Issue 3: <functional> hash specialization for error_code
Issue 4: Class error_code constructor should be explicit
Issue 5: Class error_code constructors should be constexpr
Issue 6: Class error_category should be non-copyable
Issue 7: Problems with posix_errno overloads
Issue 8: [Withdrawn]
Issue 9: EOTHER hijacks POSIX macro space, constant name
too general
Issue 10: Header dependencies are non-trivial
Issue 11: Overly complex error_code::value_type
with circular dependencies
Issue 12: Missing throw specifications
Issue 13: Missing system_error constructor
Issue 14: error_category equality via
address comparison
Issue 15: Extending error_code::value_types constants and
user-defined error_category objects
Issue 16: POSIX versus native error_categoryIssue 17: Broken
error_category::message
member functions
Issue 18: system_error::what and constructors
Issue 19: Should classes error_code and
error_category be less-than
comparable?
Issue 20: Which
error_category applies to POSIX-like
operating system errors unclear
Issue 21: Error enums add lots of names to namespace std
Issue 22: Out of memory should throw bad_alloc,
not system_error
Issue 23: error_category::name()
should return const char *
Issue 24: system_error
what-less constructors needed
Acknowledgements
This paper is a major revision of N2296, Diagnostics Enhancements; Resolution of Small Issues. The name has been changed to reflect the wider scope of several proposals.
The resolution of many of the issues are interrelated or modify overlapping sections of text in the standard. The proposed resolutions of these issues are combined in the proposed resolution wording for issue 7.
The proposed resolutions for many of these issues were drafted by Beman Dawes. The issue submitters do not necessarily agree with these PR's.
The clear() function is shown in the synopsis but is otherwise unspecified.
Reported separately by Pete Becker, PJ Plauger, and Benjamin Kosnik.
Specify the semantics. See proposed wording for issue 7.
In N2241, enum posix_errno did not specify values for each
enumeration constant. Instead, a table of equivalence was provided showing the
<cerrno> E* macro the constant was to be equivalent to. During
editing, that table was folded into the enum for brevity. That was a useful
change, but had the unintended side effect of making it look like <system_error>
was required to expose the <cerrno> macros, which was not the intent.
Submitted by PJ Plaguer
Change the form of enum posix_errno in 19.4 System error
support [syserr] as indicated:
enum posix_errno {... Apply same pattern of changes to all remaining constants for the enum.
address_family_not_supported= EAFNOSUPPORT, // EAFNOSUPPORT
address_in_use= EADDRINUSE, // EADDRINUSE
At the end of 19.4 System error support [syserr] add:
The value of each
enum posix_errnoconstant shall be the same as the value of the<cerrno>macro shown in the above synopsis.
Whether or not the <system_error> implementation actually exposes the <cerrno> macros is unspecified.
The member function hash_value should be replaced or supplemented by an
explicit specialization of template class hash (in <functional>) so people can
easily make an unordered container keyed on errors.
Submitted by PJ Plauger.
In 19.4.2.1 Class error_code overview [syserr.errcode.overview], removed:
size_t hash_value(const error_code& ec);
In 19.4.2.6 Class error_code non-member functions [syserr.errcode.nonmembers], remove:
size_t hash_value(const error_code& ec);
Returns: A hash value representing ec.
In 20.5 Function objects [function.objects], paragraph 2, Header <functional> synopsis, add:
template <> struct hash<std::error_code>;
Change 20.5.15 Class template hash [unord.hash] as indicated:
The unordered associative containers defined in clause 23.4 use specializations of hash as the default hash function. This class template is only required to be instantiable for integer types (3.9.1), floating point types (3.9.1), pointer types (8.3.1), and
std::string,std::u16string,std::u32string,andstd::wstring, andstd::error_code.
Submitted by Alisdair Meredith.
No action necessary.
The conversion constructor is important design element; without it error enum ease-of-use suffers badly.
Objects of class error_code will commonly be constructed in
very low-level systems code where efficiency is a critical consideration. Thus
static initialization is quite desirable, and would remove the runtime and
code size cost implicit in the change to enumeration
style error values.
A possible fix is to apply constexpr.
Submitted by Chris Kohlhoff and Beman Dawes.
Take no action at this time.
It doesn't appear at this time that constexpr works for the error_code constructors, because error_category isn't a literal type. Once an implementation of constexpr become available, it may be possible to develop a workaround, but until then no action should be taken.
Class error_category equality is stated in terms of address
comparison, taking advantage of the C++ language rule that no two objects can
have the same address, and thus avoiding the possibility that independently
created user-defined error categories could inadvertently compare equal.
Thus it makes no sense to allow error_category objects to be
copyable.
Submitted by Pete Becker.
Make error_category non-copyable. See proposed wording for issue 7.
The original Diagnostics proposal,
N2174, provided error_code constants for POSIX errors. The LWG
requested that these be changed to an enum, and enum posix_errno
was accordingly provided in the final proposal voted into the working paper.
Since the enums are replacing objects of type error_code, rather
than replacing simple integer constants, such a change has turned out to have
considerable fallout.
The wording in the WP included several overloads on posix_errno
introduced to make the enums workable. However, the introduced overloads have
several problems:
A solution to these problems must ensure:
ec == windows::access_denied.ec == windows::access_denied to test
for a system-specific error, but have to write ec.posix() ==
posix::permission_denied to get the portable equivalent, it is both
error prone and discourages portable code. Change 19.4 System error support [syserr] as indicated. Note that is_error_code_enum
and is_error_condition_enum will be eliminated when the
library is conceptized.
namespace std { class system_error; class error_code; class error_condition; class error_category; template< class T > struct is_error_code_enum { static const bool value = false; }; template< class T > struct is_error_condition_enum { static const bool value = false; }; namespace posix { enum posix_errno { address_family_not_supported = EAFNOSUPPORT, address_in_use = EADDRINUSE, ... value_too_large = EOVERFLOW, wrong_protocol_type = EPROTOTYPE }; }template<> struct is_error_condition_enum<posix::posix_errno> { static const bool value = true; };bool operator==(const error_code& lhs, const error_code& rhs); bool operator==(const error_code&eccode, posix_errnoenconst error_condition& condition); bool operator==(posix_errno enconst error_condition& condition, const error_code&eccode); bool operator==(const error_condition& lhs, const error_condition& rhs); bool operator!=(const error_code& lhs, const error_code& rhs); bool operator!=(const error_code&eccode, posix_errnoenconst error_condition& condition); bool operator!=(posix_errno enconst error_condition& condition, const error_code&eccode); bool operator!=(const error_condition& lhs, const error_condition& rhs); bool operator<(const error_code& lhs, const error_code& rhs); bool operator<(const error_condition& lhs, const error_condition& rhs); error_code make_error_code(posix::posix_errno e); error_condition make_error_condition(posix::posix_errno e); } // namespace stdUsers may specialize
is_error_code_enumandis_error_condition_enumtemplates to indicate that a type is eligible forclass error_codeanderror_conditionautomatic conversions respectively.
Change 19.4.1.1 Class error_category overview [syserr.errcat.overview] as indicated:
namespace std {
class error_category {
public:
virtual ~error_category();
error_category(const error_category&) = delete;
error_category& operator=(const error_category&) = delete;
virtual const string& name() const = 0;
virtual posix_errno posix(error_code::value_type ev) const = 0;
virtual error_condition default_error_condition(int ev) const;
virtual bool equivalent(int code, const error_condition & condition) const;
virtual bool equivalent(const error_code & code, int condition) const;
virtual string message(error_code::value_type ev) const = 0;
virtual wstring wmessage(error_code::value_type ev) const = 0;
bool operator==(const error_category& rhs) const;
bool operator!=(const error_category& rhs) const;
bool operator<( const error_category & rhs ) const;
};
extern const error_category& posix_category;
extern const error_category& native_category system_category;
} // namespace std
Change 19.4.1.2 Class error_category virtual members [syserr.errcat.virtuals] as indicated:
virtual posix_errno posix(error_code::value_type ev) const = 0;
virtual error_condition default_error_condition(int ev) const;Returns:
A value of typeposix_errnothat corresponds toevif such a corresponding POSIX error number exists, otherwiseothererror_condition(ev, *this).[ Note: Since the possible values ofevare not bounded, the intent is that implementations translate commonly encountered values to the equivalent POSIX error number and translate the rest to other. --end note ]Throws: Nothing.
virtual bool equivalent(int code, const error_condition & condition) const;Returns:
default_error_condition( code ) == condition.Throws: Nothing.
virtual bool equivalent(const error_code & code, int condition) const;Returns:
*this == code.category() && code.value() == condition.Throws: Nothing.
Add to 19.4.1.3 Class error_category non-virtual members [syserr.errcat.nonvirtuals]:
bool operator<(const error_category & rhs) const;Returns:
less<const error_category*>()( this, &rhs ).[Note: less([comparisons]) provides a total ordering for pointers. --end note]
Throws: Nothing.
Add a new section before 19.4.1.4 [syserr.errcat.objects], titled "Program defined classes derived from error_category":
To be supplied: Requirements for classes derived from error_category.
Add to 19.4.1.4 Error category objects [syserr.errcat.objects]:
To be supplied: Description of posix_category and system_category derived classes.
Change 19.4.2.1 Class error_code overview [syserr.errcode.overview] as indicated:
namespace std {
class error_code {
public:
typedef int_least32_t value_type;
// constructors:
error_code();
error_code(value_type int val, const error_category& cat);
error_code(posix_errno val);
template <class ErrorCodeEnum>
error_code(ErrorCodeEnum e,
typename enable_if<is_error_code_enum<ErrorCodeEnum> >::type* = 0);
// modifiers:
void assign(value_type int val, const error_category& cat);
error_code& operator=(posix_errno val);
template<typename ErrorCodeEnum>
typename enable_if<is_error_code_enum<ErrorCodeEnum>, error_code>::type &
operator=( ErrorCodeEnum val );;
void clear();
// observers:
value_type int value() const;
cont error_category& category() const;
posix_errno posix() const;
error_condition default_error_condition() const;
string message() const;
wstring wmessage() const;
operator unspecified-bool-type const;
// relational operators:
bool operator==(const error_code& rhs) const;
bool operator!=(const error_code& rhs) const;
private:
value_type int val_; // exposition only
const error_category& cat_; // exposition only
};
template <class charT, class traits>
basic_ostream<charT,traits>&
operator<<(basic_ostream<charT,traits>& os, const error_code& ec);
size_t hash_value(const error_code& ec);
} // namespace std
Change 19.4.2.2 Class error_code constructors [syserr.errcode.constructors] as indicated:
error_code();Effects: Constructs an object of type
error_code.Postconditions:
val_ == 0andcat_ ==.posix_category&system_categoryThrows: Nothing.
error_code(value_typeint val, const error_category& cat);Effects: Constructs an object of type
error_code.Postconditions:
val_ == 0andcat_ == cat.Throws: Nothing.
template <classerror_code(posix_errno val);ErrorCodeEnum> error_code(ErrorCodeEnume, typename enable_if<is_error_code_enum<ErrorCodeEnum> >::type* = 0);Effects: Constructs an object of type
error_code.Postconditions:
val_ == static_cast<value_type>(val) and cat_ == posix_category*this == make_error_code(val).Throws: Nothing.
Change 19.4.2.3 Class error_code modifiers [syserr.errcode.modifiers] as indicated:
error_code(value_typeint val, const error_category& cat);Postconditions:
val_ == valandcat_ == cat.Throws: Nothing.
template<typenameerror_code& operator=(posix_errno val);ErrorCodeEnum> typename enable_if<is_error_code_enum<ErrorCodeEnum>, error_code>::type & operator=(ErrorCodeEnumval );Postconditions:
val_ == static_cast<value_type>(val) and cat_ == posix_category*this == make_error_code(val).Returns:
*this.Throws: Nothing.
Change 19.4.2.4 Class error_code observers [syserr.errcode.observers] as indicated:
value_typeint value() const;Returns:
val_.Throws: Nothing.
...posix_errno posix() const;error_condition default_error_condition() const;Returns:
category().posix(value)category().default_error_condition(value()).Throws: Nothing.
Change 19.4.2.5 Class error_code relational operators [syserr.errcode.relational] as indicated:
bool operator==(const error_code& lhs, const error_code& rhs);Returns:
lhs.category() == rhs.category() && lhs.value() == rhs.value().Throws: Nothing.
bool operator==(const error_code&
eccode,posix_errno enconst error_condition& condition);
bool operator==(posix_errno enconst error_condition& condition, const error_code&eccode);Returns:
ec.value() == static_cast<error_code::value_type>(en) && ec.category() == posix_-
categorycode.category().equivalent(code.value(), condition)
|| condition.category().equivalent(code, condition.value()).Throws: Nothing.
bool operator==(const error_condition& lhs, const error_condition& rhs);Returns:
lhs.category() == rhs.category() && lhs.value() == rhs.value().Throws: Nothing.
bool operator!=(const error_code& lhs, const error_code& rhs);Returns:
!(lhs == rhs).Throws: Nothing.
bool operator!=(const error_code&eccode,posix_errno enconst error_condition& condition); bool operator!=(posix_errno enconst error_condition& condition, const error_code&eccode);Returns:!(eccode==encondition).Throws: Nothing.
bool operator!=(const error_condition& lhs, const error_condition& rhs);Returns:
!(lhs == rhs).Throws: Nothing.
bool operator<(const error_code& lhs, const error_code& rhs);Returns:
lhs.category() < rhs.category()
|| (lhs.category() == rhs.category() && lhs.value() < rhs.value()).Throws: Nothing.
bool operator<(const error_condition& lhs, const error_condition& rhs);Returns:
lhs.category() < rhs.category()
|| (lhs.category() == rhs.category() && lhs.value() < rhs.value()).Throws: Nothing.
error_code make_error_code(posix::posix_errno e);Returns:
error_code(e, posix_category).error_condition make_error_condition(posix::posix_errno e);Returns:
error_condition(e, posix_category).
Insert before 19.4.3 Class system_error [syserr.syserr], a new section:
19.4.n Class
error_condition[syserr.errcondition]19.4.n.1 Class
error_conditionoverview [syserr.errcondition.overview]The class
error_conditiondescribes an object used to hold values identifying error conditions. [ Note:error_conditionvalues are portable abstractions, whileerror_codevalues ([syserr.errcode]) are implementation specific. —end note ]namespace std { class error_condition { public: // constructors: error_condition(); error_condition(int val, const error_category& cat); template <class ErrorConditionEnum> error_condition(ErrorConditionEnum e, typename enable_if<is_error_condition_enum<ErrorConditionEnum> >::type* = 0); // modifiers: void assign(int val, const error_category& cat); template<typename ErrorConditionEnum> typename enable_if<is_error_condition_enum<ErrorConditionEnum>, error_code>::type & operator=( ErrorConditionEnum val ); void clear(); // observers: int value() const; const error_category& category() const; string message() const; operator unspecified-bool-type () const; private: int val_; // exposition only const error_category& cat_; // exposition only }; } // namespace std19.4.n.2 Class
error_conditionconstructors [syserr.errcondition.constructors]error_condition();Effects: Constructs an object of type
error_condition.Postconditions:
val_ == 0 and cat_ == posix_category.Throws: Nothing.
error_condition(value_type val, const error_category& cat);Effects: Constructs an object of type error_condition.
Postconditions:
val_ == val and cat_ == cat.Throws: Nothing.
template <class ErrorConditionEnum> error_condition(ErrorConditionEnum e, typename enable_if<is_error_condition_enum<ErrorConditionEnum> >::type* = 0);;Effects: Constructs an object of type
error_condition.Postconditions:
*this == make_error_condition(val).Throws: Nothing.
19.4.n.3 Class
error_conditionmodifiers [syserr.errcondition.modifiers]void assign(value_type val, const error_category& cat);Postconditions:
val_ == val and cat_ == cat.Throws: Nothing.
template<typename ErrorConditionEnum> typename enable_if<is_error_condition_enum<ErrorConditionEnum>, error_code>::type & operator=( ErrorConditionEnum val );;Postconditions:
*this == make_error_condition(val).Throws: Nothing.
void clear();postcondition:
value() == 0 && category() == posix_category19.4.n.4 Class
error_conditionobservers [syserr.errcondition.observers]value_type value() const;Returns:
val_.Throws: Nothing
const error_category& category() const;Returns:
cat_.Throws: Nothing.
string message() const;Returns:
category().message(value()).Throws: Nothing.
operator unspecified-bool-type () const;Returns: If
value() != 0, returns a value that will evaluatetruein a boolean context; otherwise, returns a value that will evaluatefalse. The return type shall not be convertible toint.Throws: Nothing.
[ Note: This conversion can be used in contexts where a
boolis expected (e.g., an if condition); however, implicit conversions (e.g., toint) that can occur withboolare not allowed, eliminating some sources of user error. One possible implementation choice for this type is pointer to member. —end note ]
For example, say the user writes:
error_code ec;
some_operation(ec);
if (ec == boo_boo)
{ ... }
operator == is currently overload on enum posix_errno
, so if boo_boo is a posix_errno constant, the
result will be as if the code read:
if (ec.posix() == boo_boo)
But this is assuming the users is trying to write portable code. If the user instead is writing system specific code, what is really wanted is the equivalent of:
if (ec == error_code(boo_boo, boo_boo's category))
With the interface as currently specified, the user would have to write that out in full. Unfortunately, the user might well opt for this instead:
if (ec.value() == boo_boo)
Unfortunately, that is really error prone. If the implementation happens to
return an error_code with the same value as boo_boo,
but in a different category, so the result will be true when it should be
false.
The working draft specifies other = EOTHER in the specification
of posix_errno in 19.4 System error support, with the specification that
EOTHER is not a POSIX-defined macro in 19.3 Error numbers. Earlier papers
stated that this new macro will be added to the include file cerrno.
The problem with this is that C++ is hijacking the POSIX macro space, which
should be avoided as future versions of the POSIX standard may add more error
macros, including EOTHER, but may have it mean something else.
Submitted by Benjamin Kosnik.
Change 19.3 Error numbers [errno], paragraph one, as indicated:
The header
<cerrno> is described in (Table 29). Its contents are the same as the POSIX header<errno.h>, except thaterrnoshall be defined as a macro, and an additional macro EOTHER shall be defined to represent errors not specified by the POSIX standard. [ Note: The intent is to remain in close alignment with the POSIX standard. --end note ]
From 19.3 Error numbers [errno], Header <cerrno> synopsis, strike
EOTHER.
Change 19.4 System error support [syserr], Header <system_error> synopsis as indicated:
other = EOTHER,
For 19.4.1.2 Class error_category virtual members [syserr.errcat.virtuals] paragraph 3, see issue 7 proposed wording.
In section 19.4.2.6 Class
error_code non-member functions, an ostream inserter for error_code
is defined for the primary ostream template. This will necessitate the inclusion
of the ostream include file, which has large dependencies on other
standard library features. The practical result is that the system_error
include, far from being a lightweight, stand-alone error-reporting facility,
will instead come with significant compile time cost.
Fix: Move the error_code inserter to the ostream
include file, or relax the requirements such that only char and
wchar_t specializations are required.
Submitted by Benjamin Kosnik.
Move the following text from 19.4.2.1 Class error_code overview [syserr.errcode.overview] to the non-member section of the synopsis in 27.6.2.1 Class template basic_ostream [ostream], making the indicated changes:
template <class charT, class traits> basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&os, const error_code&ec); template <class charT, class traits> basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&&, const error_code&);
Move the following text from 9.4.2.6 Class error_code non-member functions [syserr.errcode.nonmembers] to a new sub-section following 27.6.2.6.4 Character inserter function templates [ostream.inserters.character], making the indicated changes:
template <class charT, class traits> basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&osout, const error_code& ec); template <class charT, class traits> basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&& out, const error_code& ec);Effects:
.osout << ec.category().name() << ’:’ << ec.value()Returns:
out
error_code::value_type
with circular dependenciesSection 19.4.2.1 specifies
error_code::value_type as typedef int_least32_t value_type;.
However, this type is used in error_category, which has to be
defined before any of the trivial members in error_code can be made
inline.
In addition, this complexity results in unsightly casting in the
error_code relational operators and constructor specifications.
Possible Fix: Assume error_code::value_type is of type
int, as specified by both C and POSIX.
Submitted by Benjamin Kosnik.
Change the type to int. See proposed wording for issue 7.
The typedef is a historical artifact left over from early development of the class. An int is sufficient for all operating systems we are aware of.
In 19.4.1.2 Class
error_category virtual members, all member functions are defined to not throw,
but do not have throw() specifications.
The same hold for error_code member functions that are modifiers
and observers. i.e, sections 19.4.2.3
and 19.4.2.4.
Proposed Fix: Add in missing throw() specifications.
Submitted by Benjamin Kosnik.
No action necessary.
The Working Paper is inconsistent in its use of throw()
specifications. Many LWG members prefer the "Throws: Nothing" specification as
used in the current working paper wording for diagnostics.
system_error
constructorAll existing classes derived from std::exception provide either
a default constructor or a constructor taking a single const std::string&
parameter. The current specification for system_error breaks with
this.
Submitted by Benjamin Kosnik.
Proposed Fix: Add a constructor to
system_errorthat takes aconst std::string&argument.
No action necessary.
The design of the constructors, including order of
arguments, was a considered
decision after much discussion with Peter Dimov and others on the Boost list. A
primary objective of system_error is to report an
error_code. We don't want to encourage construction of
system_error objects that don't have one. Thus the constructors all
take error_code or equivalent arguments first in addition to the
what string argument. Not supplying a default constructor avoids the
nonsensical construction of system_error representing "no error".
error_category equality via
address comparisonSection 19.4.1.3 Class
error_category non-virtual members specifies address comparisons between
error_category base objects to determine equality and inequality. This
can be fragile or difficult in shared memory or distributed systems, and may
lead to incorrect results for equivalent error_category objects.
Submitted by Benjamin Kosnik.
Fix: Equality compare on internal details of the message catalog characteristics.
No action necessary.
Resolved by Issue 6: Class error_category should be non-copyable. No further action needed.
error_code::value_types constants and
user-defined error_category objectsSection 19.4 specifies posix_errno as an enum. As such, there is
no way to extend it for native error values, which will presumably be in the
form of another enum, in some user-defined space.
Section 19.4.1.4 Error category
objects vaguely specifies posix_category and native_category
as external objects. As such, the implementation details are likely to remain
private, making simple derivation and extension tricky or difficult.
Submitted by Benjamin Kosnik.
To 19.4.1.1 Class error_category overview [syserr.errcat.overview], at the end, add:
[Example: A user or third-party library can provide an additional
error_category:extern const error_category& my_error_category; enum my_error { boo_boo_1=1, boo_boo_2 }; namespace std { inline error_code make_error_code(my_error e) { return error_code( e, my_error_category ); } }-- end example]
After 17.4.4.8 Restrictions on exception handling [res.on.exception.handling], add a new sub-section (presumably 17.4.4.9):
17.4.4.9 Value of error codes
Certain functions in the C++ Standard Library report errors via a
std::error_code([syserr.errcode.overview]) object. That object'scategory()member shall return a reference tostd::system_categoryfor errors originating from the operating system, or a reference to an implementation-definederror_categoryobject for errors originating elsewhere. The implementation shall define the possible values ofvalue()for each of these error categories. [Example: For a POSIX-based operating system, an implementation is encouraged to define thestd::system_categoryvalues as identical to the POSIXerrnovalues, plus additional values as defined by the operating system's documentation. An implementation on a non-POSIX operating system is encouraged to define values identical to the operating system's error values. For errors that don't originate from the operating system, the implementation may provide enums for the associated values. -- end example]
posix_errno should not be extended by users or implementers. Only the POSIX committee can extend the set of POSIX errnos. Thus the concern about 19.4 is not a defect.
Regarding 19.4.1.4; yes, the implementation details of error_categories are private, and that is deliberate encapsulation.
The concern over derivation and extension can be addressed in several ways. The proposed resolution's example should clarify what users need to do. For implementers, the proposed addition to clause 17 provides general guidance. An example of actual implementation could be outside the scope of the standard. For illustration, extracts from the current Boost implementation for several platforms are given below:
// Operating system specific interfaces --------------------------------//
// The interface is divided into general and system-specific portions to
// meet these requirements:
//
// * Code calling an operating system API can create an error_code with
// a single category (system_category), even for POSIX-like operating
// systems that return some POSIX errno values and some native errno
// values. This code should not have to pay the cost of distinguishing
// between categories, since it is not yet known if that is needed.
//
// * Users wishing to write system-specific code should be given enums for
// at least the common error cases.
//
// * System specific code should fail at compile time if moved to another
// operating system.
#ifdef BOOST_POSIX_API
// POSIX-based systems -------------------------------------------------//
// To construct an error_code after a API error:
//
// error_code( errno, system_category )
// User code should use the portable "posix" enums for POSIX errors; this
// allows such code to be portable to non-POSIX systems. For the non-POSIX
// errno values that POSIX-based systems typically provide in addition to
// POSIX values, use the system specific enums below.
# ifdef __CYGWIN__
namespace cygwin
{
enum cygwin_errno
{
no_net = ENONET,
no_package = ENOPKG,
no_share = ENOSHARE,
};
} // namespace cygwin
template<> struct is_error_code_enum
{ static const bool value = true; };
inline error_code make_error_code(cygwin::cygwin_errno e)
{ return error_code( e, system_category ); }
# elif defined(linux) || defined(__linux) || defined(__linux__)
namespace Linux // linux lowercase name preempted by use as predefined macro
{
enum linux_error
{
advertise_error = EADV,
bad_exchange = EBADE,
bad_file_number = EBADFD,
bad_font_format = EBFONT,
bad_request_code = EBADRQC,
...
unclean = EUCLEAN,
};
} // namespace Linux
template<> struct is_error_code_enum
{ static const bool value = true; };
inline error_code make_error_code(Linux::linux_error e)
{ return error_code( e, system_category ); }
# endif
#elif defined(BOOST_WINDOWS_API)
// Microsoft Windows ---------------------------------------------------//
// To construct an error_code after a API error:
//
// error_code( ::GetLastError(), system_category )
namespace windows
{
enum windows_error
{
success = 0,
// These names and values are based on Windows winerror.h
invalid_function = ERROR_INVALID_FUNCTION,
file_not_found = ERROR_FILE_NOT_FOUND,
path_not_found = ERROR_PATH_NOT_FOUND,
too_many_open_files = ERROR_TOO_MANY_OPEN_FILES,
access_denied = ERROR_ACCESS_DENIED,
invalid_handle = ERROR_INVALID_HANDLE,
arena_trashed = ERROR_ARENA_TRASHED,
not_enough_memory = ERROR_NOT_ENOUGH_MEMORY,
...
already_exists = ERROR_ALREADY_EXISTS,
};
} // namespace windows
template<> struct is_error_code_enum
{ static const bool value = true; };
inline error_code make_error_code(windows::windows_error e)
{ return error_code( e, system_category ); }
#else
# error BOOST_POSIX_API or BOOST_WINDOWS_API must be defined
#endif
error_categoryShould posix_category and native_category be able
to have the same address?
In theory, yes. In practice, real POSIX-based operating systems such as Linux add additional error codes, so the error categories have to be different. That allows an implementation to use
error_code(errno, native_category)to construct an error_code. If the POSIX values of errno were a different category from the non-POSIX values, an expensive lookup would have to be done to assign the category.
Should the member function error_category::posix exist for the
predefined object posix_category? Isn't this a no-op, and best
added to the class implementing native_category?
The
error_categoryvirtuals exist to support the equivalenterror_codeanderror_conditionmember functions, allowing user-defined error categories. This mechanism unravels iferror_category::posix(or its issue 7 replacements) isn't present for all error categories.
Should the division be between underlying system errors needed by the C++0x standard library and user-defined error messages, instead of between POSIX and native?
Early versions of the current design did divide the world along standard library / native lines. The design evolved into a POSIX / operating-system / user-defined breakdown because it met specific needs of users and third-party library suppliers, who must deal with a variety of real-world use cases, and implementers who sometimes must rely on API's other than those of the operating system.. That isn't to say another design wouldn't work, but this design is known to work and represents successful existing practice. Note that the dichotomy between POSIX and native is clarified greatly by the introduction of
class error_conditionin issue 7.
Submitted by Benjamin Kosnik.
Change the name native_category to system_category.
See proposed wording for issue 7.
The name native_category seems to be causing some confusion.
It is changed to system_category to more clearly indicate this
category represents errors from the operating system itself rather than some
other library used by the implementation.
The answers to the questions are given in italics above.
error_category::message
member functionsSection 19.4.1.2 Class
error_category virtual members defines members message and
wmessage, and notes an intention to provide a locale-specific string.
However, there is no indication that this is integrated into the existing
standard library infrastructure for locale, ie std::locale.
There are two distinct issues: one, construction of error_category
would have to take some kind of std::locale parameter, some kind of
getter/setter would have to exist to provide locale data, or message
would have to take a std::locale argument. This last option would
require serious re-work of the system_error::what member function.
The second issue is code conversion so that wmessage could
return a wide string from a message catalog. The use case for this is
theoretical at the moment, as wide-character message catalogs are not known. For
this, std::codecvt would be the preferred mechanism, and that
depends on three template parameters. If this route is followed, it may make
sense to templatize the entire class, and separate out message for
char instantiations and wmessage for wchar_t
instantiations.
Note that error_code has these same member functions, which
forward to the error_category member functions, so this could be
considered a defect in both class specifications.
Submitted by Benjamin Kosnik.
Change 19.4.1.1 Class error_category overview [syserr.errcat.overview] paragraph 1 as indicated:
virtual string message(error_code::value_type ev) const = 0; virtual wstring wmessage(error_code::value_type ev) const = 0;
Change 19.4.1.2 Class error_category virtual members [syserr.errcat.virtuals] paragraph 4 and 5 as indicated:
virtual string message(error_code::value_type ev) const = 0; virtual wstring wmessage(error_code::value_type ev) const = 0;
Returns: A string that describes the error condition denoted by ev.[ Note: The intent is to return a locale-specific string that describes the error corresponding to ev. --end note ]
Throws: Nothing.
Change 19.4.2.1 Class error_code overview [syserr.errcode.overview] paragraph 1 as indicated:
string message() const; wstring wmessage() const;
Change 19.4.2.4 Class error_code observers [syserr.errcode.observers] beginning at paragraph 6 as indicated:
string message() const;
Returns:category().message(value()).
Throws: Nothing.wstring wmessage() const;
Returns:category().wmessage(value()).
Throws: Nothing.
Change 19.4.3.2 Class system_error members [syserr.syserr.members] as indicated:
const char *what() const;
Returns: An NTBS incorporating runtime_error::what() and code().message().
[ Note: One possible implementation would be:if (msg.empty()) {try{std::string tmp = runtime_error::what();if (code()){if (!tmp.empty())tmp += ": ";tmp += code().message();}swap(msg, tmp);} catch(...) {return runtime_error::what(); } return msg.c_str();—
end note ]
Discussed by the LWG in Toronto. None of the current standard exception classes worry about wide messages or locales, so a fix for one particular exception probably isn't the right approach. Better to remove the functions until the problem and solution can be more fully explored.
After the Toronto meeting, an email discussion between Beman Dawes, Peter Dimov, Christopher Kohlhoff, and Benjamin Kosnik discussed possible solutions without reaching consensus. The most likely solution was something along the lines of:
string error_message(error_code ec, const locale& loc )
{
messages<char>& f = use_facet< messages<char> >( loc );
int cat = f.open( ec.category().name(), loc );
string r = f.get( cat, 0, ec.value(), "" );
f.close( cat );
return r;
}
While this approach looked promising, it wasn't clear how it would work with user-defined error categories, and whether or not it was actually practical for the full range of operating systems.
system_error::what and constructorsIn 19.4.3.2 Class system_error
members, the constructor definitions and what specification collude
to force exception-unsafe behavior, and multiple std::string data
members.
This in effect mandates elaborate string modification as part of
system_error::what, which may cause unanticipated exceptions to occur, in
addition to being inefficient.
These specifications are a break with the existing stdexcept
requirements and the exception hierarchy design philosophy: by making what
virtual, users can reasonably expect to have derived classes with different
return strings from what. Indeed, that's the point.
In addition, with all these additional requirements, there still exists no way to pass in locale-specific strings.
See issue 17.
Submitted by Benjamin Kosnik.
Change 19.4.3.2 Class system_error members [syserr.syserr.members], paragraph 4, as indicated:
const error_code& code() const throw();
Change 19.4.3.2 Class system_error members [syserr.syserr.members], paragraph 5, as indicated:
const char *what() const throw();
No additional action necessary.
Exception safety is dealt with by the proposed resolution. The data members are not specified; implementations are free to use a std::string or some completely different mechanism. Implementations will presumably use lazy evaluation, so much of the cost will only occur if what() is actually called. No action necessary.
system_error::what and code do
specify throw() in the synopsis to deal with the exception issue, but not in the
member descriptions. See proposed resolution.
The semantics of what() are extremely valuable to users, and any inefficiency is minor compared to the general overhead of an exception being thrown. Existing code, such as main() exception monitors that catch std::exception and report the results of exception::what(), works without modification for std::system_error. and that's very desirable. No additional action necessary.
what() is virtual in the base class, so is virtual in class
system_error too. And system_error::what
does normally return a different string than runtime_error::what. No action necessary.
The Boost version provides operator< for both, allowing objects of class
error_code to be easily used as keys for associative containers. I
can't remember any rationale for removing them from the proposal; it looks like
a simple oversight.
Submitted by Beman Dawes.
See proposed wording for issue 7.
For POSIX based operating systems (Unix, Linux, Mac OS, etc.) it isn't
clear to either implementors or users whether operating system errors will be
classified as native_category or posix_category.
Submitted by Beman Dawes.
No action necessary. This is essentially a FAQ (see below) that has to be dealt with by education. The issue 15 rationale may also be helpful.
What error_category are the errors arising from POSIX-based operating
systems such as Unix, Linux, Mac OS? They are system_category
errors.
Would it be better to move them into sub-namespaces?
Submitted by Chris Kohlhoff
Resolved by moving POSIX enums into a sub-namespace. See proposed wording for issue 7.
These names should reflect the names assigned by the originating organization, such as the POSIX committee, so are somewhat beyond the control of the C++ committee. Also, name clashes with platform specific error enums occur in the absence of sub-namespaces. Experiments with real code show introduction of a posix sub-namespace improves readability.
bad_alloc,
not system_errorIf an out of memory condition occurs in a call to an operating system API
from a standard library implementation, should the resulting exception be
bad_alloc or system_error?
To 19.4.3.1 Class system_error overview [syserr.syserr.overview] add:
[Note: if an error represents an out-of-memory condition, implementations are encouraged to throw an exception of type
bad_alloc([bad.alloc]) rather thansystem_error. --end note]
The LWG discussed this in Toronto, and wishes bad_alloc be thrown.
The proposed wording is in the form of a non-normative note because the type of exception throw on out of memory is implementation defined. See 17.4.4.8 Restrictions on exception handling [res.on.exception.handling]
error_category::name() should
return const char *The specification of error_category::name() as returning a
constant string reference makes it too easy to implement in a non-thread-safe
way such as a static function-scope variable. To be safe I think the function
should return either a std::string by value or const char*.
My preference would be for the latter, since it better reflects that what you
are defining is a string constant that corresponds to the category.
Submitted by Chris Kohlhoff.
Change 19.4.1.1 Class error_category overview [syserr.errcat.overview] as indicated:
virtual conststring&char* name() const = 0;
system_error what-less
constructors neededThe problem of the what arg becomes more significant once you start composing operations. Since we're providing both throwing and non-throwing overloads, to reduce code duplication I like to implement the operation once in non-throwing form and then wrap it with the throwing version. For example:
error_code download_to_directory(
std::string dirname, std::string url, error_code& ec)
{
if (mkdir(dirname, ec))
return ec;
std::string host = url2host(url);
tcp::endpoint ep = resolve(host, ec);
if (ec) return ec;
return download(dirname, ep, url2path(url), ec);
}
void download_to_directory(
std::string dirname, std::string url)
{
error_code ec;
download_to_directory(dirname, url, ec);
if (ec) throw system_error(ec, ?);
}
The functions resolve() and download() are themselves composed
operations, which may in turn use other composed operations. The
original "what" of any error code is well and truly lost by the time you
reach the throw.
Submitted by Chris Kohlhoff.
Beman Dawes comments: It is often very useful to users to know the name of the function where a system_error originates. Thus I would write the line in question like this:
if (ec) throw system_error(ec, "download_to_directory");
But that is a QOI issue, so I support adding constructors that do not require a what string.
Change 19.4.3.1 Class system_error overview [syserr.syserr.overview] as indicated:
class system_error : public runtime_error {
public:
system_error(error_code ec, const string& what_arg);
system_error(error_code ec);
system_error(error_code::value_type ev, const error_category& ecat,
const string& what_arg);
system_error(error_code::value_type ev, const error_category& ecat);
const error_code& code() const throw();
const char* what() const throw();
};
To 19.4.3.2 Class system_error members [syserr.syserr.members] add:
system_error(error_code ec);Effects: Constructs an object of class
system_error.Postconditions:
code() == ecandstrcmp(runtime_error::what(), "") == 0.system_error(int ev, const error_category& ecat);Effects: Constructs an object of class
system_error.Postconditions:
code() == error_code(ev, ecat)andstrcmp(runtime_error::what(), "") == 0.
Chris Kohlhoff, Benjamin Kosnik, and Peter Dimov provided much assistance in identifying and resolving issues.