Is there a way to stop a C++ class if there is an error in the instantiation? Like, return NULL maybe? Basically I have a wrapper class for MySQL, and the constructor does the connecting, but if the connection fails, I want the object to be, um, useless?
PDB::PDB(string _DB_IP, string _DB_USER, string _DB_PASS, string _DB_DB)
: _DB_IP( _DB_IP ), _DB_USER( _DB_USER ), _DB_PASS( _DB_PASS ), _DB_DB( _DB_DB )
{
mysql_init(&this->mysql);
this->connection = mysql_real_connect(&this->mysql, this->_DB_IP.c_str(), this->_DB_USER.c_str(), this->_DB_PASS.c_str(), this->_DB_DB.c_str(), 0, 0, 0);
if( this->connection == NULL ) // WHAT SHOULD I DO HERE, OTHER THAN THROW AN ERROR?
{
cout << mysql_error(&this->mysql) << endl;
}
this->result = NULL;
}
What should I do in the NULL test, to stop creation, etc?
Throwing an exception is really the only way to indicate an error during construction.
http://www.parashift.com/c++-faq-lite/exceptions.html#faq-17.8
If the connection can normally fail, set a flag in the object and provide an is_connected() member function for the class, and use it in your application code. If it normally cannot fail, throw an exception. The former is the pattern that the C++ Standard Library uses for opening file streams.
Related
I am using a C library (which I also wrote) within a C++ program and I want to know how I should be doing error handling from within the C library such that the C++ program is able to decide how to handle errors that may happen.
I originally had the C library return a value indicating an error has occurred (e.g. NULL or -1) and just print an error message to stderr but this is not easily capturable by programs using the library.
Ideally I want the C++ program to be able to retrieve an error message string from the C library so that it can handle the error whichever way it wants to, such as logging the error message elsewhere.
Is there a common design idiom for this?
All methods, that require global state (errno, get_last_error, ...), will have concurrency issues. Using the return channel ruins return value optimization for you.
A common option is to take a pointer to an error struct as first or last parameter.
struct Error {
const char* message;
int code;
};
int my_nice_c_function( int a, int b, Error* err ) {
if ( err != NULL ) {
err->message = "";
err->code = 0;
}
return a + b;
}
When you pass them in from C++ it could look like:
Error err;
...
auto foo = my_nice_c_function( 5, 6, &err );
if ( err.code != 0 ) {
std::cerr << err.message << std::endl;
}
But make sure to always set the error members!
I want to return null data from a function that return type is map.
Here is my code.
map<string, string> something_to_do(string something) {
// something to do..
// if above code is something wrong, it should return null.
return null;
}
But, there seems to be no type casting to return null in map library.
How can I do it?
(I'm sorry my awful English..)
as an alternative you can in C++17 use instead std::optional
std::optional<std::map<string,string>> something_to_do(string something) {
std::map<std::string,std::string> yourmap;
yourmap["a"] = "b";
...
return yourmap;
// or
// return std::null_t;
}
...
auto d = something_to_do(something);
if (d)
{
auto& m = d.value();
std::cout << m["a"] << std::endl;
}
I think the functionality you are looking for would be better handled by throwing an exception.
That way you can proceed as normally, but if something goes wrong like you allude to in your comment, then you want to throw an exception and have any client code handle the exception accordingly. See this SO post.
It allows you to write straight-forward code without having custom types to return for every possible situation of your running code. It also eliminates the need to check for certain values being returned just to handle any errors.
To call the function and handle the error, you would simply wrap the method call in a try-catch block:
from #nsanders' orginal answer
try {
compare( -1, 3 );
}
catch( const std::invalid_argument& e ) {
// do stuff with exception...
}
If the function returns a map you must return a map - you cannot return nullptr (an empty map, sure, but that's as close as you'll get). Maybe you are looking for std::optional so you can have your function return an optional map that may or may not be there?
I am writing some basic code to experiment with Angelscript, however I cannot get even the simplest functions to work correctly. Here is the basic code block:
class Engine {
public:
void print(std::string&);
};
Engine::print(std::string &msg)
{
cout<<msg.c_str()<<endl;
}
Here is the actual code that initializes and registers C functions for Angelscript:
int r;
asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION);
r = engine->SetMessageCallback(asMETHOD(Engine,MessageCallback), this,asCALL_THISCALL);
assert( r >= 0 );
RegisterStdString(engine);
r = engine->RegisterObjectType("Engine", 0, asOBJ_APP_CLASS_CONSTRUCTOR);
cout<<"r was: "<<r<<endl;
assert( r >= 0 );
r = engine->RegisterObjectMethod("Engine","void print(const string &in)", asMETHOD(Engine,print), asCALL_THISCALL);
assert( r >= 0 );
At first I did not have the function "RegisterObjectType()" present, so running the program would throw a "'Engine' is not a valid type" error even though it was a valid class. So I found a function called "RegisterObjectType()" and implemented it as above and now it's throwing a "Invalid Flag" error on the 'RegisterObjectType()' function in the last field. I've tried all the class flags and it still throws this error.
What is the proper method for registering a class method into Angelscript? The documentation example didn't seem to work. It seems to snip out everything except the actual function it is explaining, for example, it did not hint about registering object types with the class method registration code. Or it was not very clear about it.
I should mention that all the errors encountered are runtime errors thrown by the angelscript libraries and not compiler errors.
Thanks!
The as_OBJ_APP_CLASS_CONSTRUCTOR is not proper flag, it should be used combined with other as_OBJ_APP... flags. Check this for example (bit outdated), and clarifications of ways to register object type: http://www.darawk.com/code/CoHClient/Angelscript/sdk/docs/articles/register_object.html.
Of course manual is your best friend:
http://www.angelcode.com/angelscript/sdk/docs/manual/index.html
(Using Angelscript->Registering the application interface->Registering an object type)
string receiveFromServer();
this function returns a string that was received from some server. If there was an error along the way (including protocol), i want to return a NULL string. However, this doesn't work in c++ (unlike java).
i tried:
string response = receiveFromServer();
if (response==NULL) {
cerr << "error recive response\n";
}
but its not legal.
Since an empty string is also legal to return, what can i return that will indicate the error?
thank you!
You can throw an exception to indicate an error.
try {
std::string response = receiveFromServer();
} catch(std::runtime_error & e) {
std::cerr << e.what();
}
Then you would need a throw std::runtime_error("Error receive response") somewhere in receiveFromServer().
It is often considered good practice (though some might disagree) to create your own exception-classes that derive from std::runtime_error to enable clients to catch your errors specifically.
You can either throw an exception (better way), or return boost::optional< string >, but then you have to check if the return value is valid (this is actually worse).
NULL has only a meaning when using pointers. In java strings are pointers so you can do that.
In C++, if you return an std::string, it must exist. So you have some possibilites
Throw an exception
Return a pair with a bool indicating the success
Return an empty string
Use a pointer (I strongly discourage that option)
Maybe you should try to handle with exceptions
try
{
string response = receiveFromServer();
}
catch (...)
{
cerr << "error recive response\n";
}
If you want to return an empty string you could also use the function
string::empty() to test if it is empty
I have a Visual Studio 2008 C++ project that uses a Win32Exception class in cases where there is an exceptional error. The Win32Exception class looks like this:
/// defines an exception based on Win32 error codes. The what() function will
/// return a formatted string returned from FormatMessage()
class Win32Exception : public std::runtime_error
{
public:
Win32Exception() : std::runtime_error( ErrorMessage( &error_code_ ) )
{
};
virtual ~Win32Exception() { };
/// return the actual error code
DWORD ErrorCode() const throw() { return error_code_; };
private:
static std::string ErrorMessage( DWORD* error_code )
{
*error_code = ::GetLastError();
std::string error_messageA;
wchar_t* error_messageW = NULL;
DWORD len = ::FormatMessageW( FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
*error_code,
MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
reinterpret_cast< LPWSTR >( &error_messageW ),
0,
NULL );
if( NULL != error_messageW )
{
// this may generate a C4244 warning. It is safe to ignore.
std::copy( error_messageW,
error_messageW + len,
std::back_inserter( error_messageA ) );
::LocalFree( error_messageW );
}
return error_messageA;
};
/// error code returned by GetLastError()
DWORD error_code_;
}; // class Win32Exception
The class works well in the situations it has been used in. What I would like to know is if there are any obvious cases where this will fail that I should be aware of. Any other gotchas, caveats, or general suggestions on improvements are welcome.
Please note that the boost library is not an option for this code.
This has already done by several people, including yours truly
https://github.com/BillyONeal/Instalog/blob/master/LogCommon/Win32Exception.hpp
https://github.com/BillyONeal/Instalog/blob/master/LogCommon/Win32Exception.cpp
Ironically, your code is not exception safe.
if( NULL != error_messageW )
{
// this may generate a C4244 warning. It is safe to ignore.
std::copy( error_messageW,
error_messageW + len,
std::back_inserter( error_messageA ) );
::LocalFree( error_messageW );
}
Note that if the back_inserter causes std::bad_alloc to be thrown, the memory allocated inside FormatMessage is leaked.
What a coincidence! I use a similar code in all my projects! It is actually a good idea.
This code is problematic:
// this may generate a C4244 warning. It is safe to ignore.
std::copy( error_messageW,
error_messageW + len,
std::back_inserter( error_messageA ) );
It just trancates WCHARs to chars. Your can either use FormatMessageA explicitly to get a message in the current code-page (ok, you can't as you said), or make convention that all your stings are UTF-8 encoded. I chose the later, see this why.
Error message by itself may be not useful. Capturing the stack trace may be a good idea.
Realize this is old, but at least with VC++ 2015 you can throw a system_error that will do all this with the system_category() function:
try
{
throw system_error(E_ACCESSDENIED, system_category(), "Failed to write file");
}
catch (exception& ex)
{
cout << ex.what();
}
This would print: "Failed to write file: Access is denied"
FormatMessage may itself fail. Some neutral "Unknown error with code %d" might be in order for such case.
Some error codes are not really errors (ERROR_ALREADY_EXISTS), depending on what user is expecting.
Some system functions return their own error codes (notable example being SHFileOperation) that you must handle separately. If you want them to be handled, that is.
Consider having additional information inside exception: where is exception being thrown from (source file and line), what system function caused exception, what were the parameters of the function (at least the identifying ones, like file name, handle value, or some such). Stack trace is also good.
What I would like to know is if there
are any obvious cases where this will
fail that I should be aware of. Any
other gotchas, caveats, or general
suggestions on improvements are
welcome.
The main I've problem I've had with such message retrieval has been ERROR_SUCCESS. It's rather perplexing when some operation fails, accompanied by error message "The operation succeeded". One wouldn't think that could happen, but it does.
I guess this is a special case of what Dialecticus noted, that "Some error codes are not really errors", but for most of those codes at least the message is generally acceptable.
The second problem is that most Windows system error message have a carriage return + linefeed at the end. It's problematic for insertion of messages into other text, and it breaks the convention for C++ exception messages. So, good idea to remove those chars.
Now, instead of repeating all that others have already noted, a few words about the design.
The ErrorMessage function would much more usable if was made public or moved out of the class, and took the error code by value, instead of taking pointer argument. This is the principle of keeping separate responsibilities separate. Promotes reuse.
The code in ErrorMessage would be more clear and safe and efficient if you used a destructor to deallocate the memory. Then you could also just construct the string directly in the return statement instead of using a copy loop with back inserter.
Cheers & hth.,
I was recently working on a very similar class and after reading this thread tried to make the copying part exception-safe. I introduced a little helper class that does nothing but hold the pointer to the string returned by ::FormatMessage and free it with ::LocalFree in its destructor. Copying, assigning and moving is not allowed, so one cannot get into trouble.
Here is what I came up with in total:
class windows_error {
public:
windows_error(wchar_t const* what);
// Getter functions
unsigned long errorCode() const { return _code; }
wchar_t const* description() const { return _what; }
std::wstring errorMessage() const { return _sys_err_msg; }
private:
unsigned long _code;
wchar_t const* _what;
std::wstring _sys_err_msg;
};
// This class outsources the problem of managing the string which
// was allocated with ::LocalAlloc by the ::FormatMessage function.
// This is necessary to make the constructor of windows_error exception-safe.
class LocalAllocHelper {
public:
LocalAllocHelper(wchar_t* string) : _string(string) { }
~LocalAllocHelper() {
::LocalFree(_string);
}
LocalAllocHelper(LocalAllocHelper const& other) = delete;
LocalAllocHelper(LocalAllocHelper && other) = delete;
LocalAllocHelper& operator=(LocalAllocHelper const& other) = delete;
LocalAllocHelper& operator=(LocalAllocHelper && other) = delete;
private:
wchar_t* _string;
};
windows_error::windows_error(wchar_t const* what)
: _code(::GetLastError()),
_what(what) {
// Create a temporary pointer to a wide string for the error message
LPWSTR _temp_msg = 0;
// Retrieve error message from error code and save the length
// of the buffer which is being returned. This is needed to
// implement the copy and assignment constructor.
DWORD _buffer_size = ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, _code, 0, _temp_msg, 0, NULL);
if(_buffer_size) {
// When calling _sys_err_msg.resize an exception could be thrown therefore
// the _temp_msg needs to be a managed resource.
LocalAllocHelper helper(_temp_msg);
_sys_err_msg.resize(_buffer_size + 1);
std::copy(_temp_msg, _temp_msg + _buffer_size, _sys_err_msg.begin());
}
else {
_sys_err_msg = std::wstring(L"Unknown error. (FormatMessage failed)");
}
}
Maybe this will be useful for some of you.