44#include < map>
55#include < numeric>
66#include < stdexcept>
7+ #include < iostream>
8+
9+ #if __cplusplus > 199711L
10+ #include < functional>
11+ #endif
712
813#include < python2.7/Python.h>
914
@@ -26,7 +31,8 @@ namespace matplotlibcpp {
2631
2732 private:
2833 _pyplot_global () {
29- Py_SetProgramName (" plotting" ); /* optional but recommended */
34+ char name[] = " plotting" ; // silence compiler warning abount const strings
35+ Py_SetProgramName (name); // optional but recommended
3036 Py_Initialize ();
3137
3238 PyObject* pyname = PyString_FromString (" matplotlib.pyplot" );
@@ -109,13 +115,16 @@ namespace matplotlibcpp {
109115 }
110116
111117
112- template <typename Numeric>
113- bool plot (const std::vector<Numeric>& x, const std::vector<Numeric>& y, const std::string& format = " " ) {
118+ template <typename NumericX, typename NumericY>
119+ bool plot (const std::vector<NumericX>& x, const std::vector<NumericY>& y, const std::string& s = " " )
120+ {
114121 assert (x.size () == y.size ());
115122
123+ // std::string format(s);
124+
116125 PyObject* xlist = PyList_New (x.size ());
117126 PyObject* ylist = PyList_New (y.size ());
118- PyObject* pystring = PyString_FromString (format .c_str ());
127+ PyObject* pystring = PyString_FromString (s .c_str ());
119128
120129 for (size_t i = 0 ; i < x.size (); ++i) {
121130 PyList_SetItem (xlist, i, PyFloat_FromDouble (x.at (i)));
@@ -181,7 +190,7 @@ namespace matplotlibcpp {
181190 * plot( {1,2,3,4} )
182191 */
183192 bool plot (const std::vector<double >& x, const std::vector<double >& y, const std::string& format = " " ) {
184- return plot<double >(x,y,format);
193+ return plot<double , double >(x,y,format);
185194 }
186195
187196 bool plot (const std::vector<double >& y, const std::string& format = " " ) {
@@ -196,6 +205,117 @@ namespace matplotlibcpp {
196205 return named_plot<double >(name,x,y,format);
197206 }
198207
208+ #if __cplusplus > 199711L
209+
210+ template <typename T>
211+ using is_function = typename std::is_function<std::remove_pointer<std::remove_reference<T>>>::type;
212+
213+ template <bool obj, typename T>
214+ struct is_callable_impl ;
215+
216+ template <typename T>
217+ struct is_callable_impl <false , T>
218+ {
219+ typedef is_function<T> type;
220+ }; // a non-object is callable iff it is a function
221+
222+ template <typename T>
223+ struct is_callable_impl <true , T>
224+ {
225+ struct Fallback { void operator ()(); };
226+ struct Derived : T, Fallback { };
227+
228+ template <typename U, U> struct Check ;
229+
230+ template <typename U>
231+ static std::true_type test ( ... ); // use a variadic function to make use (1) it accepts everything and (2) its always the worst match
232+
233+ template <typename U>
234+ static std::false_type test ( Check<void (Fallback::*)(), &U::operator()>* );
235+
236+ public:
237+ typedef decltype (test<Derived>(nullptr )) type;
238+ typedef decltype (&Fallback::operator ()) dtype;
239+ static constexpr bool value = type::value;
240+ }; // an object is callable iff it defines operator()
241+
242+ template <typename T>
243+ struct is_callable
244+ {
245+ // dispatch to is_callable_impl<true, T> or is_callable_impl<false, T> depending on whether T is of class type or not
246+ typedef typename is_callable_impl<std::is_class<T>::value, T>::type type; // todo: restore remove_reference
247+ };
248+
249+ template <typename IsYDataCallable>
250+ struct plot_impl { };
251+
252+ template <>
253+ struct plot_impl <std::false_type>
254+ {
255+ template <typename IterableX, typename IterableY>
256+ bool operator ()(const IterableX& x, const IterableY& y, const std::string& format)
257+ {
258+ // It's annoying that we have to repeat the code of plot() above
259+ auto xs = std::distance (std::begin (x), std::end (x));
260+ auto ys = std::distance (std::begin (y), std::end (y));
261+ assert (xs == ys && " x and y data must have the same number of elements!" );
262+
263+ PyObject* xlist = PyList_New (xs);
264+ PyObject* ylist = PyList_New (ys);
265+ PyObject* pystring = PyString_FromString (format.c_str ());
266+
267+ auto itx = std::begin (x), ity = std::begin (y);
268+ for (size_t i = 0 ; i < xs; ++i) {
269+ PyList_SetItem (xlist, i, PyFloat_FromDouble (*itx++));
270+ PyList_SetItem (ylist, i, PyFloat_FromDouble (*ity++));
271+ }
272+
273+ PyObject* plot_args = PyTuple_New (3 );
274+ PyTuple_SetItem (plot_args, 0 , xlist);
275+ PyTuple_SetItem (plot_args, 1 , ylist);
276+ PyTuple_SetItem (plot_args, 2 , pystring);
277+
278+ PyObject* res = PyObject_CallObject (detail::_pyplot_global::get ().s_python_function_plot , plot_args);
279+
280+ Py_DECREF (xlist);
281+ Py_DECREF (ylist);
282+ Py_DECREF (plot_args);
283+ if (res) Py_DECREF (res);
284+
285+ return res;
286+ }
287+ };
288+
289+ template <>
290+ struct plot_impl <std::true_type>
291+ {
292+ template <typename Iterable, typename Callable>
293+ bool operator ()(const Iterable& ticks, const Callable& f, const std::string& format)
294+ {
295+ // std::cout << "Callable impl called" << std::endl;
296+
297+ if (begin (ticks) == end (ticks)) return true ;
298+
299+ // We could use additional meta-programming to deduce the correct element type of y,
300+ // but all values have to be convertible to double anyways
301+ std::vector<double > y;
302+ for (auto x : ticks) y.push_back (f (x));
303+ return plot_impl<std::false_type>()(ticks,y,format);
304+ }
305+ };
306+
307+ // recursion stop for the above
308+ template <typename ... Args>
309+ bool plot () { return true ; }
310+
311+ template <typename A, typename B, typename ... Args>
312+ bool plot (const A& a, const B& b, const std::string& format, Args... args)
313+ {
314+ return plot_impl<typename is_callable<B>::type>()(a,b,format) && plot (args...);
315+ }
316+
317+ #endif
318+
199319 inline void legend () {
200320 PyObject* res = PyObject_CallObject (detail::_pyplot_global::get ().s_python_function_legend , detail::_pyplot_global::get ().s_python_empty_tuple );
201321 if (!res) throw std::runtime_error (" Call to legend() failed." );
0 commit comments