this is a question for “best practice”. I want to create C++ classes and then use interrogate to make them available at the Python level too. The problem: the class constructors can fail. The question: what is the best practice to make this “fail” look good both on the C++ level and on the Python level (traceback would be nice)?
So far I know these possibilities, but each has drawbacks.
(1) “throw” an exception. This is the preferred C++ way. But if used from Python I get no traceback, just a crash with a message about a runtime problem.
(2) Use a “zombie” state for the object. For example like done in NodePath (member _error_state set to ET_fail). Drawbacks: the constructor fail silently and every other method has to check the error state before executing, which gets ugly.
Any other ideas? Any input is appreciated.
What about Panda3D’s assertions (nassert,nassertv)? They throw a nice python exception as well.
Thanks for the tip. I already have tried this, and it seems I don’t use the Panda3D asserts in the right way. I have these lines in my constructor:
nassertv( true );
nassertv( false );
cout << "passed asserts\n";
“nassertv” because the constructor doesn’t return a value.
Include is “pnotify.h”.
Either the first or the second assert should fire, but when creating an object from Python both asserts pass and I get the following cout line.
Any idea how to use them right?
Well, actually, only the second assert should fire, because the semantics of assert is to fire only when its condition is false. (You are asserting that the condition must be true, and so it is an error condition if it is not true.)
But if it is not firing, it must be because you have disabled asserts in your compilation. Could you be compiling with OPTIMIZE 4? If so, I strongly recommend using OPTIMIZE 3 instead. OPTIMIZE 4 is intended only for compilation of the production, finished product, which has already been fully developed and debugged. When you compile with OPTIMIZE 4, all the asserts are removed, which includes lots of parameter checking and other useful features. If you then try to develop with this Panda in Python, the first NodePath call you make with the wrong parameters will crash hard, instead of raising a Python exception–making it very inconvenient to develop in real time.
Ok, this has set me on the right track. The evildoer has been that I defined NDEBUG. I removed this define, and reduced optimization level, and it works now. Thanks to both of you.
When we have classes where the constructor could fail, usually we use static factory functions instead of having the python program call the constructor directly. If there’s a failure, the factory function returns NULL.
Using a factory is what I will do. I feel a bit silly, since I really should have know this myself.
Right now I am having some problems with non-default constructors. More concrete two constructors for the same class, with one argument each, an PNMImage in the first case, and an unsigned int in the second. It seems the code created by interrogate (…_igate.cxx) tries in this case both constructors, until one succeeds, or a crash happens. But I will have to hunt this down better before I ask for help.
Not really. The code generated by interrogate will walk through the arguments provided and try to match them against the different function prototypes available. When it finds the first match, it will call that function, once, which will either succeed or fail.
It will not call multiple functions. This is true for any overloaded functions, constructor or otherwise.