Vous pouvez réellement l'avoir dans les deux sens, c'est-à-dire avoir deux fonctions qui traduisent du code à l'erreur d'avant en arrière. La première chose est bien sûr que #define
ne devrait pas être utilisé pour les constantes, une énumération serait probablement la meilleure, mais une énumération ne peut pas être étendue, ce qui nécessite que toutes vos erreurs soient définies au même endroit (ouch, merci beaucoup pour les dépendances ...)
Vous pouvez le faire d'une autre manière, en utilisant des espaces de noms pour isoler les symboles, et un prétraitement pour gérer toute la génération. Pour la partie de recherche, nous utiliserons un Bimap.
nous devons d'abord définir une classe de gestionnaire (pas nécessaire inline tout cela, mais il est plus facile pour des exemples)
#include <boost/bimap.hpp>
#include <boost/optional.hpp>
namespace error
{
class Handler
{
public:
typedef boost::optional<int> return_code;
typedef boost::optional<std::string> return_description;
static bool Register(int code, const char* description)
{
typedef error_map::value_type value_type;
bool result = MMap().insert(value_type(code,description)).second;
// assert(result && description)
return result;
}
static return_code GetCode(std::string const& desc)
{
error_map::map_by<description>::const_iterator it
= MMap().by<description>().find(desc);
if (it != MMap().by<description>().end()) return it->second;
else return return_code();
}
static return_description GetDescription(int c)
{
error_map::map_by<code>::const_iterator it
= MMap().by<code>().find(c);
if (it != MMap().by<code>().end()) return it->second;
else return return_description();
}
typedef std::vector< std::pair<int,std::string> > errors_t;
static errors_t GetAll()
{
errors_t result;
std::for_each(MMap().left.begin(), MMap().left.end(),
result.push_back(boost::lambda::_1));
return result;
}
private:
struct code {};
struct description {};
typedef boost::bimap<
boost::tagged<int, code>,
boost::tagged<std::string, description>
> error_map;
static error_map& Map() { static error_map MMap; return MMap; }
};
// Short-Hand
boost::optional<int> GetCode(std::string const& d)
{
return Handler::GetCode(d);
}
boost::optional<std::string> GetDescription(int c)
{
return Handler::GetDescription(c);
}
} // namespace error
Ensuite, nous avons juste besoin de fournir un peu de sucre syntaxique:
#define DEFINE_NEW_ERROR(Code_, Description_) \
const int Description_ = Code_; \
namespace error { \
const bool Description##_Registered = \
::error::Handler::Register(Code_, #Description_); \
}
Nous pourrions être un peu plus violents en cas d'enregistrement d'une erreur inconnue (affirmer par exemple).
Et puis nous pouvons toujours envelopper cette macro dans une macro qui peut définir plusieurs symboles en une seule fois ... mais cela reste à titre d'exercice.
Utilisation:
// someErrors.hpp
#include "error/handler.hpp"
DEFINE_NEW_ERROR(1, AnError)
DEFINE_NEW_ERROR(2, AnotherError)
// someFile.cpp
#include <iostream>
#include "error/handler.hpp"
int main(int argc, char* argv[])
{
int code = 6;
boost::optional<std::string> desc = error::GetDescription(code);
if (desc)
{
std::cout << "Code " << code << " is mapped to <" << *desc << ">" << std::endl;
}
else
{
std::cout << "Code " << code << " is unknown, here is the list:\n";
::error::Handler::errors_t errors = ::Error::Handler::GetAll();
std::for_each(errors.begin(), errors.end(), std::cout << " " << _1);
std::cout << std::endl;
}
}
Disclaimer: Je ne suis pas trop sûr de la syntaxe lambda, mais il ne simplifié l'écriture.
Désolé, je devrais faire un addendum, après avoir vu certaines des réponses: Ce que j'aimerais pouvoir faire, c'est générer au moment de la compilation une liste de ces chaînes et d'y indexer. – brandonC
dupe de: http://stackoverflow.com/questions/2571816/is-it-possible-to-define-enumalpha –