I inherited a bit of code in Python to convert to C++. The Python code throws an exception based on a error code returned by other functions. The Python code uses a utility function called errors(...)
that contains creates a dictionary to map error codes to exceptions. It that returns the type of the exception and the calling code instantiates it with its own message.
class BaseError(Exception):
pass
class Error1(BaseError):
pass
class Error2(BaseError):
pass
def errors(code):
errors_ = {
1: BaseError,
2: Error1,
3: Error2
}
try:
return errors_[code]
except KeyError:
return errors_[1]
def ReturnAnError():
# Oops an error!
return 2
try:
val = ReturnAnError()
if(val):
raise errors(val)('{} Failed'.format("Example Failed"))
except BaseError as ex:
print(ex)
My initial thought is to take an easy path and redefine the Python errors(code)
function as void errors(uint8_t code, const char * msg)
in c++ and then make a select statement that will throw the exception itself.
Is there a more elegant or concise solution to translate this code directly? In particular, what is the most direct translation of raise errors(val)('{} Failed'.format("Example Failed"))
in c++?
If I directly translate your code in C++ :
#include <iostream>
#include <string>
#include <map>
class BaseError {
public:
BaseError(std::string s) : msg(s) {}
virtual ~BaseError() {} // useless because inherited classes do not have attribute to delete
friend std::ostream & operator<<(std::ostream & out, const BaseError & e) {
out << e.msg;
return out;
}
static BaseError * mk(std::string s) { return new BaseError(s); }
private:
std::string msg;
};
class Error1 : public BaseError {
public:
Error1(std::string s) : BaseError(s) {}
static BaseError * mk(std::string s) { return new Error1(s); }
};
class Error2 : public Error1 {
public:
Error2(std::string s) : Error1(s) {}
static BaseError * mk(std::string s) { return new Error2(s); }
};
typedef BaseError * (*fmk)(std::string);
fmk errors(int code)
{
const static std::map<int, fmk> error = {
{1, &BaseError::mk},
{2, &Error1::mk},
{3, &Error2::mk}
};
std::map<int, fmk>::const_iterator it = error.find(code);
return ((it == error.end()) ? error.find(1) : it)->second;
}
int ReturnAnError()
{
// Oops an error!
return 2;
}
int main()
{
try {
int val = ReturnAnError();
if (val)
throw (errors(val))("blah blah");
}
catch (BaseError * ex) {
std::cout << *ex << std::endl;
delete ex;
}
}
Compilation and execution:
pi@raspberrypi:/tmp $ g++ c0.cc
pi@raspberrypi:/tmp $ ./a.out
blah blah
pi@raspberrypi:/tmp $
About the function errors :
The Python dictionary can be translated to C++ std::map
As I know contrarily to Python I cannot put in the std::map
the address of the constructor of each class, this is why I used a static operation mk for each class.
To avoid to create the std::map
each time errors is called I defined error static
(and const
to clearly indicate I do not want to modify it), but this is an optimization and this is not mandatory.
In Python the exceptions are very used, this is less the case in C++, this is why I use an iterator to know if the code is a known key, and I test it.
To have a way to print the instances of the classes I overloaded the operator<<
, anyway that does not allow to check the program created an Error1, and even I change the code to have :
try {
int val = ReturnAnError();
if (val)
throw (errors(val))("blah blah");
}
catch (Error2 * ex) {
std::cout << "err2" << *ex << std::endl;
delete ex;
}
catch (Error1 * ex) {
std::cout << "err1" << *ex << std::endl;
delete ex;
}
catch (BaseError * ex) {
std::cout << *ex << std::endl;
delete ex;
}
the executed code will be catch (BaseError * ex) {...}
If I do :
try {
int val = ReturnAnError();
if (val)
throw *(errors(val))("blah blah");
}
catch (Error2 & ex) {
std::cout << "err2" << ex << std::endl;
}
catch (Error1 & ex) {
std::cout << "err1" << ex << std::endl;
}
catch (BaseError & ex) {
std::cout << ex << std::endl;
}
again the executed code will be catch (BaseError & ex) {...}
(and I created a memory leak).
So a virtual
operation is needed to differentiate the classes when pritting, for instance :
#include <iostream>
#include <string>
#include <map>
class BaseError {
public:
BaseError(std::string s) : msg(s) {}
virtual ~BaseError() {} // useless because inherited classes do not have attribute to delete
virtual void print(std::ostream & out) const { out << msg; }
static BaseError * mk(std::string s) { return new BaseError(s); }
private:
std::string msg;
};
class Error1 : public BaseError {
public:
Error1(std::string s) : BaseError(s) {}
virtual void print(std::ostream & out) const {
out << "error1 ";
BaseError::print(out);
}
static BaseError * mk(std::string s) { return new Error1(s); }
};
class Error2 : public Error1 {
public:
Error2(std::string s) : Error1(s) {}
virtual void print(std::ostream & out) const {
out << "error2 ";
BaseError::print(out);
}
static BaseError * mk(std::string s) { return new Error2(s); }
};
typedef BaseError * (*fmk)(std::string);
fmk errors(int code)
{
const static std::map<int, fmk> error = {
{1, &BaseError::mk},
{2, &Error1::mk},
{3, &Error2::mk}
};
std::map<int, fmk>::const_iterator it = error.find(code);
return ((it == error.end()) ? error.find(1) : it)->second;
}
int ReturnAnError()
{
// Oops an error!
return 2;
}
int main()
{
try {
int val = ReturnAnError();
if (val)
throw (errors(val))("blah blah");
}
catch (BaseError * ex) {
ex->print(std::cout);
std::cout << std::endl;
delete ex;
}
}
Compilation and execution:
pi@raspberrypi:/tmp $ g++ c.cc
pi@raspberrypi:/tmp $ ./a.out
error1 blah blah
pi@raspberrypi:/tmp $
Is there a more elegant or concise solution
To be frank this is at least not concise, but your Python code is not too ;-)