Using std::error_code with non-integer values - c++

I'm writing a library and want to return error codes whenever an error is returned by a remote system. The problem is that these are identified by strings, eg, "0A01" and also contain a message, and error code requires an integer as value.
What is the best way to implement an error code, with all the functionality that std::error_code provides but that uses strings as the value? How do I add an external error string to the std::error_code or std::error_category?

As mentioned in the comments, you must know the error codes, which could be received from the remote server.
The std::string which you receive from a remote server contains 2 parts as you said,
The problem is that these are identified by strings, eg, "0A01" and also contain a message, and error code requires an integer as value.
As you haven't shared the format of the error message, I am not adding the code for spiting it, split your string into 2 parts,
Error Code
Error Message
Now you can convert Error Code of type std::string to int by using std::stoi(error_code), So lets say
int error_code_int = std::stoi(string_to_hexadecimal(error_code));
And for std::error_category which serves as base class for our custom error messages, do this,
std::string message_received = "This is the message which received from remote server.";
struct OurCustomErrCategory : std::error_category
{
const char* name() const noexcept override;
std::string message(int ev) const override;
};
const char* OurCustomErrCategory::name() const noexcept
{
return "Error Category Name";
}
std::string OurCustomErrCategory::message(int error_code_int) const
{
switch (error_code_int)
{
case 1:
return message_received;
default:
return "(unrecognized error)";
}
}
const OurCustomErrCategory ourCustomErrCategoryObject;
std::error_code make_error_code(int e)
{
return {e, ourCustomErrCategoryObject};
}
int main()
{
int error_code_int = std::stoi(string_to_hexadecimal(error_code)); // error_code = 0A01
ourCustomErrCategoryObject.message(error_code_int);
std::error_code ec(error_code_int , ourCustomErrCategoryObject);
assert(ec);
std::cout << ec << std::endl;
std::cout << ec.message() << std::endl;
}
The output for above working example is
Error Category Name : 0A01
This is the message which received from remote server.
You can use function string_to_hexadecimal() from this post.
I hope that now you can modify the above code according to your needs.
Edit 1:
As you said that:
This assumes the dynamic message is a global value. How do I pass it
to an std::error_category object?
You can see that both std::error_code::assign and constructor std::error_code::error_code are taking parameters of int for error code number and error_category. So It is obvious that std::error_code can't take the dynamic message.
But wait, I said std::error_code are taking error_category as an argument in constructor, so is there any way, we can assign the dynamic message there ?
std::error_category states that:
std::error_category serves as the base class for specific error
category types.
So it means that the struct we derived from std::error_category at the following line
struct OurCustomErrCategory : std::error_category
can have a data member and we can assign it via member function, so our struct will become like that,
struct OurCustomErrCategory : std::error_category
{
std::string message_received;
OurCustomErrCategory(std::string m) : message_received(m) {}
const char* name() const noexcept override;
std::string message(int ev) const override;
};
and you can assign it like that wherever you want,
const OurCustomErrCategory ourCustomErrCategoryObject("This is the message which received from remote server.");

Related

GStreamer GError to boost::system::error_code?

Looking for a clever way to transform a gstreamer/glib GError:
struct GError {
GQuark domain;
gint code;
gchar* message;
};
to a boost::system::error_code. Typically, you could create a category and register an enum to boost. GStreamer uses enum but the code is returned as an int. GQuark is a category.
This is mainly to standardize error handling across an application.
GQuark is a category.
I think you mean that figuratively.
Quarks are associations between strings and integer identifiers. Given either the string or the Quark identifier it is possible to retrieve the other.
That's like interned strings, aka atoms. So GQuark actually represents a string, then, that you want to treat as a category. Well then.
Categories are static
Well. Usually. At the very least categories are singleton with global object identity. So, assuming that you know statically what domains to expect, you could create categories for those.
Map them to the text representation of the domain makes for the most stable mappings.
A single category
To get started, here's a single category:
using boost::system::error_code;
using boost::system::error_condition;
enum class MyCoreError {
failed, // a general error which doesn't fit in any other category. Make sure you add a custom message to the error call.
too_lazy, // do not use this except as a placeholder for deciding where to go while developing code.
not_implemented, // use this when you do not want to implement this functionality yet.
state_change, // used for state change errors.
pad, // used for pad-related errors.
thread, // used for thread-related errors.
negotiation, // used for negotiation-related errors.
event, // used for event-related errors.
seek, // used for seek-related errors.
caps, // used for caps-related errors.
tag, // used for negotiation-related errors.
missing_plugin, // used if a plugin is missing.
clock, // used for clock related errors.
disabled, // used if functionality has been disabled at compile time.
num_errors, // the number of core error types.
};
namespace boost::system { template<> struct is_error_code_enum<MyCoreError> : std::true_type {}; }
namespace detail {
class my_core_category : public boost::system::error_category {
public:
const char* name() const noexcept override {
return g_quark_to_string(GST_CORE_ERROR);
}
std::string message(int ev) const override {
switch (static_cast<MyCoreError>(ev)) {
case MyCoreError::failed: return "a general error which doesn't fit in any other category. Make sure you add a custom message to the error call.";
case MyCoreError::too_lazy: return "do not use this except as a placeholder for deciding where to go while developing code.";
case MyCoreError::not_implemented: return "use this when you do not want to implement this functionality yet.";
case MyCoreError::state_change: return "used for state change errors.";
case MyCoreError::pad: return "used for pad-related errors.";
case MyCoreError::thread: return "used for thread-related errors.";
case MyCoreError::negotiation: return "used for negotiation-related errors.";
case MyCoreError::event: return "used for event-related errors.";
case MyCoreError::seek: return "used for seek-related errors.";
case MyCoreError::caps: return "used for caps-related errors.";
case MyCoreError::tag: return "used for negotiation-related errors.";
case MyCoreError::missing_plugin: return "used if a plugin is missing.";
case MyCoreError::clock: return "used for clock related errors.";
case MyCoreError::disabled: return "used if functionality has been disabled at compile time.";
case MyCoreError::num_errors: return "the number of core error types.";
default: return "unknown core error";
}
}
error_condition default_error_condition(int ev) const noexcept override {
return error_condition{ ev, *this };
}
bool equivalent(int ev, error_condition const& condition) const noexcept override {
return condition.value() == ev && &condition.category() == this;
}
bool equivalent(error_code const& error, int ev) const noexcept override {
return error.value() == ev && &error.category() == this;
}
};
}
error_code make_error_code(MyCoreError se)
{
static detail::my_core_category const cat{};
return error_code{static_cast<std::underlying_type<MyCoreError>::type>(se), cat};
}
Now you can just
auto code = MyCoreError::missing_plugin;
throw boost::system::system_error(make_error_code(code));
and it will magically do the right thing.
General Error Translation:
For the general case translating from API error:
boost::system::error_code make_error_code(GError const& err) {
if (err.domain == GST_CORE_ERROR) {
return make_error_code(MyCoreError(err.code));
}
if (err.domain == GST_LIBRARY_ERROR) {
return make_error_code(MyLibraryError(err.code));
}
if (err.domain == GST_RESOURCE_ERROR) {
return make_error_code(MyResourceError(err.code));
}
if (err.domain == GST_STREAM_ERROR) {
return make_error_code(MyStreamError(err.code));
}
// decide on how to represent unknown errors? Just throw, or translate into
// MyCoreError::failed?
}

Passing std::string_view to API execting const std::string&

I am using Socket.IO library to create a client. While sending a message I have to pass my message (data) as sio::message::list(msg)
void Socket_IO::send_message(std::string_view msg) //Gives Error
//void Socket_IO::send_message(const std::string &msg) //Works
{
this->client.socket()->emit(Socket_IO::general_message, sio::message::list(msg), [&](sio::message::list const& msg) {
});
}
class sio::message::list has a constructor
list(const string& text)
{
m_vector.push_back(string_message::create(text));
}
but does not have a std::string_view constructor
Error :
'<function-style-cast>': cannot convert from 'std::string_view' to 'sio::message::list'
I wish to know is their any way I can pass std::string_view for API expecting const std::string&
I can't use c_str() function as the string might contain binary data which may contain null character (0x00).
I was thinking of creating a string object sio::message::list(std::string str(msg)) but wish to inquire will it defeat the purpose of using std::string_view.
You could go with:
this->client.socket()->emit(Socket_IO::general_message, sio::message::list(std::string(msg)), ...
this makes the job done. It will initialize temporary string object and pass it to list constructor.

Turning GetLastError() into an exception with Error string

This is very similar to:
Turning GetLastError() into an exception
I also want to be able to add an std::string to the error:
class Win32Exception : public std::system_error
{
public:
Win32Exception(std::string ErrorDesc) : std::system_error(GetLastError(), std::system_category(), ErrorDesc)
{
}
};
The problem is that at least in DEBUG builds of VS2015 the construction of std::string resets GetLastError(). So by the time the Win32Exception calls GetLastError() it always gets zero/no error.
I could use const char*, but I also want to use std::wstring, which means I need to convert it to std::string or const char* (because the std::system_error expects std::string or const char*), and that leads me back to the same problem that the error gets reset.
Is there some elegant solution on how to throw Win32 errors easily, having the exception class capture GetLastError() and being able to add arbitrary info with std::string's?
You need to rewrite exception class constructor so it will take error code as parameter so error code:
Win32Exception
(
char const * const psz_description
, ::DWORD const error_code = ::GetLastError()
)
: std::system_error(error_code, std::system_category(), psz_description)
{
}

C++ Scope of Static Variable In a Static Member Function

I have a simple object which does some parsing. Inside, there is a parse function, containing a static variable that is used to limit number of error messages to print to the user:
struct CMYParsePrimitive {
static bool Parse(const std::string &s_line)
{
// do the parsing
static bool b_warned = false;
if(!b_warned) {
b_warned = true;
std::cerr << "error: token XYZ is deprecated" << std::endl;
}
// print a warning about using this token (only once)
return true;
}
};
Now these parse primitives are passed in a typelist to a parser specialization. There is some other interface which tells parser which token types should be parsed using which parse primitives.
My issue is that the warning should be displayed up to once per application run. But in my case, it is sometimes displayed multiple times, seems to be per parser instance rather than application instance.
I'm using Visual Studio 2008, I imagine this might be some bug or a deviation from the standard? Does anyone have any idea as to why this happens?
I failed to notice that the function is also a template. My bad. It is instantiated twice in the code with different parameters - hence the warning is sometimes printed twice. The real code looks more like this:
struct CMYParsePrimitive {
template <class CSink>
static bool Parse(const std::string &s_line, CSink &sink)
{
// do the parsing, results passed down to "sink"
static bool b_warned = false;
if(!b_warned) {
b_warned = true;
std::cerr << "error: token XYZ is deprecated" << std::endl;
}
// print a warning about using this token (only once)
return true;
}
};
So then there are e.g. CMYParsePrimitive::Parse<PreviewParser>::b_warned, which can print the warning once when used by PreviewParser, and then also CMYParsePrimitive::Parse<Parser>::b_warned which can print the warning when used by Parser.

Is it possible to convert a boost::system::error_code to a std:error_code?

I want to replace external libraries (like boost) as much as possible with their equivalents in standard C++ if they exist and it is possible, to minimize dependencies, therefore I wonder if there exists a safe way to convert boost::system::error_code to std::error_code. Pseudo code example:
void func(const std::error_code & err)
{
if(err) {
//error
} else {
//success
}
}
boost::system::error_code boost_err = foo(); //foo() returns a boost::system::error_code
std::error_code std_err = magic_code_here; //convert boost_err to std::error_code here
func(std_err);
The most important it is not the exactly the same error, just so close to as possible and at last if is an error or not. Are there any smart solutions?
Thanks in advance!
I had this exact same question since I wanted to use std::error_code but was also using other boost libraries that use boost::system::error_code (e.g. boost ASIO). The accepted answer works for the error codes handled by std::generic_category(), as they're a simple cast from boost's generic error codes, but it doesn't work for the general case where you want to handle custom error categories as well.
So I created the following code as a general purpose boost::system::error_code-to-std::error_code converter. It works by dynamically creating an std::error_category shim for each boost::system::error_category, forwarding the calls to the underlying Boost error category. Since error categories need to be singletons (or at least singleton-like as in this case) I don't expect there to be much of a memory explosion.
I also just convert boost::system::generic_category() object to use std::generic_category() since they should behave the same. I had wanted to do the same for system_category(), however in testing on VC++10 it printed out the wrong messages (I assume it should print out what you get from FormatMessage, but it appears to use strerror, Boost uses FormatMessage as expected).
To use it just call BoostToErrorCode(), defined below.
Just a warning, I just wrote this today so it's only had basic testing. You may use it any way you like, but you do so at your own risk.
//==================================================================================================
// These classes implement a shim for converting a boost::system::error_code to a std::error_code.
// Unfortunately this isn't straightforward since it the error_code classes use a number of
// incompatible singletons.
//
// To accomplish this we dynamically create a shim for every boost error category that passes
// the std::error_category calls on to the appropriate boost::system::error_category calls.
//==================================================================================================
#include <boost/system/error_code.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/once.hpp>
#include <boost/thread/locks.hpp>
#include <system_error>
namespace
{
// This class passes the std::error_category functions through to the
// boost::system::error_category object.
class BoostErrorCategoryShim : public std::error_category
{
public:
BoostErrorCategoryShim( const boost::system::error_category& in_boostErrorCategory )
:m_boostErrorCategory(in_boostErrorCategory), m_name(std::string("boost.") + in_boostErrorCategory.name()) {}
virtual const char *name() const;
virtual std::string message(value_type in_errorValue) const;
virtual std::error_condition default_error_condition(value_type in_errorValue) const;
private:
// The target boost error category.
const boost::system::error_category& m_boostErrorCategory;
// The modified name of the error category.
const std::string m_name;
};
// A converter class that maintains a mapping between a boost::system::error_category and a
// std::error_category.
class BoostErrorCodeConverter
{
public:
const std::error_category& GetErrorCategory( const boost::system::error_category& in_boostErrorCategory )
{
boost::lock_guard<boost::mutex> lock(m_mutex);
// Check if we already have an entry for this error category, if so we return it directly.
ConversionMapType::iterator stdErrorCategoryIt = m_conversionMap.find(&in_boostErrorCategory);
if( stdErrorCategoryIt != m_conversionMap.end() )
return *stdErrorCategoryIt->second;
// We don't have an entry for this error category, create one and add it to the map.
const std::pair<ConversionMapType::iterator, bool> insertResult = m_conversionMap.insert(
ConversionMapType::value_type(
&in_boostErrorCategory,
std::unique_ptr<const BoostErrorCategoryShim>(new BoostErrorCategoryShim(in_boostErrorCategory))) );
// Return the newly created category.
return *insertResult.first->second;
}
private:
// We keep a mapping of boost::system::error_category to our error category shims. The
// error categories are implemented as singletons so there should be relatively few of
// these.
typedef std::unordered_map<const boost::system::error_category*, std::unique_ptr<const BoostErrorCategoryShim>> ConversionMapType;
ConversionMapType m_conversionMap;
// This is accessed globally so we must manage access.
boost::mutex m_mutex;
};
namespace Private
{
// The init flag.
boost::once_flag g_onceFlag = BOOST_ONCE_INIT;
// The pointer to the converter, set in CreateOnce.
BoostErrorCodeConverter* g_converter = nullptr;
// Create the log target manager.
void CreateBoostErrorCodeConverterOnce()
{
static BoostErrorCodeConverter converter;
g_converter = &converter;
}
}
// Get the log target manager.
BoostErrorCodeConverter& GetBoostErrorCodeConverter()
{
boost::call_once( Private::g_onceFlag, &Private::CreateBoostErrorCodeConverterOnce );
return *Private::g_converter;
}
const std::error_category& GetConvertedErrorCategory( const boost::system::error_category& in_errorCategory )
{
// If we're accessing boost::system::generic_category() or boost::system::system_category()
// then just convert to the std::error_code versions.
if( in_errorCategory == boost::system::generic_category() )
return std::generic_category();
// I thought this should work, but at least in VC++10 std::error_category interprets the
// errors as generic instead of system errors. This means an error returned by
// GetLastError() like 5 (access denied) gets interpreted incorrectly as IO error.
//if( in_errorCategory == boost::system::system_category() )
// return std::system_category();
// The error_category was not one of the standard boost error categories, use a converter.
return GetBoostErrorCodeConverter().GetErrorCategory(in_errorCategory);
}
// BoostErrorCategoryShim implementation.
const char* BoostErrorCategoryShim::name() const
{
return m_name.c_str();
}
std::string BoostErrorCategoryShim::message(value_type in_errorValue) const
{
return m_boostErrorCategory.message(in_errorValue);
}
std::error_condition BoostErrorCategoryShim::default_error_condition(value_type in_errorValue) const
{
const boost::system::error_condition boostErrorCondition = m_boostErrorCategory.default_error_condition(in_errorValue);
// We have to convert the error category here since it may not have the same category as
// in_errorValue.
return std::error_condition( boostErrorCondition.value(), GetConvertedErrorCategory(boostErrorCondition.category()) );
}
}
std::error_code BoostToErrorCode( boost::system::error_code in_errorCode )
{
return std::error_code( in_errorCode.value(), GetConvertedErrorCategory(in_errorCode.category()) );
}
Since C++-11 (std::errc), boost/system/error_code.hpp maps the same error codes to std::errc, which is defined in the system header system_error.
You can compare both enums and they should be functionally equivalent because they both appear to be based on the POSIX standard. May require a cast.
For example,
namespace posix_error
{
enum posix_errno
{
success = 0,
address_family_not_supported = EAFNOSUPPORT,
address_in_use = EADDRINUSE,
address_not_available = EADDRNOTAVAIL,
already_connected = EISCONN,
argument_list_too_long = E2BIG,
argument_out_of_domain = EDOM,
bad_address = EFAULT,
bad_file_descriptor = EBADF,
bad_message = EBADMSG,
....
}
}
and std::errc
address_family_not_supported error condition corresponding to POSIX code EAFNOSUPPORT
address_in_use error condition corresponding to POSIX code EADDRINUSE
address_not_available error condition corresponding to POSIX code EADDRNOTAVAIL
already_connected error condition corresponding to POSIX code EISCONN
argument_list_too_long error condition corresponding to POSIX code E2BIG
argument_out_of_domain error condition corresponding to POSIX code EDOM
bad_address error condition corresponding to POSIX code EFAULT
I adapted the above solution into a shorter solution.
Using a thread_local std::map to map between the category name to its instance so no locking is needed.
The limitation is that you can't pass error codes between threads since the category pointer will be different.(Converting it to a locking function is simple enough if you don't want to use the thread_local storage)
Also I feed it is more compact.
#include <iostream>
#include <map>
#include <boost/system/system_error.hpp>
namespace std
{
error_code make_error_code(boost::system::error_code error)
{
struct CategoryAdapter : public error_category
{
CategoryAdapter(const boost::system::error_category& category)
: m_category(category)
{
}
const char* name() const noexcept
{
return m_category.name();
}
std::string message(int ev) const
{
return m_category.message(ev);
}
private:
const boost::system::error_category& m_category;
};
static thread_local map<std::string, CategoryAdapter> nameToCategory;
auto result = nameToCategory.emplace(error.category().name(), error.category());
auto& category = result.first->second;
return error_code(error.value(), category);
}
};
int main() {
auto a = boost::system::errc::make_error_code(boost::system::errc::address_family_not_supported);
auto b = std::make_error_code(a);
std::cout << b.message() << std::endl;
}
As of Boost version 1.65, you can convert boost::error_code to its C++11 counterpart:
On a C++11 compiler, Boost.System now provides implicit conversions from boost::system::error_category, error_code, and error_condition to their standard equivalents from <system_error>.
This allows libraries to expose a C++11 interface and report errors via std::error_code even when using Boost.System, directly or through a dependency such as Boost.ASIO.
So it should now be as simple as:
auto ec = static_cast<std::error_code>(boost_error_code);