Search code examples
pythonc++code-translation

Python Returning Types Translated to C++


I inherited a bit of code in Python to convert to C++. The Python code throws an exception based on a error code returned by other functions. The Python code uses a utility function called errors(...) that contains creates a dictionary to map error codes to exceptions. It that returns the type of the exception and the calling code instantiates it with its own message.

class BaseError(Exception):
    pass

class Error1(BaseError):
    pass

class Error2(BaseError):
    pass

def errors(code):
    errors_ = {
        1: BaseError,
        2: Error1,
        3: Error2
    }

    try:
        return errors_[code]
    except KeyError:
        return errors_[1]

def ReturnAnError():
    # Oops an error!
    return 2

try:
    val = ReturnAnError()
    if(val):
        raise errors(val)('{} Failed'.format("Example Failed"))
except BaseError as ex:
    print(ex)

My initial thought is to take an easy path and redefine the Python errors(code) function as void errors(uint8_t code, const char * msg) in c++ and then make a select statement that will throw the exception itself.

Is there a more elegant or concise solution to translate this code directly? In particular, what is the most direct translation of raise errors(val)('{} Failed'.format("Example Failed")) in c++?


Solution

  • If I directly translate your code in C++ :

    #include <iostream>
    #include <string>
    #include <map>
    
    class BaseError {
      public:
        BaseError(std::string s) : msg(s) {}
        virtual ~BaseError() {} // useless because inherited classes do not have attribute to delete
        friend std::ostream & operator<<(std::ostream & out, const BaseError & e) {
           out << e.msg;
           return out;
        }
        static BaseError * mk(std::string s) { return new BaseError(s); }
      private:
        std::string msg;
    };
    
    class Error1 : public BaseError {
      public:
        Error1(std::string s) : BaseError(s) {}
        static BaseError * mk(std::string s) { return new Error1(s); }
    };
    
    class Error2 : public Error1 {
      public:
        Error2(std::string s) : Error1(s) {}
        static BaseError * mk(std::string s) { return new Error2(s); }
    };
    
    typedef BaseError * (*fmk)(std::string);
    
    fmk errors(int code) 
    {
      const static std::map<int, fmk> error = {
           {1, &BaseError::mk},
           {2, &Error1::mk},
           {3, &Error2::mk}
      };
      std::map<int, fmk>::const_iterator it = error.find(code);
      
      return ((it == error.end()) ? error.find(1) : it)->second;
    }
    
    int ReturnAnError()
    {
      // Oops an error!
      return 2;
    }
    
    
    int main()
    {
      try {
        int val = ReturnAnError();
      
        if (val)
          throw (errors(val))("blah blah");
      }
      catch (BaseError * ex) {
          std::cout << *ex << std::endl;
          delete ex;
      }
    }    
    

    Compilation and execution:

    pi@raspberrypi:/tmp $ g++ c0.cc
    pi@raspberrypi:/tmp $ ./a.out
    blah blah
    pi@raspberrypi:/tmp $ 
    

    About the function errors :

    • The Python dictionary can be translated to C++ std::map

    • As I know contrarily to Python I cannot put in the std::map the address of the constructor of each class, this is why I used a static operation mk for each class.

    • To avoid to create the std::map each time errors is called I defined error static (and const to clearly indicate I do not want to modify it), but this is an optimization and this is not mandatory.

    • In Python the exceptions are very used, this is less the case in C++, this is why I use an iterator to know if the code is a known key, and I test it.

    To have a way to print the instances of the classes I overloaded the operator<<, anyway that does not allow to check the program created an Error1, and even I change the code to have :

      try {
        int val = ReturnAnError();
      
        if (val)
          throw (errors(val))("blah blah");
      }
      catch (Error2 * ex) {
          std::cout << "err2" << *ex << std::endl;
          delete ex;
      }
      catch (Error1 * ex) {
          std::cout << "err1" << *ex << std::endl;
          delete ex;
      }
      catch (BaseError * ex) {
          std::cout << *ex << std::endl;
          delete ex;
      }
    

    the executed code will be catch (BaseError * ex) {...}

    If I do :

      try {
        int val = ReturnAnError();
      
        if (val)
          throw *(errors(val))("blah blah");
      }
      catch (Error2 & ex) {
          std::cout << "err2" << ex << std::endl;
      }
      catch (Error1 & ex) {
          std::cout << "err1" << ex << std::endl;
      }
      catch (BaseError & ex) {
          std::cout << ex << std::endl;
      }
    

    again the executed code will be catch (BaseError & ex) {...} (and I created a memory leak).

    So a virtual operation is needed to differentiate the classes when pritting, for instance :

    #include <iostream>
    #include <string>
    #include <map>
    
    class BaseError {
      public:
        BaseError(std::string s) : msg(s) {}
        virtual ~BaseError() {} // useless because inherited classes do not have attribute to delete
        virtual void print(std::ostream & out) const { out << msg; }
        static BaseError * mk(std::string s) { return new BaseError(s); }
      private:
        std::string msg;
    };
    
    class Error1 : public BaseError {
      public:
        Error1(std::string s) : BaseError(s) {}
        virtual void print(std::ostream & out) const { 
          out << "error1 ";
          BaseError::print(out);
        }
        static BaseError * mk(std::string s) { return new Error1(s); }
    };
    
    class Error2 : public Error1 {
      public:
        Error2(std::string s) : Error1(s) {}
        virtual void print(std::ostream & out) const { 
          out << "error2 ";
          BaseError::print(out);
        }
        static BaseError * mk(std::string s) { return new Error2(s); }
    };
    
    typedef BaseError * (*fmk)(std::string);
    
    fmk errors(int code) 
    {
      const static std::map<int, fmk> error = {
           {1, &BaseError::mk},
           {2, &Error1::mk},
           {3, &Error2::mk}
      };
      std::map<int, fmk>::const_iterator it = error.find(code);
      
      return ((it == error.end()) ? error.find(1) : it)->second;
    }
    
    int ReturnAnError()
    {
      // Oops an error!
      return 2;
    }
    
    
    int main()
    {
      try {
        int val = ReturnAnError();
      
        if (val)
          throw (errors(val))("blah blah");
      }
      catch (BaseError * ex) {
          ex->print(std::cout);
          std::cout << std::endl;
          delete ex;
      }
    }    
    

    Compilation and execution:

    pi@raspberrypi:/tmp $ g++ c.cc
    pi@raspberrypi:/tmp $ ./a.out
    error1 blah blah
    pi@raspberrypi:/tmp $ 
    

    Is there a more elegant or concise solution

    To be frank this is at least not concise, but your Python code is not too ;-)