The whole wrapper is inside the Python namespace. Before you start using anything inside it, you should call Python::initialize(), which initializes the Python API.
The Python::Object class provides an abstraction of a python object. Python::Objects wrap a PyObject*(which is the abstraction of a Python object provided by its API), inside a std::shared_ptr. On their destructor, a call to Py_DECREF is performed, so the underlying PyObject* will get free'd appropriately.
In order to load a python script, a static method Python::Object::from_script(const std::string &) should be called, which returns a Python::Object that represents that script. The name of the script(with or without the ".py" extension) should be passed as argument:
#include "pywrapper.h" /* ... */ Python::Object script = Python::Object::from_script("test.py"); //could be test also
After that sentence, the "script" variable will have loaded the "test.py" script, located in the current working directory.
Now you can call functions defined in that script using the Python::Object::call_function method, which has this signatures:
// Variadic template arguments version template<typename... Args> Python::Object call_function(const std::string &name, const Args... &args); // No arguments version Python::Object call_function(const std::string &name);
This method takes the name of the function as the first argument, followed by 0 or more arguments. These arguments will be implicitly converted to PyObject pointers, which can be used as arguments using the Python API. So far, you can use arguments of these types:
- std::string
- const char *
- Any integral type(for which std::is_integral is true).
- bool
- double
- std::vector
- std::list
- std::map
The Script::call_function method returns the Python return value wrapped in a Python::Object. Note that the objects stored in the std::vectors and std::lists must also be convertible to PyObject pointers(must be listed above).
In case you want to use the return value, you might want to use Python::Object::convert which will convert the wrapped PyObject* into one of the same C++ types mentioned above, and also std::tuple.
As an example, i used this python script:
#!/usr/bin/python def foo(a, b, c, d, e): print '{0} - {1} - {2} - {3} - {4}'.format( a, str(b), str(c), repr(d), repr(e) ) def int_fun(): return 12 def list_fun(): return [1,2,3,4,561,2] def dict_fun(): return { 'bar' : 1, 'foo' : 15 } def tuple_fun(): return (1, 'foo', 15.5) def bool_fun(): return False x = 1598
It's just a bunch of functions that take/return different types of arguments. My C++ code that calls these functions is this one:
#include <string> #include <vector> #include <iostream> #include <stdexcept> #include <iomanip> #include <map> #include <tuple> #include "pywrapper.h" int main() { Python::initialize(); Python::Object script(Python::Object::from_script("test_script.py")); std::vector<int> v({2,6,5}); std::map<std::string, int> dict({ {"bleh", 1}, {"foofoo", 10} }); std::cout << "Calling foo:\n"; script.call_function("foo", "a string", true, 10, v, dict); // Int test std::cout << "Calling int_fun:\n"; Python::Object ptr = script.call_function("int_fun"); int num; if(ptr.convert(num)) std::cout << "Result: " << num << '\n'; else std::cout << "Long conversion failed\n"; // List test std::vector<int> lst; std::cout << "Calling list_fun:\n"; ptr = script.call_function("list_fun"); if(ptr.convert(lst)) { std::cout << "List size: " << lst.size() << '\n'; for(auto it(lst.begin()); it != lst.end(); ++it) std::cout << *it << " "; std::cout << '\n'; } else std::cout << "List conversion failed\n"; // Dict test std::map<std::string, int> mp; std::cout << "Calling dict_fun:\n"; ptr = script.call_function("dict_fun"); if(ptr.convert(mp)) { std::cout << "Map size: " << mp.size() << '\n'; for(auto it(mp.begin()); it != mp.end(); ++it) std::cout << it->first << " -> " << it->second << '\n'; } else std::cout << "Map conversion failed\n"; // Tuple test std::cout << "Calling tuple_fun:\n"; ptr = script.call_function("tuple_fun"); std::tuple<int, std::string, double> tup; if(ptr.convert(tup)) { std::cout << std::get<0>(tup) << "\n"; std::cout << std::get<1>(tup) << "\n"; std::cout << std::get<2>(tup) << "\n"; } else std::cout << "Tuple conversion failed\n"; bool bool_val; std::cout << "Calling bool_fun:\n"; ptr = script.call_function("bool_fun"); if(ptr.convert(bool_val)) std::cout << "Result: " << std::boolalpha << bool_val << '\n'; else std::cout << "Long conversion failed\n"; // Get attr test std::cout << "Retrieving 'x' variable:\n"; try { ptr = script.get_attr("x"); if(ptr.convert(num)) std::cout << "X == " << num << '\n'; } catch(std::runtime_error &ex) { std::cout << ex.what() << '\n'; } }
In the last code senteces, the Python::Object::get_attr method is used, which returns a Python::Object containing the contents of the script attribute which has this method argument's name. After executing this application, this output is produced:
Calling foo: a string - True - 10 - [2, 6, 5] - {'bleh': 1, 'foofoo': 10} Calling int_fun: Result: 12 Calling list_fun: List size: 6 1 2 3 4 561 2 Calling dict_fun: Map size: 2 bar -> 1 foo -> 15 Calling tuple_fun: 1 foo 15.5 Calling bool_fun: Result: false Retrieving 'x' variable: X == 1598
As you can see, this class allows a type-safe variable argument interface for calling Python functions and retrieving defined attributes in scripts.
You can get the header and source file here.
In order to compile this application, using gcc, remember to use the -std=c++0x and -lpython2.7 arguments.
I hope you find this wrapper useful!
I'm not sure how old this post is but I just wanted to thank you for this, it's both an amazing display of C++11 and an extremely useful python wrapper
ReplyDeleteby the way here's a cmakelists.txt if anyone wants it, it's very simple though
ReplyDeletecmake_minimum_required(VERSION 2.8)
set(CMAKE_CXX_STANDARD 11)
find_package(PythonLibs 2.7 REQUIRED)
add_library(pywrapper pywrapper.cpp pywrapper.h)
target_include_directories(pywrapper PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${PYTHON_INCLUDE_DIRS})