Могу ли я использовать шаблоны вместо макросов для создания класса исключений?
Я часто хочу определить новые классы "Исключение", но мне нужно определить соответствующий конструктор, потому что конструкторы не наследуются.
class MyException : public Exception
{
public:
MyException (const UString Msg) : Exception(Msg)
{
};
}
Typedefs не работают для этого, потому что они просто псевдонимы, а не новые классы. В настоящее время, чтобы избежать повторения этого тривиального шаблона, я использую #define, который выполняет ословую работу.
#define TEXCEPTION(T) class T : public Exception \
{ \
public:\
T(const UString Msg) : Exception(Msg) {}; \
}
...
TEXCEPTION(MyException);
Но я продолжаю задаваться вопросом, есть ли лучший способ достичь этого - возможно, с помощью шаблонов или какой-то новой функции C++0x
3 ответа
Если вы действительно хотите иметь новые классы, производные от Exception, в отличие от шаблона, параметризованного параметром, нет способа написать собственный конструктор, который просто делегирует аргументы без использования макроса. C++0x будет иметь возможность, что вам нужно, используя что-то вроде
class MyException : public Exception
{
public:
using Exception::Exception;
};
Вы можете прочитать о деталях этого (кажется, есть довольно много дополнительных правил) в 12.9 "Наследование конструкторов" в последнем проекте C++ 0x.
В то же время, я бы порекомендовал дизайн, основанный на политике (сделал небольшой текст, потому что ФП принял вышеизложенное, а не этот материал политики):
// deriving from Impl first is crucial, so it's built first
// before Exception and its Ctor can be used.
template<typename Impl>
struct ExceptionT : Impl, Exception {
// taking a tuple with the arguments.
ExceptionT(arg_types const& t = arg_types())
:Exception(Impl::Ctor(t)) { }
// taking a string. plain old stuff
ExceptionT(std::string const& s):Exception(Impl::Ctor(s)) { }
};
struct ExceptionDefImpl {
typedef boost::tuple<> arg_types;
// user defined ctor args can be done using a tuple
std::string Ctor(arg_types const& s) {
return std::string();
}
std::string const& Ctor(std::string const& s) {
return s;
}
};
// will inherit Ctor modifier from DefImpl.
struct MemoryLost : ExceptionDefImpl {
typedef boost::tuple<int> arg_types;
std::string Ctor(arg_types const& s) {
std::ostringstream os;
os << "Only " << get<0>(s) << " bytes left!";
return os.str();
}
int getLeftBytes() const { return leftBytes; }
private:
int leftBytes;
};
struct Stackru : ExceptionDefImpl { };
// alias for the common exceptions
typedef ExceptionT<MemoryLost> MemoryLostError;
typedef ExceptionT<Stackru> StackruError;
void throws_mem() {
throw MemoryLostError(boost::make_tuple(5));
}
void throws_stack() { throw StackruError(); }
int main() {
try { throws_mem(); }
catch(MemoryListError &m) { std::cout << "Left: " << m.getLeftBytes(); }
catch(StackruError &m) { std::cout << "Stackru happened"; }
}
// You could put this in a different scope so it doesn't clutter your namespaces.
template<struct S> // Make S different for different exceptions.
class NewException :
public Exception
{
public:
NewException(const UString Msg) :
Exception(Msg)
{
}
};
// Create some new exceptions
struct MyExceptionStruct; typedef NewException<MyExceptionStruct> MyException;
struct YourExceptionStruct; typedef NewException<YourExceptionStruct> YourException;
struct OurExceptionStruct; typedef NewException<OurExceptionStruct> OurException;
// Or use a helper macro (which kinda defeats the purpose =])
#define MAKE_EXCEPTION(name) struct name##Struct; typedef NewException<name##Struct> name;
MAKE_EXCEPTION(MyException);
MAKE_EXCEPTION(YourException);
MAKE_EXCEPTION(OurException);
// Now use 'em
throw new MyException(":(");
Вы можете параметризовать свой шаблонный класс целым числом:
#include <iostream>
#include <string>
using namespace std;
enum ExceptionId {
EXCEPTION_FOO,
EXCEPTION_BAR
};
class Exception {
string msg_;
public:
Exception(const string& msg) : msg_(msg) { }
void print() { cout << msg_ << endl; }
};
template <int T>
class TException : public Exception {
public:
TException(const string& msg) : Exception(msg) {};
};
void
foo()
{
throw TException<EXCEPTION_FOO>("foo");
}
void
bar()
{
throw TException<EXCEPTION_BAR>("bar");
}
int
main(int argc, char *argv[])
{
try {
foo();
} catch (TException<EXCEPTION_FOO>& e) {
e.print();
};
try {
bar();
} catch (TException<EXCEPTION_BAR>& e) {
e.print();
};
return 0;
}
Хотя я не понимаю, почему вы предпочли бы это по сравнению с использованием одного класса с внутренним перечислением, которое устанавливается / читается во время выполнения:
class TException {
public:
enum Type { FOO, BAR };
TException(Type type, const string& msg) : Exception(msg), type_(type) {}
Type type() const { return type_; }
private:
Type type_;
};
Затем просто включите тип, когда вы ловите TException...