I have a C++ class with a pure virtual method exposed to python using boost-python. I am calling the virtual function from C++ and assuming the virtual function is implemented in python. It all works if the function is implemented but if it's not I get a nasty exception.
I am trying to find a way to detect if the method is in fact implemented without calling when I load the class
Roughly the code looks like this
#include <boost/python.hpp>
using namespace boost::python;
public Foo {
public:
void func() = 0;
}
class PyFoo : public Foo, public boost::python::wrapper<Foo> {
public:
void func() override {
get_override("func")();
}
};
BOOST_PYTHON_MODULE(example)
{
using namespace boost::python;
class_<PyFoo>, boost::noncopyable>("Foo")
.def("func", pure_virtual(&PyFoo::func))
;
}
void create {
object main_module = import("__main__");
object main_namespace = main_module.attr("__dict__");
std::string overrideCommand(
R"(
import example
class MyFoo(example.Foo):
def __init__(self):
example.Foo.__init__(self)
# virtual function in C++. (Should be defined)
# def func(self):
# print('func called')
)");
boost::python::exec(overrideCommand.c_str(), main_namespace);
result = eval("MyFoo()", main_namespace);
// Can I detect if 'result' has func implemented? If I call it and it
// is not defined death results. I have tried:
object attr = result.attr("func");
// but attr always seems to be set even if there is no function,
// I think from the base class Foo.
// This is the call:
Foo& t = extract<Foo&>(result);
t.func();
}
You can use PyCallable_Check.
if (!PyCallable_Check(result.func()))
{
PyErr_SetString(PyExc_TypeError, error_msg.str().c_str());
python::throw_error_already_set();
}
I found a solution. Workable not elegant. I added the method:
bool isThere() {
auto obj = get_override("func");
return PyCallable_Check(obj.ptr());
}
to FooPy. Then:
FooPy& t = extract<FooPy&>(result);
t.isThere();
Related
My goal is to construct a derived classes nested class from the interface. However the nested classes don't have the same constructors. The question is how can I make an interface to create two different "sub-nested" classes.
Constraints:
Cannot use Heap
Nested Classes' Methods cannot be called before it is constructed
C++ 17
ITest::INestedTest* MakeTest(ITest* test, ITest::Config config)
{
// Can't call directly because it's not on the interface i.e. test.InitializeNestedTest ...
// Only workable situation is this:
if (condition)
{
auto myTest = static_cast<Test2::Test*>(test);
int p = 2;
return myTest->InitalizeNestedTest(config, p);
// ERROR function returning abstract class not allowed
} else {
auto myTest = static_cast<Test1::Test*>(test);
return myTest->InitalizeNestedTest(config);
// ERROR function returning abstract class not allowed
}
}
This static cast didn't return what I wanted previously because I was returning a pointer to a locally defined variable, which was pointed out in the comments. How am I able to return a class from this since it's an abstract class, do i need to cast it again or make multiple functions?
Test1::Test myTest;
auto myNestedTest = myTest.InitializeNestedTest(config);
I've thought of a few options but none of them seem right, or I'm not entirely sure how to implement them
Have an overloaded Virtual function for each type on the interface and then override them on the subclass (not sure if possible and doesn't seem like the right way to do it)
Extend the Config struct Test2 namespace so that it includes parameter p, so that they all have the same prototype and put it on the interface. (is it possible to "extend" the struct" from the interface?)
Maybe use a different type of cast, or do so in a different way?
I've included the definitions of my Interface and two subclasses for reference.
class ITest
{
//other things in ITest.hpp not relevant to question
public:
struct Config
{
int a;
bool enable;
};
class INestedTest
{
public:
virtual void Enable() const = 0;
virtual void Configure(Config const& config)
{
if(config.enable)
{
Enable();
}
}
};
};
namespace Test1
{
class Test : public ITest
{
public:
class NestedTest : public ITest::INestedTest
{
public:
NestedTest(Config const& config)
{
Configure(config);
}
void Enable() const override
{
//impl
}
}; // End NestedTest
NestedTest InitalizeNestedTest(Config const& config)
{
return NestedTest(config);
}
};
};
namespace Test2
{
class Test : public ITest
{
public:
class NestedTest : public ITest::INestedTest
{
public:
using Parameter = int;
NestedTest(ITest::Config const& config, Parameter p)
{
Configure(config);
}
void Enable() const override
{
//impl
}
}; // End NestedTest
NestedTest InitalizeNestedTest(Config const& config, NestedTest::Parameter p)
{
return NestedTest(config, p);
}
};
};
Maybe you could make the object static so it's declared in RAM at compile time (and not heap or stack).
I have a C++ class defined as:
class MyFuture {
public:
virtual bool isDone() = 0;
virtual const std::string& get() = 0;
virtual void onDone(MyCallBack& callBack) = 0;
virtual ~MyFuture() { /* empty */ }
};
typedef boost::shared_ptr<MyFuture> MyFuturePtr;
I expose the class to Python using boost.python as (this class is never created in Python but returned from an API call and thus the noncopyable):
BOOST_PYTHON_MODULE(MySDK)
{
class_<MyFuture, noncopyable>("MyFuture", no_init)
.def("isDone", &MyFuture::isDone)
.def("get", &MyFuture::get, return_value_policy<copy_const_reference>())
.def("onDone", &MyFuture::onDone)
;
}
From python I use it like:
import MySDK
def main():
# more code here ...
future = session.submit()
response = future.get
print response
if __name__ == "__main__":
main()
but this leads to the Python error:
File "main.py", line 14, in main
future = session.submit()
TypeError: No to_python (by-value) converter found for C++ type: class boost::shared_ptr<class MySDK::MyFuture>
How can I expose the typedef typedef boost::shared_ptr<MyFuture> MyFuturePtr;?
UPDATE
Changing the class expose using boost.Python to:
class_<MyFuture, boost::shared_ptr<MyFuture> >("MyFuture", no_init)
leads to the compiler error:
boost\python\converter\as_to_python_function.hpp(21):
error C2259: 'MySDK::MyFuture' : cannot instantiate abstract class
class_<MyFuture, boost::shared_ptr<MyFuture>, boost::noncopyable>("MyFuture")
as per the docs
http://www.boost.org/doc/libs/1_62_0/libs/python/doc/html/reference/high_level_components.html#high_level_components.boost_python_class_hpp.class_template_class_t_bases_hel
Are there any system of classe reference in D? To be more accurate I look for the equivalent of the Delphi
TMyClassRef = class of TMyClass;
This would be used for a factory (just like in the Object but without using the class name):
// ideally
void AddNew(*TBaseClass APtr, /*?class_ref_type?*/ AClassType)
{
*APtr = new AClassType;
}
Currently I do this:
void AddNew(*TBaseClass APtr)
{
*APtr = new typeof(*APtr);
}
But the problem is that typeof() returns always TBaseClass and never a sub class of TBaseClass (when a sub class is passed as parameter). This is clearly a case where class references would be used in Delphi but the D language doesn't seem to have such a system.
Maybe I'm completely missing the idea in Delphi, but this seems to be what a templates are for:
import std.stdio;
class Parent {
string inherited() {
return "Hello from parent";
}
override string toString() {
return "Hello from parent";
}
}
class Child : Parent {
override string toString() {
return "Hello from child";
}
}
void add(C, P)(P* ptr) {
*ptr = new C;
}
void main() {
Parent t;
writeln(t); // prints null
add!Child(&t);
writeln(t); // prints Hello from child
writeln(t.inherited()); // prints Hello from parent
}
This way you pass in the type you want to instantiate instead of an instantiated object of that type. This should generate compile errors if C is not a child of P in add().
Edit:
If you want to be more specific with add, you could do this:
void add(T : Parent)(Parent* ptr) {
*ptr = new T;
}
To make things nicer, use an out parameter to be more idiomatic:
void add(T : Parent)(out Parent ptr) {
ptr = new T;
}
void main() {
Parent p;
add!Child(p);
}
D has no class references in the Delphi way as far as I have understood Delphi concept. If you need to make a run-time decision about object construction, object.TypeInfo may help you.
You can retrieve TypeInfo for a variable via typeid construct:
import std.stdio;
class Base
{
void f()
{
writeln("Base");
}
}
class Descendant : Base
{
override void f()
{
writeln("Descendant");
}
}
Base makeNew(Base other)
{
// cast is needed because create() returns plain Object
// we can be sure it is Base at least, though, because it was crated from Base
return cast(Base)typeid(other).create();
}
void main()
{
Descendant source = new Descendant;
Base target = makeNew(source);
// prints "Descendant"
target.f();
}
Is this code sample similar to what you want?
D generally has a very clear differentiation between run-time actions and compile-time ones. typeof works on compile-time and thus can't query "real" class type in case of hierarchies.
I'm trying to expose my C++ Classes to Python using Boost.Python. Here is a simplyfied version of what i'm trying to do:
I have a class A deriving from boost::noncopyable and a second class B with a method that takes a reference to A as an argument.
class A : boost::noncopyable { /*...*/ };
class B {
public:
virtual void do_something(A& a) {
/*...*/
}
};
I'm exposing the classes as follows:
/* Wrapper for B, so B can be extended in python */
struct BWrap : public B, wrapper<B> {
void do_something(A &a) {
if (override do_something = this->get_override("do_something")) {
do_something(a);
return;
}
else {
B::do_something(a);
}
}
void default_do_something(A& a) { this->B::do_something(a); }
};
BOOST_PYTHON_MODULE(SomeModule) {
class_<A, boost::noncopyable>("A");
class_<BWrap, boost::noncopyable>("B")
.def("do_something", &B::do_something, &BWrap::default_do_something)
;
}
I extend B in python like this:
test.py:
import SomeModule
class BDerived(SomeModule.B):
def do_something(self, a):
pass
and call the extended B like this:
try {
py::object main = py::import("__main__"); \
py::object global(main.attr("__dict__")); \
py::object result = py::exec_file("test.py", global, global); \
py::object pluginClass = global["BDerived"]; \
py::object plugin_base = pluginClass(); \
B& plugin = py::extract<B&>(plugin_base) BOOST_EXTRACT_WORKAROUND;
A a;
B.do_something(a);
}
catch (py::error_already_set) {
PyErr_Print();
}
However this results in an error message:
TypeError: No to_python (by-value) converter found for C++ type: A
If A isn't derived from boost::noncopyable the code runs without any errors but the argument a in do_something(A& a) gets copied during the function call even though it's passed in by reference. But just removing the noncopyable requirement on A isn't an option since it's there for a reason.
Any suggestions how to solve the problem?
Thanks.
Change B.do_something(a); to B.do_something(boost::ref(a));.
See Calling Python Functions and Methods in the boost manual.
this is my first post :). I could convert a python extended object into a C++ pointer, but I have a problem. First I will show you my code and then I will explain the problem.
This is my class:
#include <boost/python.hpp>
using namespace boost::python;
class Base
{
public:
virtual const char* HelloWorld() = 0;
};
class BaseWrapper : public Base, public wrapper<BaseWrapper>
{
public:
virtual const char* HelloWorld()
{
if (override f = this->get_override("HelloWorld"))
return call<const char*>(f.ptr());
return "FAILED TO CALL";
}
};
Boost wrapping:
BOOST_PYTHON_MODULE(hello_ext)
{
class_<Base, boost::noncopyable>("Base", no_init);
class_<BaseWrapper, bases<Base> >("BaseWrapper")
.def("HelloWorld", &BaseWrapper::HelloWorld);
}
The Python code (hello.py):
def NewDerived():
import hello_ext
class Derived(hello_ext.BaseWrapper):
def __init__(self):
super(Derived, self).__init__()
def HelloWorld(self):
return "This is a Hello World!!!"
return Derived()
and the main file:
int main()
{
// Start the interpreter.
Py_Initialize();
// Import the module that we need (hello.py)
object module = import("hello");
// Get a C++ pointer of the derived python class.
Base* base = extract< Base* >( module.attr("NewDerived")() );
// Call the HelloWorld function
std::cout << base->HelloWorld() << std::endl;
}
When I run my application I can see at the screen "This is a Hello World!!!" as I expected.
So, what is the problem??? Suppose I change the python code to:
def NewDerived():
import hello_ext
class Derived(hello_ext.BaseWrapper):
def __init__(self):
super(Derived, self).__init__()
def HelloWorld(self):
return "This is a Hello" # I CHANGED THIS LINE!!!!
return Derived()
Then, when I run my application again, it crashes, because I got an error in the line:
std::cout << base->HelloWorld() << std::endl;
because base is NULL.
More precisely, the error is "Access violation reading location 0xblablabla".
When I debug, the debugguer stops at the function (Boost or Python code, I think)
inline api::object_base::~object_base()
{
Py_DECREF(m_ptr);
}
What do you think???
Finally, another programmer explained me the solution.
I don't know why it worked originally, but the problem is that the object is getting destroyed before I try calling the member function. I need to break the extract call into two parts like so:
object derived = module.attr("NewDerived")();
Base* base = extract< Base* >( derived );
This will keep the object around long enough for me to actually call functions on it.