wlav / cppyy

Other
400 stars 41 forks source link

Exception message not properly propagated from inner nested class/struct #79

Closed ikabadzhov closed 1 year ago

ikabadzhov commented 2 years ago

I realized that I cannot correctly printout my exceptions in case of nested classes.

If I have a class taking a nested class in ints ctor, I am getting an unclear message.

Minimal reproducer:

import cppyy
cppyy.cppdef("""
#include <stdexcept>

struct A {
    struct B {
        int x;
        B(int xx) : x(xx) {
            if (x > 42)
                throw std::logic_error("My error!");
        }
    };
    B b;
    A(B bb) : b(bb) {}

};""")

from cppyy.gbl import A

b = A.B(200) # this throws the expected logical error:
# cppyy.gbl.std.logic_error: B::B(int xx) =>
#     logic_error: My error!

a = A(200) # throws:
# TypeError: none of the 3 overloaded methods succeeded. Full details:
#   A::A(A&&) =>
#     TypeError: could not convert argument 1
#   A::A(A::B bb) =>
#     TypeError: could not convert argument 1
#   A::A(const A&) =>
#     TypeError: could not convert argument 1

The behaviour that I expect is, as in C++ in both cases to show the message, i.e.:

terminate called after throwing an instance of 'std::logic_error'
  what():  My error!

It looks very much like a cppyy issue.

Related question: I wonder if there is a recommended workaround for such cases. I still want to be throwing from the inner class since it could be used on its own. But I want calling the ctor of the outer class to correctly complain.

wlav commented 1 year ago

The issue isn't nested classes, but rather that the second callrequires an implicit conversion, which is a concept that doesn't exist in Python. There is an implicit conversion mechanism in cppyy, but it fails b/c of the exception. The error reporting then still has to be at the level of A::A of which there are 3 overloads, not at the level of the single B failing. In C++, it's different b/c the overload is matched at compile time using the implicit conversion. Hence only 1 overload is ever tried, and it fails then as expected.

Only possible improvement might be to have A::A(A::B bb) report the C++ exception as part of the TypeError, but it makes no sense to have A(200) report only cppyy.gbl.std.logic_error from that single overload.