Могу ли я использовать шаблоны вместо макросов для создания классов исключений?

StackOverflow https://stackoverflow.com/questions/407881

  •  03-07-2019
  •  | 
  •  

Вопрос

Я часто хочу определить новые классы «Исключения», но мне нужно определить соответствующий конструктор, поскольку конструкторы не наследуются.

class MyException : public Exception 
{ 
public:
  MyException (const UString Msg) : Exception(Msg)
  {
  };
}

Определения типов для этого не подходят, потому что это просто псевдонимы, а не новые классы.В настоящее время, чтобы избежать повторения этого тривиального шаблона, я использую #define, который выполняет основную работу.

#define TEXCEPTION(T) class T : public Exception \
{ \
public:\
    T(const UString Msg) : Exception(Msg) {}; \
}

...

TEXCEPTION(MyException);

Но я все время задаюсь вопросом, есть ли лучший способ добиться этого - возможно, с помощью шаблонов или какой-то новой функции C++0x.

Это было полезно?

Решение

Если вы действительно хотите иметь новые классы, производные от 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 StackOverflow : ExceptionDefImpl { };

// alias for the common exceptions
typedef ExceptionT<MemoryLost> MemoryLostError;
typedef ExceptionT<StackOverflow> StackOverflowError;

void throws_mem() {
    throw MemoryLostError(boost::make_tuple(5));
}    

void throws_stack() { throw StackOverflowError(); }

int main() {
    try { throws_mem(); } 
    catch(MemoryListError &m) { std::cout << "Left: " << m.getLeftBytes(); }
    catch(StackOverflowError &m) { std::cout << "Stackoverflow happened"; }
}

Другие советы

Вы можете параметризовать свой класс шаблона целым числом:

#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...

// 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(":(");
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top