Skip to content

fix segfault on exit with python3 #301

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions .clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: None
AlignConsecutiveBitFields: None
AlignConsecutiveDeclarations: None
AlignConsecutiveMacros: AcrossEmptyLinesAndComments
AlignEscapedNewlines: Right
AlignTrailingComments: false
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: Always
AllowShortCaseLabelsOnASingleLine: false
AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: true
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: true
BinPackArguments: true
BinPackParameters: true
BitFieldColonSpacing: Both
BreakBeforeBraces: Custom
BraceWrapping:
AfterCaseLabel: false
AfterClass: true
AfterFunction: true
AfterControlStatement: false
SplitEmptyFunction: false
AfterEnum: false
AfterNamespace: true
AfterStruct: true
AfterUnion: false
AfterExternBlock: false
BeforeCatch: true
BeforeElse: true
BeforeLambdaBody: false
BeforeWhile: true
SplitEmptyRecord: false
SplitEmptyNamespace: false
BreakBeforeBinaryOperators: All
BreakBeforeConceptDeclarations: false
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
BreakInheritanceList: BeforeColon
BreakStringLiterals: true
ColumnLimit: 80
CompactNamespaces: false
Cpp11BracedListStyle: true
DerivePointerAlignment: false
EmptyLineBeforeAccessModifier: Never
FixNamespaceComments: true
IncludeBlocks: Merge
IndentCaseLabels: false
IndentExternBlock: Indent
IndentRequires: false
IndentWidth: 4
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: false
NamespaceIndentation: All
PointerAlignment: Left
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: false
SpaceAroundPointerQualifiers: Default
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: Never
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesInCStyleCastParentheses: false
SpacesInConditionalStatement: false
SpacesInContainerLiterals: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
UseTab: Never
17 changes: 16 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ include(GNUInstallDirs)
set(PACKAGE_NAME matplotlib_cpp)
set(INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/${PACKAGE_NAME}/cmake)


# Library target
add_library(matplotlib_cpp INTERFACE)
target_include_directories(matplotlib_cpp
Expand Down Expand Up @@ -100,6 +99,22 @@ if(Python3_NumPy_FOUND)
add_executable(spy examples/spy.cpp)
target_link_libraries(spy PRIVATE matplotlib_cpp)
set_target_properties(spy PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")

add_executable(span examples/span.cpp)
target_link_libraries(span PRIVATE matplotlib_cpp)
target_compile_features(span PRIVATE cxx_std_20)
set_target_properties(span PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")

add_executable(ranges examples/ranges.cpp)
target_link_libraries(ranges PRIVATE matplotlib_cpp)
target_compile_features(ranges PRIVATE cxx_std_20)
set_target_properties(ranges PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")

add_executable(timeseries examples/timeseries.cpp)
target_link_libraries(timeseries PRIVATE matplotlib_cpp)
target_compile_features(timeseries PRIVATE cxx_std_20)
set_target_properties(timeseries PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")

endif()


Expand Down
138 changes: 138 additions & 0 deletions datetime_utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
#pragma once

#include "matplotlibcpp.h"
#include <Python.h>
#include <chrono>
#include <cstdlib>
#include <datetime.h>
#include <map>
#include <ranges>
#include <string>

// Convenience functions for converting C/C++ time objects to datetime.datetime
// objects. These are outside the matplotlibcpp namespace because they do not
// exist in matplotlib.pyplot.
template<class TimePoint>
PyObject* toPyDateTime(const TimePoint& t, int dummy = 0)
{
using namespace std::chrono;
auto tsec = time_point_cast<seconds>(t);
auto us = duration_cast<microseconds>(t - tsec);

time_t tt = system_clock::to_time_t(t);
PyObject* obj = toPyDateTime(tt, us.count());

return obj;
}

template<>
PyObject* toPyDateTime(const time_t& t, int us)
{
tm tm{};
gmtime_r(&t, &tm); // compatible with matlab, inverse of datenum.

if(!PyDateTimeAPI) { PyDateTime_IMPORT; }

PyObject* obj = PyDateTime_FromDateAndTime(tm.tm_year + 1900, tm.tm_mon + 1,
tm.tm_mday, tm.tm_hour,
tm.tm_min, tm.tm_sec, us);
if(obj) {
PyDateTime_Check(obj);
Py_INCREF(obj);
}

return obj;
}

template<class Time_t>
PyObject* toPyDateTimeList(const Time_t* t, size_t nt)
{
PyObject* tlist = PyList_New(nt);
if(tlist == nullptr) return nullptr;

if(!PyDateTimeAPI) { PyDateTime_IMPORT; }

for(size_t i = 0; i < nt; i++) {
PyObject* ti = toPyDateTime(t[i], 0);
PyList_SET_ITEM(tlist, i, ti);
}

return tlist;
}

template<class Time_t>
class DateTimeList
{
public:

DateTimeList() = default;

DateTimeList(const Time_t* t, size_t nt)
{
matplotlibcpp::detail::_interpreter::get();
tlist = (PyListObject*)toPyDateTimeList(t, nt);
}

~DateTimeList()
{
if(tlist) Py_DECREF((PyObject*)tlist);
}

DateTimeList& operator=(const DateTimeList& rhs) {
tlist=rhs.tlist;
Py_INCREF(tlist);
return *this;
}

PyListObject* get() const { return tlist; }
size_t size() const { return tlist ? PyList_Size((PyObject*)tlist) : 0; }
private:
mutable PyListObject* tlist = nullptr;
};

namespace matplotlibcpp
{
// special purpose function to plot against python datetime objects.
template<class Time_t, std::ranges::contiguous_range ContainerY>
bool plot(const DateTimeList<Time_t>& t, const ContainerY& y,
const std::string& fmt = "")
{
detail::_interpreter::get();

// DECREF decrements the ref counts of all objects in the plot_args
// tuple, In particular, it decreasesthe ref count of the time array x.
// We want to maintain that unchanged though, so we can reuse it.
PyListObject* tarray = t.get();
Py_INCREF(tarray);

NPY_TYPES ytype
= detail::select_npy_type<typename ContainerY::value_type>::type;

npy_intp tsize = PyList_Size((PyObject*)tarray);
assert(y.size() % tsize == 0
&& "length of y must be a multiple of length of x!");

npy_intp yrows = tsize, ycols = y.size() / yrows;
npy_intp ysize[] = {yrows, ycols}; // ysize[0] must equal tsize

PyObject* yarray = PyArray_New(&PyArray_Type, 2, ysize, ytype, nullptr,
(void*)y.data(), 0, NPY_ARRAY_FARRAY,
nullptr); // col major

PyObject* pystring = PyString_FromString(fmt.c_str());

PyObject* plot_args = PyTuple_New(3);
PyTuple_SetItem(plot_args, 0, (PyObject*)tarray);
PyTuple_SetItem(plot_args, 1, yarray);
PyTuple_SetItem(plot_args, 2, pystring);

PyObject* res = PyObject_CallObject(
detail::_interpreter::get().s_python_function_plot, plot_args);

Py_DECREF(plot_args);
if(res) Py_DECREF(res);

return true;
}

} // namespace matplotlibcpp
59 changes: 33 additions & 26 deletions examples/animation.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
//
// g++ -g -Wall -o animation $(python-config --includes) animation.cpp $(python-config --ldflags --embed)
//

#define _USE_MATH_DEFINES
#include <cmath>
#include "../matplotlibcpp.h"
Expand All @@ -6,31 +10,34 @@ namespace plt = matplotlibcpp;

int main()
{
int n = 1000;
std::vector<double> x, y, z;

for(int i=0; i<n; i++) {
x.push_back(i*i);
y.push_back(sin(2*M_PI*i/360.0));
z.push_back(log(i));

if (i % 10 == 0) {
// Clear previous plot
plt::clf();
// Plot line from given x and y data. Color is selected automatically.
plt::plot(x, y);
// Plot a line whose name will show up as "log(x)" in the legend.
plt::named_plot("log(x)", x, z);

// Set x-axis to interval [0,1000000]
plt::xlim(0, n*n);

// Add graph title
plt::title("Sample figure");
// Enable legend.
plt::legend();
// Display plot continuously
plt::pause(0.01);
}
int n = 1000;
std::vector<double> x, y, z;

for(int i=0; i<n; i++) {
x.push_back(i*i);
y.push_back(sin(2*M_PI*i/360.0));
z.push_back(log(i));

if (i % 10 == 0) {
// Clear previous plot
plt::clf();
// Plot line from given x and y data. Color is selected automatically.
plt::plot(x, y);
// Plot a line whose name will show up as "log(x)" in the legend.
plt::named_plot("log(x)", x, z);

// Set x-axis to interval [0,1000000]
plt::xlim(0, n*n);

// Add graph title
plt::title("Sample figure");
// Enable legend.
plt::legend();
// Display plot continuously
plt::pause(0.01);
}
}

plt::detail::_interpreter::kill();
return 0;
}
5 changes: 5 additions & 0 deletions examples/bar.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
//
// g++ -g -Wall -o bar $(python-config --includes) bar.cpp $(python-config --ldflags --embed)
//

#define _USE_MATH_DEFINES

#include <iostream>
Expand All @@ -14,5 +18,6 @@ int main(int argc, char **argv) {
plt::bar(test_data);
plt::show();

plt::detail::_interpreter::kill();
return (0);
}
12 changes: 10 additions & 2 deletions examples/basic.cpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
//
// g++ -g -Wall -std=c++20 -o basic -I/usr/include/python3.9 basic.cpp -lpython3.9
// g++ -g -Wall -std=c++20 -o basic $(python-config --includes) basic.cpp $(python-config --ldflags --embed)
//

#define _USE_MATH_DEFINES
#include <iostream>
#include <cmath>
#include "../matplotlibcpp.h"

namespace plt = matplotlibcpp;

int main()
int main()
{
// Prepare data.
int n = 5000;
Expand All @@ -15,7 +20,7 @@ int main()
y.at(i) = sin(2*M_PI*i/360.0);
z.at(i) = log(i);
}

// Set the size of output image = 1200x780 pixels
plt::figure_size(1200, 780);

Expand All @@ -41,4 +46,7 @@ int main()
const char* filename = "./basic.png";
std::cout << "Saving result to " << filename << std::endl;;
plt::save(filename);

plt::detail::_interpreter::kill();
return 0;
}
Binary file modified examples/basic.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions examples/colorbar.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
//
// g++ -g -Wall -o colorbar $(python-config --includes) colorbar.cpp $(python-config --ldflags --embed)
//

#define _USE_MATH_DEFINES
#include <cmath>
#include <iostream>
Expand Down Expand Up @@ -29,4 +33,7 @@ int main()
plt::show();
plt::close();
Py_DECREF(mat);

plt::detail::_interpreter::kill();
return 0;
}
Loading