diff --git a/examples/basic.cpp b/examples/basic.cpp index de6ac420..066c0733 100644 --- a/examples/basic.cpp +++ b/examples/basic.cpp @@ -6,28 +6,29 @@ namespace plt = matplotlibcpp; int main() { - // Prepare data. - int n = 5000; - std::vector x(n), y(n), z(n), w(n,2); - for(int i=0; i x(n), y(n), z(n), w(n,2); + for(int i=0; i - bool plot(const std::vector &x, const std::vector &y, const std::map& keywords) - { - assert(x.size() == y.size()); - - // using python lists - PyObject* xlist = PyList_New(x.size()); - PyObject* ylist = PyList_New(y.size()); - - for(size_t i = 0; i < x.size(); ++i) { - PyList_SetItem(xlist, i, PyFloat_FromDouble(x.at(i))); - PyList_SetItem(ylist, i, PyFloat_FromDouble(y.at(i))); - } - - // construct positional args - PyObject* args = PyTuple_New(2); - PyTuple_SetItem(args, 0, xlist); - PyTuple_SetItem(args, 1, ylist); - - Py_DECREF(xlist); - Py_DECREF(ylist); - - // construct keyword args - PyObject* kwargs = PyDict_New(); - for(std::map::const_iterator it = keywords.begin(); it != keywords.end(); ++it) - { - PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str())); - } - - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_plot, args, kwargs); - - Py_DECREF(args); - Py_DECREF(kwargs); - if(res) Py_DECREF(res); - - return res; - } - - - template - bool plot(const std::vector& x, const std::vector& y, const std::string& s = "") - { - assert(x.size() == y.size()); - - PyObject* xlist = PyList_New(x.size()); - PyObject* ylist = PyList_New(y.size()); - PyObject* pystring = PyString_FromString(s.c_str()); - - for(size_t i = 0; i < x.size(); ++i) { - PyList_SetItem(xlist, i, PyFloat_FromDouble(x.at(i))); - PyList_SetItem(ylist, i, PyFloat_FromDouble(y.at(i))); - } - - PyObject* plot_args = PyTuple_New(3); - PyTuple_SetItem(plot_args, 0, xlist); - PyTuple_SetItem(plot_args, 1, ylist); - PyTuple_SetItem(plot_args, 2, pystring); - - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_plot, plot_args); - - Py_DECREF(xlist); - Py_DECREF(ylist); - Py_DECREF(plot_args); - if(res) Py_DECREF(res); - - return res; - } - - - template - bool named_plot(const std::string& name, const std::vector& x, const std::vector& y, const std::string& format = "") { - PyObject* kwargs = PyDict_New(); - PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); - - PyObject* xlist = PyList_New(x.size()); - PyObject* ylist = PyList_New(y.size()); - PyObject* pystring = PyString_FromString(format.c_str()); - - for(size_t i = 0; i < x.size(); ++i) { - PyList_SetItem(xlist, i, PyFloat_FromDouble(x.at(i))); - PyList_SetItem(ylist, i, PyFloat_FromDouble(y.at(i))); - } - - PyObject* plot_args = PyTuple_New(3); - PyTuple_SetItem(plot_args, 0, xlist); - PyTuple_SetItem(plot_args, 1, ylist); - PyTuple_SetItem(plot_args, 2, pystring); + namespace detail { + struct _interpreter { + PyObject *s_python_function_show; + PyObject *s_python_function_save; + PyObject *s_python_function_figure; + PyObject *s_python_function_plot; + PyObject *s_python_function_legend; + PyObject *s_python_function_xlim; + PyObject *s_python_function_ylim; + PyObject *s_python_function_title; + PyObject *s_python_function_axis; + PyObject *s_python_function_xlabel; + PyObject *s_python_function_ylabel; + PyObject *s_python_function_grid; + PyObject *s_python_empty_tuple; + + /* For now, _interpreter is implemented as a singleton since its currently not possible to have + multiple independent embedded python interpreters without patching the python source code + or starting a seperate process for each. + +http://bytes.com/topic/python/answers/793370-multiple-independent-python-interpreters-c-c-program +*/ + + static _interpreter& get() { + static _interpreter ctx; + return ctx; + } + + private: + _interpreter() { + char name[] = "plotting"; // silence compiler warning abount const strings + Py_SetProgramName(name); // optional but recommended + Py_Initialize(); + + PyObject* pyplotname = PyString_FromString("matplotlib.pyplot"); + PyObject* pylabname = PyString_FromString("pylab"); + if(!pyplotname || !pylabname) { throw std::runtime_error("couldnt create string"); } + + PyObject* pymod = PyImport_Import(pyplotname); + Py_DECREF(pyplotname); + if(!pymod) { throw std::runtime_error("Error loading module matplotlib.pyplot!"); } + + PyObject* pylabmod = PyImport_Import(pylabname); + Py_DECREF(pylabname); + if(!pymod) { throw std::runtime_error("Error loading module pylab!"); } + + s_python_function_show = PyObject_GetAttrString(pymod, "show"); + s_python_function_figure = PyObject_GetAttrString(pymod, "figure"); + s_python_function_plot = PyObject_GetAttrString(pymod, "plot"); + s_python_function_legend = PyObject_GetAttrString(pymod, "legend"); + s_python_function_ylim = PyObject_GetAttrString(pymod, "ylim"); + s_python_function_title = PyObject_GetAttrString(pymod, "title"); + s_python_function_axis = PyObject_GetAttrString(pymod, "axis"); + s_python_function_xlabel = PyObject_GetAttrString(pymod, "xlabel"); + s_python_function_ylabel = PyObject_GetAttrString(pymod, "ylabel"); + s_python_function_grid = PyObject_GetAttrString(pymod, "grid"); + s_python_function_xlim = PyObject_GetAttrString(pymod, "xlim"); + + s_python_function_save = PyObject_GetAttrString(pylabmod, "savefig"); + + if(!s_python_function_show + || !s_python_function_save + || !s_python_function_figure + || !s_python_function_plot + || !s_python_function_legend + || !s_python_function_xlim + || !s_python_function_ylim + || !s_python_function_title + || !s_python_function_axis + || !s_python_function_xlabel + || !s_python_function_ylabel + || !s_python_function_grid + ) + { throw std::runtime_error("Couldnt find required function!"); } + + if(!PyFunction_Check(s_python_function_show) + || !PyFunction_Check(s_python_function_save) + || !PyFunction_Check(s_python_function_figure) + || !PyFunction_Check(s_python_function_plot) + || !PyFunction_Check(s_python_function_legend) + || !PyFunction_Check(s_python_function_xlim) + || !PyFunction_Check(s_python_function_ylim) + || !PyFunction_Check(s_python_function_title) + || !PyFunction_Check(s_python_function_axis) + || !PyFunction_Check(s_python_function_xlabel) + || !PyFunction_Check(s_python_function_ylabel) + || !PyFunction_Check(s_python_function_grid) + ) + { throw std::runtime_error("Python object is unexpectedly not a PyFunction."); } + + s_python_empty_tuple = PyTuple_New(0); + } + + ~_interpreter() { + Py_Finalize(); + } + }; + } + + + + template + bool plot(const std::vector &x, const std::vector &y, const std::map& keywords) + { + assert(x.size() == y.size()); + + // using python lists + PyObject* xlist = PyList_New(x.size()); + PyObject* ylist = PyList_New(y.size()); + + for(size_t i = 0; i < x.size(); ++i) { + PyList_SetItem(xlist, i, PyFloat_FromDouble(x.at(i))); + PyList_SetItem(ylist, i, PyFloat_FromDouble(y.at(i))); + } + + // construct positional args + PyObject* args = PyTuple_New(2); + PyTuple_SetItem(args, 0, xlist); + PyTuple_SetItem(args, 1, ylist); + + Py_DECREF(xlist); + Py_DECREF(ylist); + + // construct keyword args + PyObject* kwargs = PyDict_New(); + for(std::map::const_iterator it = keywords.begin(); it != keywords.end(); ++it) + { + PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str())); + } + + PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_plot, args, kwargs); + + Py_DECREF(args); + Py_DECREF(kwargs); + if(res) Py_DECREF(res); + + return res; + } + + + template + bool plot(const std::vector& x, const std::vector& y, const std::string& s = "") + { + assert(x.size() == y.size()); + + PyObject* xlist = PyList_New(x.size()); + PyObject* ylist = PyList_New(y.size()); + PyObject* pystring = PyString_FromString(s.c_str()); + + for(size_t i = 0; i < x.size(); ++i) { + PyList_SetItem(xlist, i, PyFloat_FromDouble(x.at(i))); + PyList_SetItem(ylist, i, PyFloat_FromDouble(y.at(i))); + } + + PyObject* plot_args = PyTuple_New(3); + PyTuple_SetItem(plot_args, 0, xlist); + PyTuple_SetItem(plot_args, 1, ylist); + PyTuple_SetItem(plot_args, 2, pystring); + + PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_plot, plot_args); + + Py_DECREF(xlist); + Py_DECREF(ylist); + Py_DECREF(plot_args); + if(res) Py_DECREF(res); + + return res; + } + + + template + bool named_plot(const std::string& name, const std::vector& x, const std::vector& y, const std::string& format = "") { + PyObject* kwargs = PyDict_New(); + PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); + + PyObject* xlist = PyList_New(x.size()); + PyObject* ylist = PyList_New(y.size()); + PyObject* pystring = PyString_FromString(format.c_str()); - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_plot, plot_args, kwargs); + for(size_t i = 0; i < x.size(); ++i) { + PyList_SetItem(xlist, i, PyFloat_FromDouble(x.at(i))); + PyList_SetItem(ylist, i, PyFloat_FromDouble(y.at(i))); + } - Py_DECREF(kwargs); - Py_DECREF(xlist); - Py_DECREF(ylist); - Py_DECREF(plot_args); - if(res) Py_DECREF(res); + PyObject* plot_args = PyTuple_New(3); + PyTuple_SetItem(plot_args, 0, xlist); + PyTuple_SetItem(plot_args, 1, ylist); + PyTuple_SetItem(plot_args, 2, pystring); - return res; - } + PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_plot, plot_args, kwargs); - template - bool plot(const std::vector& y, const std::string& format = "") - { - std::vector x(y.size()); - for(size_t i=0; i + bool plot(const std::vector& y, const std::string& format = "") + { + std::vector x(y.size()); + for(size_t i=0; i - void ylim(Numeric left, Numeric right) - { - PyObject* list = PyList_New(2); - PyList_SetItem(list, 0, PyFloat_FromDouble(left)); - PyList_SetItem(list, 1, PyFloat_FromDouble(right)); + inline void legend() { + PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_legend, detail::_interpreter::get().s_python_empty_tuple); + if(!res) throw std::runtime_error("Call to legend() failed."); - PyObject* args = PyTuple_New(1); - PyTuple_SetItem(args, 0, list); + Py_DECREF(res); + } - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_ylim, args); - if(!res) throw std::runtime_error("Call to ylim() failed."); + template + void ylim(Numeric left, Numeric right) + { + PyObject* list = PyList_New(2); + PyList_SetItem(list, 0, PyFloat_FromDouble(left)); + PyList_SetItem(list, 1, PyFloat_FromDouble(right)); - Py_DECREF(list); - Py_DECREF(args); - Py_DECREF(res); - } + PyObject* args = PyTuple_New(1); + PyTuple_SetItem(args, 0, list); - template - void xlim(Numeric left, Numeric right) - { - PyObject* list = PyList_New(2); - PyList_SetItem(list, 0, PyFloat_FromDouble(left)); - PyList_SetItem(list, 1, PyFloat_FromDouble(right)); - - PyObject* args = PyTuple_New(1); - PyTuple_SetItem(args, 0, list); + PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_ylim, args); + if(!res) throw std::runtime_error("Call to ylim() failed."); - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_xlim, args); - if(!res) throw std::runtime_error("Call to xlim() failed."); + Py_DECREF(list); + Py_DECREF(args); + Py_DECREF(res); + } - Py_DECREF(list); - Py_DECREF(args); - Py_DECREF(res); - } + template + void xlim(Numeric left, Numeric right) + { + PyObject* list = PyList_New(2); + PyList_SetItem(list, 0, PyFloat_FromDouble(left)); + PyList_SetItem(list, 1, PyFloat_FromDouble(right)); - - inline void title(const std::string &titlestr) - { - PyObject* pytitlestr = PyString_FromString(titlestr.c_str()); - PyObject* args = PyTuple_New(1); - PyTuple_SetItem(args, 0, pytitlestr); + PyObject* args = PyTuple_New(1); + PyTuple_SetItem(args, 0, list); - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_title, args); - if(!res) throw std::runtime_error("Call to title() failed."); + PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_xlim, args); + if(!res) throw std::runtime_error("Call to xlim() failed."); - //if PyDeCRFF, the show function doesn't wook on Mac OS - } + Py_DECREF(list); + Py_DECREF(args); + Py_DECREF(res); + } - inline void show() - { - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_show, detail::_interpreter::get().s_python_empty_tuple); - if(!res) throw std::runtime_error("Call to show() failed."); - Py_DECREF(res); - } - - inline void save(const std::string& filename) - { - PyObject* pyfilename = PyString_FromString(filename.c_str()); - - PyObject* args = PyTuple_New(1); - PyTuple_SetItem(args, 0, pyfilename); - - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_save, args); - if(!res) throw std::runtime_error("Call to save() failed."); + inline void title(const std::string &titlestr) + { + PyObject* pytitlestr = PyString_FromString(titlestr.c_str()); + PyObject* args = PyTuple_New(1); + PyTuple_SetItem(args, 0, pytitlestr); - Py_DECREF(pyfilename); - Py_DECREF(args); - Py_DECREF(res); - } + PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_title, args); + if(!res) throw std::runtime_error("Call to title() failed."); + + //if PyDeCRFF, the show function doesn't wook on Mac OS + } + + inline void axis(const std::string &axisstr) + { + PyObject* str = PyString_FromString(axisstr.c_str()); + PyObject* args = PyTuple_New(1); + PyTuple_SetItem(args, 0, str); + + PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_axis, args); + if(!res) throw std::runtime_error("Call to title() failed."); + + //if PyDeCRFF, the show function doesn't wook on Mac OS + } + + + + inline void xlabel(const std::string &str) + { + PyObject* pystr = PyString_FromString(str.c_str()); + PyObject* args = PyTuple_New(1); + PyTuple_SetItem(args, 0, pystr); + + PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_xlabel, args); + if(!res) throw std::runtime_error("Call to xlabel() failed."); + + //if PyDeCRFF, the show function doesn't wook on Mac OS + } + + inline void ylabel(const std::string &str) + { + PyObject* pystr = PyString_FromString(str.c_str()); + PyObject* args = PyTuple_New(1); + PyTuple_SetItem(args, 0, pystr); + + PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_ylabel, args); + if(!res) throw std::runtime_error("Call to ylabel() failed."); + + //if PyDeCRFF, the show function doesn't wook on Mac OS + } + + inline void grid(const bool &flag) + { + PyObject* pyflag; + if(flag){ + pyflag=Py_True; + } + else{ + pyflag=Py_False; + } + PyObject* args = PyTuple_New(1); + PyTuple_SetItem(args, 0, pyflag); + + PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_grid, args); + if(!res) throw std::runtime_error("Call to grid() failed."); + + //if PyDeCRFF, the show function doesn't wook on Mac OS + } + + + + inline void show() + { + PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_show, detail::_interpreter::get().s_python_empty_tuple); + if(!res) throw std::runtime_error("Call to show() failed."); + + Py_DECREF(res); + } + + inline void save(const std::string& filename) + { + PyObject* pyfilename = PyString_FromString(filename.c_str()); + + PyObject* args = PyTuple_New(1); + PyTuple_SetItem(args, 0, pyfilename); + + PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_save, args); + if(!res) throw std::runtime_error("Call to save() failed."); + + Py_DECREF(pyfilename); + Py_DECREF(args); + Py_DECREF(res); + } #if __cplusplus > 199711L - // C++11-exclusive content starts here (variadic plot() and initializer list support) - - namespace detail { - template - using is_function = typename std::is_function>>::type; - - template - struct is_callable_impl; - - template - struct is_callable_impl - { - typedef is_function type; - }; // a non-object is callable iff it is a function - - template - struct is_callable_impl - { - struct Fallback { void operator()(); }; - struct Derived : T, Fallback { }; - - template struct Check; - - template - static std::true_type test( ... ); // use a variadic function to make sure (1) it accepts everything and (2) its always the worst match - - template - static std::false_type test( Check* ); - - public: - typedef decltype(test(nullptr)) type; - typedef decltype(&Fallback::operator()) dtype; - static constexpr bool value = type::value; - }; // an object is callable iff it defines operator() - - template - struct is_callable - { - // dispatch to is_callable_impl or is_callable_impl depending on whether T is of class type or not - typedef typename is_callable_impl::value, T>::type type; - }; - - template - struct plot_impl { }; - - template<> - struct plot_impl - { - template - bool operator()(const IterableX& x, const IterableY& y, const std::string& format) - { - // 2-phase lookup for distance, begin, end - using std::distance; - using std::begin; - using std::end; - - auto xs = distance(begin(x), end(x)); - auto ys = distance(begin(y), end(y)); - assert(xs == ys && "x and y data must have the same number of elements!"); - - PyObject* xlist = PyList_New(xs); - PyObject* ylist = PyList_New(ys); - PyObject* pystring = PyString_FromString(format.c_str()); - - auto itx = begin(x), ity = begin(y); - for(size_t i = 0; i < xs; ++i) { - PyList_SetItem(xlist, i, PyFloat_FromDouble(*itx++)); - PyList_SetItem(ylist, i, PyFloat_FromDouble(*ity++)); - } - - PyObject* plot_args = PyTuple_New(3); - PyTuple_SetItem(plot_args, 0, xlist); - PyTuple_SetItem(plot_args, 1, ylist); - PyTuple_SetItem(plot_args, 2, pystring); - - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_plot, plot_args); - - Py_DECREF(xlist); - Py_DECREF(ylist); - Py_DECREF(plot_args); - if(res) Py_DECREF(res); - - return res; - } - }; - - template<> - struct plot_impl - { - template - bool operator()(const Iterable& ticks, const Callable& f, const std::string& format) - { - //std::cout << "Callable impl called" << std::endl; - - if(begin(ticks) == end(ticks)) return true; - - // We could use additional meta-programming to deduce the correct element type of y, - // but all values have to be convertible to double anyways - std::vector y; - for(auto x : ticks) y.push_back(f(x)); - return plot_impl()(ticks,y,format); - } - }; - } - - // recursion stop for the above - template - bool plot() { return true; } - - template - bool plot(const A& a, const B& b, const std::string& format, Args... args) - { - return detail::plot_impl::type>()(a,b,format) && plot(args...); - } - - /* - * This group of plot() functions is needed to support initializer lists, i.e. calling - * plot( {1,2,3,4} ) - */ - bool plot(const std::vector& x, const std::vector& y, const std::string& format = "") { - return plot(x,y,format); - } - - bool plot(const std::vector& y, const std::string& format = "") { - return plot(y,format); - } - - bool plot(const std::vector& x, const std::vector& y, const std::map& keywords) { - return plot(x,y,keywords); - } - - bool named_plot(const std::string& name, const std::vector& x, const std::vector& y, const std::string& format = "") { - return named_plot(name,x,y,format); - } + // C++11-exclusive content starts here (variadic plot() and initializer list support) + + namespace detail { + template + using is_function = typename std::is_function>>::type; + + template + struct is_callable_impl; + + template + struct is_callable_impl + { + typedef is_function type; + }; // a non-object is callable iff it is a function + + template + struct is_callable_impl + { + struct Fallback { void operator()(); }; + struct Derived : T, Fallback { }; + + template struct Check; + + template + static std::true_type test( ... ); // use a variadic function to make sure (1) it accepts everything and (2) its always the worst match + + template + static std::false_type test( Check* ); + + public: + typedef decltype(test(nullptr)) type; + typedef decltype(&Fallback::operator()) dtype; + static constexpr bool value = type::value; + }; // an object is callable iff it defines operator() + + template + struct is_callable + { + // dispatch to is_callable_impl or is_callable_impl depending on whether T is of class type or not + typedef typename is_callable_impl::value, T>::type type; + }; + + template + struct plot_impl { }; + + template<> + struct plot_impl + { + template + bool operator()(const IterableX& x, const IterableY& y, const std::string& format) + { + // 2-phase lookup for distance, begin, end + using std::distance; + using std::begin; + using std::end; + + auto xs = distance(begin(x), end(x)); + auto ys = distance(begin(y), end(y)); + assert(xs == ys && "x and y data must have the same number of elements!"); + + PyObject* xlist = PyList_New(xs); + PyObject* ylist = PyList_New(ys); + PyObject* pystring = PyString_FromString(format.c_str()); + + auto itx = begin(x), ity = begin(y); + for(size_t i = 0; i < xs; ++i) { + PyList_SetItem(xlist, i, PyFloat_FromDouble(*itx++)); + PyList_SetItem(ylist, i, PyFloat_FromDouble(*ity++)); + } + + PyObject* plot_args = PyTuple_New(3); + PyTuple_SetItem(plot_args, 0, xlist); + PyTuple_SetItem(plot_args, 1, ylist); + PyTuple_SetItem(plot_args, 2, pystring); + + PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_plot, plot_args); + + Py_DECREF(xlist); + Py_DECREF(ylist); + Py_DECREF(plot_args); + if(res) Py_DECREF(res); + + return res; + } + }; + + template<> + struct plot_impl + { + template + bool operator()(const Iterable& ticks, const Callable& f, const std::string& format) + { + //std::cout << "Callable impl called" << std::endl; + + if(begin(ticks) == end(ticks)) return true; + + // We could use additional meta-programming to deduce the correct element type of y, + // but all values have to be convertible to double anyways + std::vector y; + for(auto x : ticks) y.push_back(f(x)); + return plot_impl()(ticks,y,format); + } + }; + } + + // recursion stop for the above + template + bool plot() { return true; } + + template + bool plot(const A& a, const B& b, const std::string& format, Args... args) + { + return detail::plot_impl::type>()(a,b,format) && plot(args...); + } + + /* + * This group of plot() functions is needed to support initializer lists, i.e. calling + * plot( {1,2,3,4} ) + */ + bool plot(const std::vector& x, const std::vector& y, const std::string& format = "") { + return plot(x,y,format); + } + + bool plot(const std::vector& y, const std::string& format = "") { + return plot(y,format); + } + + bool plot(const std::vector& x, const std::vector& y, const std::map& keywords) { + return plot(x,y,keywords); + } + + bool named_plot(const std::string& name, const std::vector& x, const std::vector& y, const std::string& format = "") { + return named_plot(name,x,y,format); + } #endif