completed the system_error description in advancedtemplates

This commit is contained in:
Frank B. Brokken 2017-12-02 13:43:03 +01:00
parent 8b41d305a2
commit 0a5dc51fc2
76 changed files with 213 additions and 189 deletions

View file

@ -15,7 +15,7 @@ associations are collected in a class template tt(CatMap), derived from
tt(std::unordered_map). tt(CatMap)'s design is a rather straightforward,
offering a constructor accepting an tt(std::initializer_list):
verbinsert(-s4 //impl examples/errcode2/catmap/catmap.h)
verbinsert(-s4 //impl examples/errcode/catmap/catmap.h)
As tt(ErrorCodeEnum)-values may have been randomly assigned (not using 0)
tt(CapMap) objects offers fast access to the errors' descriptions and
@ -40,7 +40,7 @@ const) member, returning the description matching tt(ev) or the error message
tt(noEnumValue) if tt(ev) does not represent a defined enum value. Here is its
implementation:
verbinsert(-s4 //msg examples/errcode2/categorybase/categorybase.h)
verbinsert(-s4 //msg examples/errcode/categorybase/categorybase.h)
But that isn't all that tt(CategoryBase) can accomplish: since it contains
tt(s_errors), the associations between error enum values and category
@ -59,7 +59,7 @@ tt(s_errors), and compares the stored error condition's name to the name
provided by the (singleton) tt(error_condition) object, given the condition's
number. Here is its implementation:
verbinsert(-s4 //equiv examples/errcode2/categorybase/categorybase.h)
verbinsert(-s4 //equiv examples/errcode/categorybase/categorybase.h)
In addition, to allow single inheritance to be used when deriving classes from
tt(std::error_category) the class tt(CategoryBase) itself is derived from
@ -71,7 +71,7 @@ tt(equivalent) that can be used by all classes derived from
tt(std::error_category); and, finally, a class that itself also is an
tt(error_category) class. Here is the interface of tt(CategoryBase):
verbinsert(-s4 //impl examples/errcode2/categorybase/categorybase.h)
verbinsert(-s4 //impl examples/errcode/categorybase/categorybase.h)
Now we're ready for defining our own error category classes. To define a
category class we take the following steps:
@ -86,12 +86,12 @@ category class we take the following steps:
tt(CalculatorCategory's) class interface:
verbinsert(//class
examples/errcode2/calculatorcategory/calculatorcategory.h)
examples/errcode/calculatorcategory/calculatorcategory.h)
The member tt(instance) returns a reference to the singleton object,
initializing it the first time it is called:
verbinsert(//impl examples/errcode2/calculatorcategory/instance.cc)
verbinsert(//impl examples/errcode/calculatorcategory/instance.cc)
it() The member tt(name) simply returns a short string naming the category
(tt("calculator") for the calculator category).
@ -100,7 +100,7 @@ category class we take the following steps:
thanks to tt(CategoryBase::member): it merely returns what's returned by
that latter member:
verbinsert(//impl examples/errcode2/calculatorcategory/messace.cc)
verbinsert(//impl examples/errcode/calculatorcategory/messace.cc)
it() The associations between error code enum values, descriptions and
error condition names are defined when initializing the tt(CatMap)
@ -108,20 +108,20 @@ category class we take the following steps:
base class it can be initialized when defining the category
class. Here is the initialization for tt(CalculatorCategory):
verbinsert(//impl examples/errcode2/calculatorcategory/data.cc)
verbinsert(//impl examples/errcode/calculatorcategory/data.cc)
it() We're now in position to actually create tt(error_code) objects from
tt(CalculatorError) enum values. For this we define the free function
tt(make_error_code(CalculatorError ce)):
verbinsert(//impl examples/errcode2/calculatorcategory/makeerrorcode.cc)
verbinsert(//impl examples/errcode/calculatorcategory/makeerrorcode.cc)
)
Now that a tt(CalculatorError) value can be converted to an
tt(std::error_code) we can use it in our programs. Here is a little demo
program illustrating its use:
verbinsert(-as4 examples/errcode2/main.part);
verbinsert(-as4 examples/errcode/main.part);
In the next section defining and using error conditions is covered in detail.

View file

@ -44,8 +44,8 @@ itemization(
it() Our first step consists of defining our own error enumerations: one
related to the calculator and one related to the simulator:
verbinsert(//enum examples/errcode2/calculatorerror/calculatorerror.h)
verbinsert(//enum examples/errcode2/simulatorerror/simulatorerror.h)
verbinsert(//enum examples/errcode/calculatorerror/calculatorerror.h)
verbinsert(//enum examples/errcode/simulatorerror/simulatorerror.h)
The class tt(std::error_code) is designed so that two pieces of
information (the error value and its category) become available. The
@ -80,7 +80,7 @@ it() Second, our intention is to let tt(error_code) accept
Here is the specialization for tt(CalculatorError); the one for
tt(SimulatorError) is defined analogously:
verbinsert(//trait examples/errcode2/calculatorerror/.h)
verbinsert(//trait examples/errcode/calculatorerror/.h)
This completes the definition of our own error enumerations, which are
now `promoted' to tt(ErrorCodeEnums).

View file

@ -32,58 +32,68 @@ instead use verbal labels denoting error conditions. So rather than specifying
};
)
textual labels like tt("InputCond", "UnavailCond",) and tt("SystemCond")
could be used. Instead of defining a fixed-size tt(enum class ErrorCondition)
a singleton tt(class ErrorCondition) is defined, guaranteeing the uniqueness
of error conditions which are identified by their textual labels.
could be used. Instead of defining a fixed-size tt(ErrorConditionEnum) a
singleton class tt(ErrorCondition) is used, providing and encapsulating such
an enumeration, at the same time guaranteeing the uniqueness of error
conditions which are identified by textual labels.
Error conditions are obtained from a function tt(make_error_condition),
expecting an tt(ErrorConditionEnum) value as its argument, and using an
tt(std::error_category) that is associated with
the various error condition enum values, much like tt(CalculatorCategory) was
associated with the tt(CalculatorError) enumeration. The tt(std::error_category)
The class tt(ConditionCategory) implements the requirements imposed by the
class tt(std::error_condition), tailored to the error conditions managed by
the class tt(ErrorCondition). Also designed as a singleton,
tt(ConditionCategory) contains a vector whose elements hold names and
descriptions of the various error conditions. In addition to the familiar
members tt(name, message) and tt(equivalent) it offers a member
tt(addCondition). That latter function is used by tt(ErrorCondition) to add
new error conditions to the current set. Here is its interface:
verbinsert(-s4 //class
examples/errcode/conditioncategory/conditioncategory.h)
========================== WIP
The members tt(name) and tt(message) have trivial implementations. The member
tt(equivalent) receives an tt(error_code) object and an error condition enum
value (as an tt(int)). The function must return tt(true) if the provided
tt(error_code) is associated with the provided error condition enum
value. tt(Equivalent) itself doesn't perform those checks. Rather, it
retrieves the tt(error_code's error_category) and uses that object's
tt(equivalent) function to perform the test:
verbinsert(-s4 //impl
examples/errcode/conditioncategory/equivalent.cc)
Next we look at the class tt(ErrorCondition). It contains a
tt(ConditionCategory) object (i.e., em(the) tt(ConditionCategory) object), and
a tt(unsorted_map), mapping condition names to their indices in the
tt(ConditionCategory's) vector. Other than that it's a rather plain class,
offering members to retrieve the enum values of the various error conditions,
and returning conditions' names given their numbers. Error condition enum
values are simply defined as their indices in the tt(ConditionCategory's)
vector, statically cast to the enum tt(ErrorCondition::Enum).
tt(Enum) by itself is defined as mere enumeration name, promoted to an
tt(ErrorConditionEnum) by the tt(std::is_error_condition_enum) trait class
specialization (comparable to what the tt(is_error_code_enum) trait class
accomplished for our enums like tt(CalculatorError)). Here is the
tt(is_error_condition_enum) trait class specialization, followed by
tt(ErrorCondition's) class interface:
verbinsert(-s4 //trait examples/errcode/errorcondition/errorcondition.h)
is provided by an object of the class tt(ErrorCondCat)
. To create an tt(error_condition) an tt(ErrorCondition::Enum) value
is required and the enum's value matching error category.
verbinsert(-s4 //class examples/errcode/errorcondition/errorcondition.h)
Error conditions objects themselves are returned by a function
tt(make_error_condition), expecting an tt(ErrorConditionEnum) value as its
argument, and internally using an tt(std::error_category) that is associated
with the various error condition enum values, much like tt(CalculatorCategory)
was associated with the tt(CalculatorError) enumeration. Since
tt(ConditionCategory) is a singleton, it can directly pass that singleton
object to the tt(std::error_condition) object:
verbinsert(-s4 //make
examples/errcode/errorcondition/makeerrorcondition.cc)
A demo program illustrating some of the facilities of the tt(error_code,
error_category, error_condition) and related classes concludes this
section. The complete implementation of the program is provided in the
annotations()' source archive in the directory
tt(yo/advancedtemplates/examples/errocde).
: their uniqueness can be guaranteed by another class,
tt(ErrorCondition), which is also a singleton. That class defines an empty
tt(enum Enum), which is promoted to an tt(ErrorConditionEnum) by the
tt(std::is_error_condition_enum) trait class specialization:
verbinsert(-s4 //trait examples/errorcondition/errorcondition.h)
verbinsert(-s4 //demo examples/errcode/errcode/main.cc)
Error ondition objects are created by
The tt(ErrorCondition) singleton also has a member tt(addCondition)
allowing programs to add error conditions by name (and a description).
To access error condition objects a
Let's
define our conditions as:
verbinsert(-s4 //simerrsrc examples/errcodeenum.cc)
This allows us to distinguish errors made by the user, errors from the
calculator, and errors from the simulator. As in the previous section, we'll
`promote' this enum, this time to an ti(ErrorConditionEnum):
verbinsert(-s4 //simcondtrait examples/errcodeenum.cc)
Now that an error condition enumeration has been defined, error codes can be
mapped to their appropriate error condition values. To do that, a tt(class
ErrorSourceCategory) is derived (again: in the anonymous namespace) from
tt(std::error_category), similarly to what we did before with, e.g.,
tt(CalculatorErrCategory):
verbinsert(-s4 //errorourcecat examples/errcodeenum.cc)
The tt(name) and tt(message) members are defined as before, but in
addition the member tt(equivalent) is defined comparing a received
tt(error_code) to the available tt(SimErrSource) values. The function
tt(equivalent) returns tt(true) if tt(error_code code) can be mapped to tt(int
condition), which in fact is a statically cast tt(SimErrSource) value. For tt(

View file

@ -0,0 +1,3 @@
calculatorerror
simulatorerror
errorsource

View file

@ -3,7 +3,7 @@
#include <system_error>
//enum
//calcerrc
enum class CalculatorError
{
// no 0, since that's by convention implies no error
@ -16,19 +16,25 @@ enum class CalculatorError
MissingParentheses, // ( and ) don't match
};
//=
//trait
class CalculatorCategory: public std::error_category
{
char const *name() const noexcept override;
std::string message(int ce) const override;
};
extern CalculatorCategory const calculatorCategory;
std::error_code make_error_code(CalculatorError ce);
//calctrait
namespace std
{
template <>
struct is_error_code_enum<CalculatorError>: public true_type
{};
}
//=
#endif

View file

@ -1,6 +1,5 @@
#define CLS
#define MAIN "main.cc"
#define LIBRARY "modules"
#define SOURCES "*.cc"
#define OBJ_EXT ".o"
#define SHAREDREQ ""

View file

@ -0,0 +1,19 @@
#include "main.ih"
int main()
try
{
std::error_code ec = CalculatorError::TypeError;
assert(ec == ErrorSource::CalcError);
assert(ec != ErrorSource::SimError);
assert(ec != ErrorSource::InputError);
ec = CalculatorError::ArityError;
std::cout << ec << ' ' << ec.message() << '\n';
throw std::system_error{ ec, "For demonstration purposes: " };
}
catch (std::system_error &se)
{
std::cout << se.what() << ": " << se.code() << '\n';
}

View file

@ -0,0 +1,11 @@
#include <cassert>
#include <iostream>
// #include <string>
// // #include <unordered_map>
// #include <system_error>
#include "calculatorerror/calculatorerror.h"
#include "simulatorerror/simulatorerror.h"
#include "errorsource/errorsource.h"

View file

@ -3,7 +3,7 @@
#include <system_error>
//enum
//=simerrc
enum class SimulatorError
{
// no 0
@ -16,6 +16,12 @@ enum class SimulatorError
};
//=
class SimulatorCategory: public std::error_category
{
char const *name() const noexcept override;
std::string message(int ce) const override;
};
namespace std
{
template <>
@ -23,6 +29,10 @@ namespace std
{};
}
std::error_code make_error_code(SimulatorError ce);
extern SimulatorCategory const simulatorCategory;
#endif

View file

@ -1,3 +1,8 @@
calculatorerror
simulatorerror
errorsource
// specific error categories, defined for this program
calculatorcategory
simulatorcategory
// classes handling all error conditions
conditioncategory
errorcondition

View file

@ -3,7 +3,7 @@
#include <system_error>
//calcerrc
//enum
enum class CalculatorError
{
// no 0, since that's by convention implies no error
@ -16,25 +16,19 @@ enum class CalculatorError
MissingParentheses, // ( and ) don't match
};
//=
class CalculatorCategory: public std::error_category
{
char const *name() const noexcept override;
std::string message(int ce) const override;
};
extern CalculatorCategory const calculatorCategory;
std::error_code make_error_code(CalculatorError ce);
//calctrait
//trait
namespace std
{
template <>
struct is_error_code_enum<CalculatorError>: public true_type
{};
}
//=
#endif

View file

@ -6,6 +6,7 @@
#include <string>
#include <tuple>
//class
class ConditionCategory: public std::error_category
{
static ConditionCategory *s_instance;
@ -25,16 +26,18 @@ class ConditionCategory: public std::error_category
bool equivalent(std::error_code const &code,
int condition) const noexcept override;
// returns condition idx-th name
std::string const &operator[](size_t idx) const;
void addCondition(char const *name, char const *description);
size_t size() const;
std::string const &operator[](size_t idx) const;
private:
ConditionCategory();
};
//=
inline void ConditionCategory::addCondition(char const *name,
char const *description)
char const *description)
{
d_conditionInfo.push_back({ name, description });
}
@ -50,6 +53,3 @@ inline std::string const &ConditionCategory::operator[](size_t idx) const
}
#endif

View file

@ -5,9 +5,10 @@
// error code and the condition number to verify that the error condition
// associated with the error_code matches the condition number.
//impl
bool ConditionCategory::equivalent(std::error_code const &ec, int condNr )
const noexcept
{
return ec.category().equivalent(ec, condNr);
}
//=

View file

@ -5,6 +5,7 @@
#include "../conditioncategory/conditioncategory.h"
//class
class ErrorCondition
{
static ErrorCondition *s_instance;
@ -31,8 +32,10 @@ class ErrorCondition
private:
ErrorCondition(); // singleton, see instance.cc
friend std::error_condition make_error_condition(Enum ec);
};
//=
std::error_condition make_error_condition(ErrorCondition::Enum ec);
inline std::string const &ErrorCondition::operator[](size_t nr) const
{

View file

@ -2,7 +2,7 @@
std::error_condition make_error_condition(ErrorCondition::Enum ec)
{
return { static_cast<int>(ec), ErrorCondition::instance().d_ec };
return { static_cast<int>(ec), ConditionCategory::instance() };
}

View file

@ -1,5 +1,6 @@
#define CLS
#define MAIN "main.cc"
#define LIBRARY "modules"
#define SOURCES "*.cc"
#define OBJ_EXT ".o"
#define SHAREDREQ ""
@ -9,7 +10,7 @@
#define CXXFLAGS " --std=c++17 -Wall -O2" \
" -fdiagnostics-color=never "
#define IH ".ih"
//#define PRECOMP "-x c++-header"
#define PRECOMP "-x c++-header"
#define REFRESH
#define LDFLAGS ""
#define ADD_LIBRARIES ""

View file

@ -1,19 +1,55 @@
#include "main.ih"
//demo
int main()
try
{
ErrorCondition &errorCond = ErrorCondition::instance();
std::cerr << CalculatorCategory::instance().name() << '\n' <<
SimulatorCategory::instance().name() << '\n';
errorCond.addCondition("InputCond", "error in user request");
errorCond.addCondition("UnavailCond", "function not available");
errorCond.addCondition("SystemCond", "system failure");
// ec is an actual error code, belonging to some error enum
// the assert checks whether the specified error code belongs to
// the specified error condition
// // also OK: ErrorCondition::Enum{};
// std::error_condition cond = errorCond("InputCond");
std::error_code ec = CalculatorError::TypeError;
assert(ec == ErrorSource::CalcError);
assert(ec != ErrorSource::SimError);
assert(ec != ErrorSource::InputError);
std::cerr << "Enum value of UnavailCond = " <<
errorCond("UnavailCond") << '\n';
assert(ec != ErrorCondition::Enum{});
assert(ec == errorCond("UnavailCond"));
assert(ec != errorCond("SystemCond"));
ec = CalculatorError::MissingParentheses;
assert(ec == errorCond("InputCond"));
ec = CalculatorError::ArityError;
std::cout << ec << ' ' << ec.message() << '\n';
throw std::system_error{ ec, "For demonstration purposes: " };
throw std::system_error{ ec, "For demonstration purposes" };
}
catch (std::system_error &se)
catch (std::system_error const &se)
{
std::cout << se.what() << ": " << se.code() << '\n';
std::cout << "System Error: " << se.what() << ":\n"
" " << se.code() << '\n';
//throw; cannot be rethrown!!
}
/*
Produced output:
calculator
simulator
Enum value of UnavailCond = 2
calculator:4 incorrect number of arguments
System Error: For demonstration purposes: incorrect number of arguments:
calculator:4
*/
//=

View file

@ -1,11 +1,15 @@
#include <cassert>
#include <iostream>
// #include <string>
// // #include <unordered_map>
// #include <system_error>
#include <string>
#include <system_error>
#include "calculatorerror/calculatorerror.h"
#include "calculatorcategory/calculatorcategory.h"
#include "simulatorerror/simulatorerror.h"
#include "simulatorcategory/simulatorcategory.h"
#include "errorsource/errorsource.h"
#include "errorcondition/errorcondition.h"

View file

@ -9,15 +9,17 @@ try
{
std::error_code ec = CalculatorError::ArityError;
std::cout << ec << ' ' << ec.message() << '\n';
throw std::system_error{ ec, "For demonstration purposes: " };
throw std::system_error{ ec, "For demonstration purposes" };
}
catch (std::system_error &se)
{
std::cout << se.what() << ": " << se.code() << '\n';
std::cout << se.what() << ":\n"
" " << se.code() << '\n';
}
/*
Outputs:
calculator:4 incorrect number of arguments
For demonstration purposes: : incorrect number of arguments: calculator:4
For demonstration purposes: incorrect number of arguments:
calculator:4
*/

View file

@ -3,7 +3,7 @@
#include <system_error>
//=simerrc
//enum
enum class SimulatorError
{
// no 0
@ -16,12 +16,6 @@ enum class SimulatorError
};
//=
class SimulatorCategory: public std::error_category
{
char const *name() const noexcept override;
std::string message(int ce) const override;
};
namespace std
{
template <>
@ -29,10 +23,6 @@ namespace std
{};
}
std::error_code make_error_code(SimulatorError ce);
extern SimulatorCategory const simulatorCategory;
#endif

View file

@ -1,8 +0,0 @@
// specific error categories, defined for this program
calculatorcategory
simulatorcategory
// classes handling all error conditions
conditioncategory
errorcondition

View file

@ -1,47 +0,0 @@
#include "main.ih"
int main()
try
{
ErrorCondition &errorCond = ErrorCondition::instance();
std::cerr << CalculatorCategory::instance().name() << '\n' <<
SimulatorCategory::instance().name() << '\n';
errorCond.addCondition("InputCond", "error in user request");
errorCond.addCondition("UnavailCond", "function not available");
errorCond.addCondition("SystemCond", "system failure");
// ec is an actual error code, belonging to some error enum
// the assert checks whether the specified error code belongs to
// the specified error condition
// // also OK: ErrorCondition::Enum{};
// std::error_condition cond = errorCond("InputCond");
std::error_code ec = CalculatorError::TypeError;
std::cerr << "Enum value of UnavailCond = " << errorCond("UnavailCond") <<
'\n';
assert(ec != ErrorCondition::Enum{});
assert(ec == errorCond("UnavailCond"));
assert(ec != errorCond("SystemCond"));
ec = CalculatorError::MissingParentheses;
assert(ec == errorCond("InputCond"));
ec = CalculatorError::ArityError;
std::cout << ec << ' ' << ec.message() << '\n';
throw std::system_error{ ec, "For demonstration purposes: " };
}
catch (std::system_error const &se)
{
std::cout << "System Error: " << se.what() << ": " << se.code() << '\n';
//throw; cannot be rethrown!!
}
catch (std::exception const &exc)
{
std::cout << "Exception: " << exc.what() << '\n';
}

View file

@ -1,15 +0,0 @@
#include <cassert>
#include <iostream>
#include <string>
#include <system_error>
#include "calculatorerror/calculatorerror.h"
#include "calculatorcategory/calculatorcategory.h"
#include "simulatorerror/simulatorerror.h"
#include "simulatorcategory/simulatorcategory.h"
#include "errorcondition/errorcondition.h"