From a8f512f6ed672bc2c111661497b9b1fd86084c2d Mon Sep 17 00:00:00 2001 From: Lennart Steffen Date: Mon, 15 Aug 2022 18:28:20 +0200 Subject: [PATCH 1/8] removed std::cout from constructor, added throw on pipe failure --- include/gnuplot.h | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/include/gnuplot.h b/include/gnuplot.h index cb6cfa4..e26316b 100644 --- a/include/gnuplot.h +++ b/include/gnuplot.h @@ -24,15 +24,18 @@ #include #include +#include + class GnuplotPipe { public: inline GnuplotPipe(bool persist = true) { - std::cout << "Opening gnuplot... "; + /* L. Steffen: + * removed std::cout call on success, + * replaced it with throw on failure. + * */ pipe = popen(persist ? "gnuplot -persist" : "gnuplot", "w"); if (!pipe) - std::cout << "failed!" << std::endl; - else - std::cout << "succeded." << std::endl; + throw std::runtime_error("Failed to open gnuplot pipe!"); } inline virtual ~GnuplotPipe(){ if (pipe) pclose(pipe); @@ -49,7 +52,7 @@ class GnuplotPipe { if (!pipe) return; for (unsigned i = 0; i < repeatBuffer; i++) { for (auto& line : buffer) fputs(line.c_str(), pipe); - fputs("e\n", pipe); + fputs("\n", pipe); } fflush(pipe); buffer.clear(); From ae84a1509c30cfd5c16930ab5c8e3cb602ce5156 Mon Sep 17 00:00:00 2001 From: Lennart Steffen Date: Mon, 15 Aug 2022 18:28:40 +0200 Subject: [PATCH 2/8] extended example --- example/example.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/example/example.cpp b/example/example.cpp index 8fe6778..e4a3cff 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -1,7 +1,15 @@ #include "gnuplot.h" +#include +#include + int main(){ GnuplotPipe gp; gp.sendLine("plot [-pi/2:pi] cos(x),-(sin(x) > sin(x+1) ? sin(x) : sin(x+1))"); + gp.sendEndOfData(); + int duration {2}; + std::cout << "Sleeping for " << duration << "s, then plotting next graph.\n"; + std::this_thread::sleep_for(std::chrono::seconds(duration)); + gp.sendLine("plot [-pi/2:pi] -(sin(x) > sin(x+1) ? sin(x+1) : sin(x))"); return 0; } From a43055731179547881030bd382e9173b31cd727c Mon Sep 17 00:00:00 2001 From: Lennart Steffen Date: Mon, 15 Aug 2022 18:59:40 +0200 Subject: [PATCH 3/8] default constructor now does nothing, moved opening of pipe to GnuplotPipe::open --- example/example.cpp | 1 + include/gnuplot.h | 85 ++++++++++++++++++++++++--------------------- 2 files changed, 46 insertions(+), 40 deletions(-) diff --git a/example/example.cpp b/example/example.cpp index e4a3cff..13942ae 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -5,6 +5,7 @@ int main(){ GnuplotPipe gp; + gp.open(); gp.sendLine("plot [-pi/2:pi] cos(x),-(sin(x) > sin(x+1) ? sin(x) : sin(x+1))"); gp.sendEndOfData(); int duration {2}; diff --git a/include/gnuplot.h b/include/gnuplot.h index e26316b..96a265d 100644 --- a/include/gnuplot.h +++ b/include/gnuplot.h @@ -28,49 +28,54 @@ class GnuplotPipe { public: - inline GnuplotPipe(bool persist = true) { - /* L. Steffen: - * removed std::cout call on success, - * replaced it with throw on failure. - * */ - pipe = popen(persist ? "gnuplot -persist" : "gnuplot", "w"); - if (!pipe) - throw std::runtime_error("Failed to open gnuplot pipe!"); - } - inline virtual ~GnuplotPipe(){ - if (pipe) pclose(pipe); - } + GnuplotPipe() = default; - void sendLine(const std::string& text, bool useBuffer = false){ - if (!pipe) return; - if (useBuffer) - buffer.push_back(text + "\n"); - else - fputs((text + "\n").c_str(), pipe); - } - void sendEndOfData(unsigned repeatBuffer = 1){ - if (!pipe) return; - for (unsigned i = 0; i < repeatBuffer; i++) { - for (auto& line : buffer) fputs(line.c_str(), pipe); - fputs("\n", pipe); - } - fflush(pipe); - buffer.clear(); - } - void sendNewDataBlock(){ - sendLine("\n", !buffer.empty()); - } + void open(bool persist = true) { + /* L. Steffen: + * removed std::cout call on success, + * replaced it with throw on failure. + * */ + pipe = popen(persist ? "gnuplot -persist" : "gnuplot", "w"); + if (!pipe) + throw std::runtime_error("Failed to open gnuplot pipe!"); + } - void writeBufferToFile(const std::string& fileName){ - std::ofstream fileOut(fileName); - for (auto& line : buffer) fileOut << line; - fileOut.close(); - } + inline virtual ~GnuplotPipe(){ + if (pipe) pclose(pipe); + } + + void sendLine(const std::string& text, bool useBuffer = false){ + if (!pipe) return; + if (useBuffer) + buffer.push_back(text + "\n"); + else + fputs((text + "\n").c_str(), pipe); + } + + void sendEndOfData(unsigned repeatBuffer = 1){ + if (!pipe) return; + for (unsigned i = 0; i < repeatBuffer; i++) { + for (auto& line : buffer) fputs(line.c_str(), pipe); + fputs("\n", pipe); + } + fflush(pipe); + buffer.clear(); + } + + void sendNewDataBlock(){ + sendLine("\n", !buffer.empty()); + } + + void writeBufferToFile(const std::string& fileName){ + std::ofstream fileOut(fileName); + for (auto& line : buffer) fileOut << line; + fileOut.close(); + } private: - GnuplotPipe(GnuplotPipe const&) = delete; - void operator=(GnuplotPipe const&) = delete; + GnuplotPipe(GnuplotPipe const&) = delete; + void operator=(GnuplotPipe const&) = delete; - FILE* pipe; - std::vector buffer; + FILE* pipe {nullptr}; + std::vector buffer; }; From 2dee406e1b0274a44b8ecdff8a0dc166b1f4135a Mon Sep 17 00:00:00 2001 From: Lennart Steffen Date: Mon, 15 Aug 2022 19:48:14 +0200 Subject: [PATCH 4/8] added public bool isOpen() const --- include/gnuplot.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/include/gnuplot.h b/include/gnuplot.h index 96a265d..9d7c76a 100644 --- a/include/gnuplot.h +++ b/include/gnuplot.h @@ -14,6 +14,7 @@ * * URL: https://github.com/martinruenz/gnuplot-cpp * AUTHOR: Martin Rünz, 2015 + * Modifications: Lennart Steffen, 2022 */ #pragma once @@ -30,11 +31,11 @@ class GnuplotPipe { public: GnuplotPipe() = default; + bool isOpen() const { return pipe != nullptr; } + void open(bool persist = true) { - /* L. Steffen: - * removed std::cout call on success, - * replaced it with throw on failure. - * */ + if ( isOpen() ) + return; pipe = popen(persist ? "gnuplot -persist" : "gnuplot", "w"); if (!pipe) throw std::runtime_error("Failed to open gnuplot pipe!"); From 8a091262902b60ea582edc376a9ef1fb888828dd Mon Sep 17 00:00:00 2001 From: Lennart Steffen Date: Tue, 16 Aug 2022 10:22:31 +0200 Subject: [PATCH 5/8] reverted removal of 'e' in sendEndOfData --- include/gnuplot.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/gnuplot.h b/include/gnuplot.h index 9d7c76a..342c0d5 100644 --- a/include/gnuplot.h +++ b/include/gnuplot.h @@ -57,7 +57,7 @@ class GnuplotPipe { if (!pipe) return; for (unsigned i = 0; i < repeatBuffer; i++) { for (auto& line : buffer) fputs(line.c_str(), pipe); - fputs("\n", pipe); + fputs("e\n", pipe); } fflush(pipe); buffer.clear(); From 507fd8c327e92e92ce50b83cabd002274586aaed Mon Sep 17 00:00:00 2001 From: Lennart Steffen Date: Tue, 16 Aug 2022 16:55:19 +0200 Subject: [PATCH 6/8] new example --- example/.gitignore | 1 + example/CMakeLists.txt | 4 +- example/example.cpp | 18 ++++---- example/example2.cpp | 93 ++++++++++++++++++++++++++++++++++++++++++ include/gnuplot.h | 5 +++ 5 files changed, 111 insertions(+), 10 deletions(-) create mode 100644 example/example2.cpp diff --git a/example/.gitignore b/example/.gitignore index 17e4b9a..6f28605 100644 --- a/example/.gitignore +++ b/example/.gitignore @@ -3,3 +3,4 @@ Default/ Release/ build/ CMakeLists.txt.user +*.dat diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index bb7641d..352db24 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -1,6 +1,8 @@ cmake_minimum_required(VERSION 3.0) project (gnuplot_cpp_example) -set (CMAKE_CXX_STANDARD 11) +set (CMAKE_CXX_STANDARD 17) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../include) add_executable(gnuplot_cpp_example example.cpp ../include/gnuplot.h) + +add_executable(gnuplot_cpp_example2 example2.cpp) diff --git a/example/example.cpp b/example/example.cpp index 13942ae..02a8970 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -4,13 +4,13 @@ int main(){ - GnuplotPipe gp; - gp.open(); - gp.sendLine("plot [-pi/2:pi] cos(x),-(sin(x) > sin(x+1) ? sin(x) : sin(x+1))"); - gp.sendEndOfData(); - int duration {2}; - std::cout << "Sleeping for " << duration << "s, then plotting next graph.\n"; - std::this_thread::sleep_for(std::chrono::seconds(duration)); - gp.sendLine("plot [-pi/2:pi] -(sin(x) > sin(x+1) ? sin(x+1) : sin(x))"); - return 0; + GnuplotPipe gp; + gp.open(); + gp.sendLine("plot [-pi/2:pi] cos(x),-(sin(x) > sin(x+1) ? sin(x) : sin(x+1))"); + gp.sendEndOfData(); + int duration {2}; + std::cout << "Sleeping for " << duration << "s, then plotting next graph.\n"; + std::this_thread::sleep_for(std::chrono::seconds(duration)); + gp.sendLine("plot [-pi/2:pi] -(sin(x) > sin(x+1) ? sin(x+1) : sin(x))"); + return 0; } diff --git a/example/example2.cpp b/example/example2.cpp new file mode 100644 index 0000000..fdf5f87 --- /dev/null +++ b/example/example2.cpp @@ -0,0 +1,93 @@ +#include "gnuplot.h" +#include +#include +#include +#include +#include +#include +#include + +namespace fs = std::filesystem; + +std::ofstream getFile( const fs::path& path, bool append = false ){ + std::ofstream of; + of.open( path.c_str(), + append ? std::ios_base::app : std::ios_base::trunc + ); + if (of){ + return of; + } + throw std::runtime_error( + "Could not write to file \"" + path.string() + "\"!" + ); +} + +void writeLine( + const std::string& line, + const fs::path& path, + bool append = false +){ + std::ofstream of { getFile(path, append) }; + of << line << '\n'; + of.close(); +} + + +class LineWriter { +private: + fs::path m_path; + bool m_append {false}; +public: + LineWriter(const fs::path& p) : m_path {p} {} + void append(bool b){ m_append = b; } + + void write( const std::string& str ){ + writeLine(str, m_path, m_append); + } +}; + +int main(){ + const fs::path wd { fs::current_path().lexically_normal() }; + double xmin {0}, xmax {5}, xstep {0.1}; + int sleep_ms {100}; + + using Func = double (*)(double); + Func fn { std::sin }; + + std::string outf { "outf.dat" }; + fs::path outp { wd / outf }; + LineWriter lw {outp}; + lw.write("title_x title_y"); + lw.append(true); + + std::stringstream plotCommand; plotCommand + << "plot " + << '\"' << outf << '\"' // file + << " using 1:2" // cols + << " with lines" // style + ; + std::stringstream rng; rng + << "set xrange [" << xmin << ':' << xmax << ']'; + + GnuplotPipe gp; + gp.open(); + gp.sendLine( "cd \"" + wd.string() + "\"" ); + gp.sendLine( "set key autotitle columnhead" ); + gp.sendLine( rng.str() ); + + bool first {true}; + for ( double x{xmin}; x Date: Tue, 23 Aug 2022 12:34:14 +0200 Subject: [PATCH 7/8] allowed move construction and move assignment --- include/gnuplot.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/gnuplot.h b/include/gnuplot.h index 766cf4b..912d926 100644 --- a/include/gnuplot.h +++ b/include/gnuplot.h @@ -31,6 +31,9 @@ class GnuplotPipe { public: GnuplotPipe() = default; + GnuplotPipe(GnuplotPipe&&) = default; + GnuplotPipe& operator=(GnuplotPipe&&) = default; + bool isOpen() const { return pipe != nullptr; } void open(bool persist = true) { @@ -80,7 +83,7 @@ class GnuplotPipe { private: GnuplotPipe(GnuplotPipe const&) = delete; - void operator=(GnuplotPipe const&) = delete; + GnuplotPipe& operator=(GnuplotPipe const&) = delete; FILE* pipe {nullptr}; std::vector buffer; From 2fcd0f445a7ee70916fa8ba1cf239804b15fae4c Mon Sep 17 00:00:00 2001 From: lens Date: Tue, 30 Aug 2022 18:08:01 +0200 Subject: [PATCH 8/8] throw when accessing non-open pipe --- include/gnuplot.h | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/include/gnuplot.h b/include/gnuplot.h index 912d926..ce1a173 100644 --- a/include/gnuplot.h +++ b/include/gnuplot.h @@ -49,7 +49,7 @@ class GnuplotPipe { } void sendLine(const std::string& text, bool useBuffer = false){ - if (!pipe) return; + throwIfNotOpen(); if (useBuffer) buffer.push_back(text + "\n"); else @@ -57,12 +57,12 @@ class GnuplotPipe { } void flush(){ - if (!pipe) return; + throwIfNotOpen(); fflush(pipe); } void sendEndOfData(unsigned repeatBuffer = 1){ - if (!pipe) return; + throwIfNotOpen(); for (unsigned i = 0; i < repeatBuffer; i++) { for (auto& line : buffer) fputs(line.c_str(), pipe); fputs("e\n", pipe); @@ -84,6 +84,10 @@ class GnuplotPipe { private: GnuplotPipe(GnuplotPipe const&) = delete; GnuplotPipe& operator=(GnuplotPipe const&) = delete; + void throwIfNotOpen() const { + if (!pipe) + throw std::runtime_error("Gnuplot pipe is not open!"); + } FILE* pipe {nullptr}; std::vector buffer;