embedding.cpp 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. // Copyright Stefan Seefeld 2005.
  2. // Distributed under the Boost Software License, Version 1.0. (See
  3. // accompanying file LICENSE_1_0.txt or copy at
  4. // http://www.boost.org/LICENSE_1_0.txt)
  5. #include <boost/python.hpp>
  6. #include <boost/detail/lightweight_test.hpp>
  7. #include <iostream>
  8. namespace python = boost::python;
  9. // An abstract base class
  10. class Base : public boost::noncopyable
  11. {
  12. public:
  13. virtual ~Base() {};
  14. virtual std::string hello() = 0;
  15. };
  16. // C++ derived class
  17. class CppDerived : public Base
  18. {
  19. public:
  20. virtual ~CppDerived() {}
  21. virtual std::string hello() { return "Hello from C++!";}
  22. };
  23. // Familiar Boost.Python wrapper class for Base
  24. struct BaseWrap : Base, python::wrapper<Base>
  25. {
  26. virtual std::string hello()
  27. {
  28. #if BOOST_WORKAROUND(BOOST_MSVC, <= 1300)
  29. // workaround for VC++ 6.x or 7.0, see
  30. // http://boost.org/libs/python/doc/tutorial/doc/html/python/exposing.html#python.class_virtual_functions
  31. return python::call<std::string>(this->get_override("hello").ptr());
  32. #else
  33. return this->get_override("hello")();
  34. #endif
  35. }
  36. };
  37. // Pack the Base class wrapper into a module
  38. BOOST_PYTHON_MODULE(embedded_hello)
  39. {
  40. python::class_<BaseWrap, boost::noncopyable> base("Base");
  41. }
  42. void exec_test()
  43. {
  44. std::cout << "registering extension module embedded_hello..." << std::endl;
  45. // Register the module with the interpreter
  46. if (PyImport_AppendInittab("embedded_hello", initembedded_hello) == -1)
  47. throw std::runtime_error("Failed to add embedded_hello to the interpreter's "
  48. "builtin modules");
  49. std::cout << "defining Python class derived from Base..." << std::endl;
  50. // Retrieve the main module
  51. python::object main = python::import("__main__");
  52. // Retrieve the main module's namespace
  53. python::object global(main.attr("__dict__"));
  54. // Define the derived class in Python.
  55. python::object result = python::exec(
  56. "from embedded_hello import * \n"
  57. "class PythonDerived(Base): \n"
  58. " def hello(self): \n"
  59. " return 'Hello from Python!' \n",
  60. global, global);
  61. python::object PythonDerived = global["PythonDerived"];
  62. // Creating and using instances of the C++ class is as easy as always.
  63. CppDerived cpp;
  64. BOOST_TEST(cpp.hello() == "Hello from C++!");
  65. std::cout << "testing derived class from C++..." << std::endl;
  66. // But now creating and using instances of the Python class is almost
  67. // as easy!
  68. python::object py_base = PythonDerived();
  69. Base& py = python::extract<Base&>(py_base) BOOST_EXTRACT_WORKAROUND;
  70. // Make sure the right 'hello' method is called.
  71. BOOST_TEST(py.hello() == "Hello from Python!");
  72. std::cout << "success!" << std::endl;
  73. }
  74. void exec_file_test(std::string const &script)
  75. {
  76. std::cout << "running file " << script << "..." << std::endl;
  77. // Run a python script in an empty environment.
  78. python::dict global;
  79. python::object result = python::exec_file(script.c_str(), global, global);
  80. // Extract an object the script stored in the global dictionary.
  81. BOOST_TEST(python::extract<int>(global["number"]) == 42);
  82. std::cout << "success!" << std::endl;
  83. }
  84. void exec_test_error()
  85. {
  86. std::cout << "intentionally causing a python exception..." << std::endl;
  87. // Execute a statement that raises a python exception.
  88. python::dict global;
  89. python::object result = python::exec("print unknown \n", global, global);
  90. std::cout << "Oops! This statement should be skipped due to an exception" << std::endl;
  91. }
  92. int main(int argc, char **argv)
  93. {
  94. BOOST_TEST(argc == 2);
  95. std::string script = argv[1];
  96. // Initialize the interpreter
  97. Py_Initialize();
  98. bool error_expected = false;
  99. if (
  100. python::handle_exception(exec_test)
  101. || python::handle_exception(boost::bind(exec_file_test, script))
  102. || (
  103. (error_expected = true)
  104. && python::handle_exception(exec_test_error)
  105. )
  106. )
  107. {
  108. if (PyErr_Occurred())
  109. {
  110. if (!error_expected)
  111. BOOST_ERROR("Python Error detected");
  112. PyErr_Print();
  113. }
  114. else
  115. {
  116. BOOST_ERROR("A C++ exception was thrown for which "
  117. "there was no exception translator registered.");
  118. }
  119. }
  120. // Boost.Python doesn't support Py_Finalize yet, so don't call it!
  121. return boost::report_errors();
  122. }