Pregunta

A menudo quiero definir nuevas clases de 'Excepción', pero necesito tener un constructor apropiado definido porque los constructores no se heredan.

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

Los typedefs no funcionan para esto, porque son simplemente alias, no clases nuevas. Actualmente, para evitar repetir este trivial boiler, uso un #define que hace el trabajo de burro.

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

...

TEXCEPTION(MyException);

Pero sigo preguntándome si hay una mejor manera de lograrlo, tal vez con plantillas o alguna nueva función de C ++ 0x

¿Fue útil?

Solución

Si realmente desea tener nuevas clases derivadas de Exception, en lugar de tener una plantilla parametrizada por un parámetro, no hay forma de escribir su propio constructor que simplemente delega los argumentos sin usar una macro. C ++ 0x tendrá la capacidad que necesitas al usar algo como

class MyException : public Exception 
{ 
public:
    using Exception::Exception;
};

Puedes leer sobre los detalles de eso (parece tener un poco de reglas adicionales) en 12.9 " Inheriting Constructors " en el último borrador de C ++ 0x .

Mientras tanto, recomendaría un diseño basado en políticas (texto pequeño, porque el OP aceptó lo anterior, y no esta política):

// 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"; }
}

Otros consejos

Podría parametrizar su clase de plantilla con un entero:

#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;
}

Aunque, no veo por qué favorecería esto sobre el uso de una sola clase con una enumeración interna que se establece / lee en tiempo de ejecución:

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_;
};

Luego, simplemente active el tipo cuando detecte una 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(":(");
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top