Если я напрямую переведу ваш код на 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;
}
}
Компиляция и выполнение:
pi@raspberrypi:/tmp $ g++ c0.cc
pi@raspberrypi:/tmp $ ./a.out
blah blah
pi@raspberrypi:/tmp $
О функции ошибки :
Словарь Python может быть переведен на C ++ std::map
Насколько я знаю, в отличие от Python, я не могу указать в std::map
адрес конструктора каждого класса, поэтому я использовал stati c операцию mk для каждого класса.
Чтобы избежать создавать std::map
каждый раз, когда вызывается ошибок Я определил error static
(и const
, чтобы четко указать, что я не хочу его изменять), но это оптимизация и это не обязательно.
В Python исключения очень часто используются, это не так в C ++, поэтому я использую итератор, чтобы узнать, является ли код известный ключ, и я тестирую его.
Чтобы иметь возможность распечатать экземпляры классов, я перегрузил operator<<
, в любом случае это не позволяет t o проверьте, что программа создала Error1 , и даже если я изменил код, чтобы он имел:
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;
}
, выполненный код будет catch (BaseError * ex) {...}
Если я это сделаю:
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;
}
снова исполняемый код будет catch (BaseError & ex) {...}
(и я создал утечку памяти).
Итак, операция virtual
необходима, чтобы различать классы, например, при установке :
#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;
}
}
Компиляция и исполнение:
pi@raspberrypi:/tmp $ g++ c.cc
pi@raspberrypi:/tmp $ ./a.out
error1 blah blah
pi@raspberrypi:/tmp $
Есть ли более элегантное или лаконичное решение
Честно говоря, это как минимум не лаконично, но ваш код Python не слишком; -)