Possible to convert list of #defines into strings

前端 未结 8 1103
梦谈多话
梦谈多话 2021-02-05 22:45

Suppose I have a list of #defines in a header file for an external library. These #defines represent error codes returned from functions. I want to wri

8条回答
  •  -上瘾入骨i
    2021-02-05 23:19

    You can actually have it both ways, ie having two functions that translate from code to error back and forth.

    The first thing of course is that #define should not be used for constants, an enum would probably be best, however an enum cannot be extended, which requires that all your errors be defined in the same place (ouch, thank you so much for dependencies...)

    You can do it another may though, using namespaces to isolate the symbols, and preprocessing to handle the whole generation. For the lookup part, we'll use a Bimap.

    First we need to define a Handler class (not necessary to inline all that, but it's easier for examples)

    #include 
    #include 
    
    namespace error
    {
    
      class Handler
      {
      public:
        typedef boost::optional return_code;
        typedef boost::optional 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::const_iterator it
              = MMap().by().find(desc);
          if (it != MMap().by().end()) return it->second;
          else return return_code();
        }
    
        static return_description GetDescription(int c)
        {
          error_map::map_by::const_iterator it
              = MMap().by().find(c);
          if (it != MMap().by().end()) return it->second;
          else return return_description();
        }
    
        typedef std::vector< std::pair > 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,
          boost::tagged
        > error_map;
    
        static error_map& Map() { static error_map MMap; return MMap; }
      };
    
      // Short-Hand
      boost::optional GetCode(std::string const& d)
      {
        return Handler::GetCode(d);
      }
    
      boost::optional GetDescription(int c)
      { 
        return Handler::GetDescription(c);
      }
    } // namespace error
    

    Then we just need to provide some syntactic sugar:

    #define DEFINE_NEW_ERROR(Code_, Description_)            \
      const int Description_ = Code_;                        \
      namespace error {                                      \
        const bool Description##_Registered =                \
          ::error::Handler::Register(Code_, #Description_);  \
      }
    

    We could be a bit more violent in case of a registration of an unknown error (assert for example).

    And then we can always wrap that macro into one that can define multiple symbols in one go... but that's left as an exercise.

    Usage:

    // someErrors.hpp
    #include "error/handler.hpp"
    
    DEFINE_NEW_ERROR(1, AnError)
    DEFINE_NEW_ERROR(2, AnotherError)
    
    // someFile.cpp
    #include 
    #include "error/handler.hpp"
    
    int main(int argc, char* argv[])
    {
      int code = 6;
      boost::optional 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: I am not too sure about the lambda syntax, but it did simplified the writing.

提交回复
热议问题