Puis-je utiliser des modèles au lieu de macros pour la création de classe Exception?
-
03-07-2019 - |
Question
Je souhaite souvent définir de nouvelles classes "Exception", mais il est nécessaire de définir un constructeur approprié, car les constructeurs ne sont pas hérités.
class MyException : public Exception
{
public:
MyException (const UString Msg) : Exception(Msg)
{
};
}
Les typedefs ne fonctionnent pas pour cela, car ce sont simplement des alias, pas de nouvelles classes. Actuellement, pour éviter de répéter ce passe-partout trivial, j'utilise un #define qui fait le travail d'âne.
#define TEXCEPTION(T) class T : public Exception \
{ \
public:\
T(const UString Msg) : Exception(Msg) {}; \
}
...
TEXCEPTION(MyException);
Mais je me demande sans cesse s'il existe un meilleur moyen d'y parvenir - peut-être avec des modèles ou une nouvelle fonctionnalité C ++ 0x
La solution
Si vous voulez vraiment que de nouvelles classes soient dérivées d'Exception, par opposition à un modèle paramétré par un paramètre, vous n'avez aucun moyen d'écrire votre propre constructeur qui délègue simplement les arguments sans utiliser de macro. C ++ 0x aura la capacité dont vous avez besoin en utilisant quelque chose comme
class MyException : public Exception
{
public:
using Exception::Exception;
};
Vous pouvez en savoir plus sur les détails de cette opération (vous semblez avoir pas mal de règles supplémentaires) dans 12.9 "Constructeurs héritiers". dans le dernier brouillon de C ++ 0x .
En attendant, je recommanderais une conception basée sur des règles (rédigée en petit texte, car le PO a accepté ce qui précède, et non ce type de règles):
// 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"; }
}
Autres conseils
Vous pouvez paramétrer votre classe de modèle avec un entier:
#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;
}
Bien que, je ne vois pas pourquoi vous préféreriez cela plutôt que d'utiliser une seule classe avec une énumération interne définie / lue à l'exécution:
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_;
};
Activez simplement le type lorsque vous attrapez une exception 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(":(");