Posso utilizzare i modelli anziché le macro per la creazione della classe di eccezione?

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

  •  03-07-2019
  •  | 
  •  

Domanda

Spesso desidero definire nuove classi di "eccezione", ma ho bisogno di definire un costruttore appropriato perché i costruttori non sono ereditati.

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

I typedef non funzionano per questo, perché sono semplicemente alias, non nuove classi. Attualmente, per evitare di ripetere questo banale scaldabagno, uso un #define che fa l'asino.

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

...

TEXCEPTION(MyException);

Ma continuo a chiedermi se esiste un modo migliore per raggiungere questo obiettivo, magari con i modelli o alcune nuove funzionalità di C ++ 0x

È stato utile?

Soluzione

Se vuoi davvero avere nuove classi derivate da Exception, al contrario di avere un template parametrizzato da un parametro, non c'è modo di scrivere il tuo costruttore che deleghi gli argomenti senza usare una macro. C ++ 0x avrà la capacità di cui hai bisogno usando qualcosa come

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

Puoi leggere i dettagli di questo (sembra avere un bel po 'di regole extra) in 12.9 "Ereditando i costruttori". nella ultima bozza di C ++ 0x .

Nel frattempo, consiglierei una progettazione basata su criteri (resa in piccolo testo, perché il PO ha accettato quanto sopra e non questa roba politica):

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

Altri suggerimenti

È possibile parametrizzare la classe del modello con un numero intero:

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

Anche se, non vedo perché preferiresti questo rispetto all'uso di una singola classe con un'enumerazione interna impostata / letta in fase di esecuzione:

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

Quindi attiva il tipo quando catturi 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(":(");
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top