Skip to content

Commit ccbcfc1

Browse files
author
Benno Evers
committed
Cleanup and documentation changes
1 parent 029a613 commit ccbcfc1

File tree

1 file changed

+136
-124
lines changed

1 file changed

+136
-124
lines changed

matplotlibcpp.h

+136-124
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
namespace matplotlibcpp {
1616

1717
namespace detail {
18-
struct _pyplot_global {
18+
struct _interpreter {
1919
PyObject *s_python_function_show;
2020
PyObject *s_python_function_figure;
2121
PyObject *s_python_function_plot;
@@ -24,13 +24,20 @@ namespace matplotlibcpp {
2424
PyObject *s_python_function_ylim;
2525
PyObject *s_python_empty_tuple;
2626

27-
static _pyplot_global& get() {
28-
static _pyplot_global ctx;
27+
/* For now, _interpreter is implemented as a singleton since its currently not possible to have
28+
multiple independent embedded python interpreters without patching the python source code
29+
or starting a seperate process for each.
30+
31+
http://bytes.com/topic/python/answers/793370-multiple-independent-python-interpreters-c-c-program
32+
*/
33+
34+
static _interpreter& get() {
35+
static _interpreter ctx;
2936
return ctx;
3037
}
3138

3239
private:
33-
_pyplot_global() {
40+
_interpreter() {
3441
char name[] = "plotting"; // silence compiler warning abount const strings
3542
Py_SetProgramName(name); // optional but recommended
3643
Py_Initialize();
@@ -68,7 +75,7 @@ namespace matplotlibcpp {
6875
s_python_empty_tuple = PyTuple_New(0);
6976
}
7077

71-
~_pyplot_global() {
78+
~_interpreter() {
7279
Py_Finalize();
7380
}
7481
};
@@ -105,7 +112,7 @@ namespace matplotlibcpp {
105112
PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str()));
106113
}
107114

108-
PyObject* res = PyObject_Call(detail::_pyplot_global::get().s_python_function_plot, args, kwargs);
115+
PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_plot, args, kwargs);
109116

110117
Py_DECREF(args);
111118
Py_DECREF(kwargs);
@@ -136,7 +143,7 @@ namespace matplotlibcpp {
136143
PyTuple_SetItem(plot_args, 1, ylist);
137144
PyTuple_SetItem(plot_args, 2, pystring);
138145

139-
PyObject* res = PyObject_CallObject(detail::_pyplot_global::get().s_python_function_plot, plot_args);
146+
PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_plot, plot_args);
140147

141148
Py_DECREF(xlist);
142149
Py_DECREF(ylist);
@@ -166,7 +173,7 @@ namespace matplotlibcpp {
166173
PyTuple_SetItem(plot_args, 1, ylist);
167174
PyTuple_SetItem(plot_args, 2, pystring);
168175

169-
PyObject* res = PyObject_Call(detail::_pyplot_global::get().s_python_function_plot, plot_args, kwargs);
176+
PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_plot, plot_args, kwargs);
170177

171178
Py_DECREF(kwargs);
172179
Py_DECREF(xlist);
@@ -205,119 +212,8 @@ namespace matplotlibcpp {
205212
return named_plot<double>(name,x,y,format);
206213
}
207214

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-
319-
inline void legend() {
320-
PyObject* res = PyObject_CallObject(detail::_pyplot_global::get().s_python_function_legend, detail::_pyplot_global::get().s_python_empty_tuple);
215+
inline void legend() {
216+
PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_legend, detail::_interpreter::get().s_python_empty_tuple);
321217
if(!res) throw std::runtime_error("Call to legend() failed.");
322218

323219
Py_DECREF(res);
@@ -333,7 +229,7 @@ namespace matplotlibcpp {
333229
PyObject* args = PyTuple_New(1);
334230
PyTuple_SetItem(args, 0, list);
335231

336-
PyObject* res = PyObject_CallObject(detail::_pyplot_global::get().s_python_function_ylim, args);
232+
PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_ylim, args);
337233
if(!res) throw std::runtime_error("Call to ylim() failed.");
338234

339235
Py_DECREF(list);
@@ -351,7 +247,7 @@ namespace matplotlibcpp {
351247
PyObject* args = PyTuple_New(1);
352248
PyTuple_SetItem(args, 0, list);
353249

354-
PyObject* res = PyObject_CallObject(detail::_pyplot_global::get().s_python_function_xlim, args);
250+
PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_xlim, args);
355251
if(!res) throw std::runtime_error("Call to xlim() failed.");
356252

357253
Py_DECREF(list);
@@ -360,11 +256,127 @@ namespace matplotlibcpp {
360256
}
361257

362258
inline void show() {
363-
PyObject* res = PyObject_CallObject(detail::_pyplot_global::get().s_python_function_show, detail::_pyplot_global::get().s_python_empty_tuple);
259+
PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_show, detail::_interpreter::get().s_python_empty_tuple);
364260
if(!res) throw std::runtime_error("Call to show() failed.");
365261

366262
Py_DECREF(res);
367263
}
368264

265+
#if __cplusplus > 199711L
266+
// C++11-exclusive content starts here, in particular the variadic plot()
267+
268+
namespace detail {
269+
template<typename T>
270+
using is_function = typename std::is_function<std::remove_pointer<std::remove_reference<T>>>::type;
271+
272+
template<bool obj, typename T>
273+
struct is_callable_impl;
274+
275+
template<typename T>
276+
struct is_callable_impl<false, T>
277+
{
278+
typedef is_function<T> type;
279+
}; // a non-object is callable iff it is a function
280+
281+
template<typename T>
282+
struct is_callable_impl<true, T>
283+
{
284+
struct Fallback { void operator()(); };
285+
struct Derived : T, Fallback { };
286+
287+
template<typename U, U> struct Check;
288+
289+
template<typename U>
290+
static std::true_type test( ... ); // use a variadic function to make sure (1) it accepts everything and (2) its always the worst match
291+
292+
template<typename U>
293+
static std::false_type test( Check<void(Fallback::*)(), &U::operator()>* );
294+
295+
public:
296+
typedef decltype(test<Derived>(nullptr)) type;
297+
typedef decltype(&Fallback::operator()) dtype;
298+
static constexpr bool value = type::value;
299+
}; // an object is callable iff it defines operator()
300+
301+
template<typename T>
302+
struct is_callable
303+
{
304+
// dispatch to is_callable_impl<true, T> or is_callable_impl<false, T> depending on whether T is of class type or not
305+
typedef typename is_callable_impl<std::is_class<T>::value, T>::type type; // todo: restore remove_reference
306+
};
307+
308+
template<typename IsYDataCallable>
309+
struct plot_impl { };
310+
311+
template<>
312+
struct plot_impl<std::false_type>
313+
{
314+
template<typename IterableX, typename IterableY>
315+
bool operator()(const IterableX& x, const IterableY& y, const std::string& format)
316+
{
317+
// It's annoying that we have to repeat the code of plot() above
318+
auto xs = std::distance(std::begin(x), std::end(x));
319+
auto ys = std::distance(std::begin(y), std::end(y));
320+
assert(xs == ys && "x and y data must have the same number of elements!");
321+
322+
PyObject* xlist = PyList_New(xs);
323+
PyObject* ylist = PyList_New(ys);
324+
PyObject* pystring = PyString_FromString(format.c_str());
325+
326+
auto itx = std::begin(x), ity = std::begin(y);
327+
for(size_t i = 0; i < xs; ++i) {
328+
PyList_SetItem(xlist, i, PyFloat_FromDouble(*itx++));
329+
PyList_SetItem(ylist, i, PyFloat_FromDouble(*ity++));
330+
}
331+
332+
PyObject* plot_args = PyTuple_New(3);
333+
PyTuple_SetItem(plot_args, 0, xlist);
334+
PyTuple_SetItem(plot_args, 1, ylist);
335+
PyTuple_SetItem(plot_args, 2, pystring);
336+
337+
PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_plot, plot_args);
338+
339+
Py_DECREF(xlist);
340+
Py_DECREF(ylist);
341+
Py_DECREF(plot_args);
342+
if(res) Py_DECREF(res);
343+
344+
return res;
345+
}
346+
};
347+
348+
template<>
349+
struct plot_impl<std::true_type>
350+
{
351+
template<typename Iterable, typename Callable>
352+
bool operator()(const Iterable& ticks, const Callable& f, const std::string& format)
353+
{
354+
//std::cout << "Callable impl called" << std::endl;
355+
356+
if(begin(ticks) == end(ticks)) return true;
357+
358+
// We could use additional meta-programming to deduce the correct element type of y,
359+
// but all values have to be convertible to double anyways
360+
std::vector<double> y;
361+
for(auto x : ticks) y.push_back(f(x));
362+
return plot_impl<std::false_type>()(ticks,y,format);
363+
}
364+
};
365+
}
366+
367+
// recursion stop for the above
368+
template<typename... Args>
369+
bool plot() { return true; }
370+
371+
template<typename A, typename B, typename... Args>
372+
bool plot(const A& a, const B& b, const std::string& format, Args... args)
373+
{
374+
return detail::plot_impl<typename detail::is_callable<B>::type>()(a,b,format) && plot(args...);
375+
}
376+
377+
#endif
378+
379+
380+
369381

370382
}

0 commit comments

Comments
 (0)