Python 返回转换为 C++ 的类型

Python Returning Types Translated to C++

我继承了 Python 中的一些代码以转换为 C++。 Python 代码根据其他函数返回的错误代码抛出异常。 Python 代码使用一个名为 errors(...) 的实用函数,该函数包含创建一个字典以将错误代码映射到异常。 returns 异常的类型和调用代码用自己的消息实例化它。

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)

我最初的想法是走一条简单的路,在 C++ 中将 Python errors(code) 函数重新定义为 void errors(uint8_t code, const char * msg),然后创建一个 select 语句,该语句将抛出异常本身。

是否有更优雅或更简洁的解决方案来直接翻译这段代码?特别是raise errors(val)('{} Failed'.format("Example Failed"))在c++中最直接的翻译是什么?

如果我直接用 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;
  }
}    

编译执行:

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

关于函数 错误 :

  • Python字典可以翻译成C++ std::map

  • 据我所知,与 Python 相反,我不能在 std::map 中放入每个 class 的构造函数的地址,这就是为什么我使用 static 操作 mk 每个 class.

  • 为了避免每次调用 errors 时创建 std::map 我定义了 error static(和 const 明确表示我不想修改它),但这是一个优化,这不是强制性的。

  • 在Python中非常常用异常,在C++中很少出现这种情况,这就是为什么我使用迭代器来知道代码是否是已知密钥,并且我测试它。

为了打印 classes 的实例,我重载了 operator<<,无论如何不允许检查程序创建了一个 Error1,甚至我将代码更改为:

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

执行的代码将是catch (BaseError * ex) {...}

如果我这样做:

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

再次执行的代码将是 catch (BaseError & ex) {...}(我造成了内存泄漏)。

所以打印时需要virtual操作来区分classes,例如:

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

编译执行:

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

坦率地说,这至少不够简洁,但您的 Python 代码也不太简洁 ;-)

一个函数,每个参数的名称和类型完全相同,不能有两个 return 类型。所以没有办法允许 errors return 不同类型的异常。即 errors 无法准确翻译。

但对于 raise errors(val)("..."),一种简单的方法是 throw errors 中的异常。示例代码:

#include <string>
class BaseError {
public:
    std::string s;
    BaseError(const std::string &_s):s(_s){}
};
class Error1 : public BaseError {
public:
    Error1(const std::string &s):BaseError(s){}
};
class Error2 : public BaseError {
public:
    Error2(const std::string &s):BaseError(s){}
};

void errors(int val, const std::string &s)
{
    switch(val)
    {
    case 1:
        throw BaseError(s);
    case 2:
        throw Error1(s);
    case 3:
        throw Error2(s);
    default:
        throw BaseError(s);
    }
}

或者如果需要 object 可以 throw 在其他地方,一种方法是创建 class 可以 throw 例外。示例代码:

class errors {
public:
    int val;
    std::string s;
    errors(int _val, const std::string _s): val(_val), s(_s){}
    void raise()
    {
        switch(val)
        {
        case 1:
            throw BaseError(s);
        case 2:
            throw Error1(s);
        case 3:
            throw Error2(s);
        default:
            throw BaseError(s);
        }
    }
};