Skip to content

Commit ef76547

Browse files
committed
Replaced regex with custom string parsing and a few more low hanging improvements
1 parent 24db6b8 commit ef76547

File tree

6 files changed

+75
-65
lines changed

6 files changed

+75
-65
lines changed

source/matplot/backend/backend_interface.h

+25-1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ namespace matplot {
4141
class MATPLOT_EXPORTS backend_interface {
4242
/// Virtual functions you can override to create any backend
4343
public:
44+
virtual ~backend_interface() noexcept = default;
4445
/// \brief True if backend is in interactive mode
4546
/// One backends might support both interactive and
4647
/// non-interactive mode.
@@ -213,7 +214,30 @@ namespace matplot {
213214
/// This is useful when tracing the gnuplot commands
214215
/// and when generating a gnuplot file.
215216
virtual void include_comment(const std::string &text);
216-
};
217+
}; // backend_interface
218+
/// Captures (backend) version information.
219+
struct Version {
220+
int major{}, minor{}, patch{};
221+
constexpr bool operator==(const Version &o) const {
222+
return major == o.major && minor == o.minor && patch == o.patch;
223+
}
224+
constexpr bool operator!=(const Version &other) const { return !(*this == other); }
225+
constexpr bool operator<(const Version &other) const {
226+
if (major < other.major)
227+
return true;
228+
else if (major == other.major) {
229+
if (minor < other.minor)
230+
return true;
231+
else if (minor == other.minor)
232+
return patch < other.patch;
233+
}
234+
return false;
235+
}
236+
constexpr bool operator>(const Version &other) const { return other < *this; }
237+
constexpr bool operator<=(const Version &other) const { return !(other < *this); }
238+
constexpr bool operator>=(const Version &other) const { return !(other > *this); }
239+
constexpr operator bool() const { return *this != Version{0,0,0}; }
240+
}; // struct Version
217241
} // namespace backend
218242

219243
} // namespace matplot

source/matplot/backend/gnuplot.cpp

+32-43
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
#include <matplot/util/common.h>
88
#include <matplot/util/popen.h>
99
#include <iostream>
10-
#include <regex>
10+
#include <charconv>
1111
#include <thread>
1212
#include <cstring>
1313
#include <cstdlib>
@@ -317,13 +317,28 @@ namespace matplot::backend {
317317
}
318318
}
319319

320+
/// returns the next word in text after prefix terminated with white space.
321+
std::string_view word_after(std::string_view text, std::string_view prefix)
322+
{
323+
auto res = text.substr(0,0);
324+
if (auto b = text.find(prefix); b != std::string_view::npos) {
325+
b += prefix.length();
326+
while (b < text.length() && std::isspace(text[b]))
327+
++b; // skip white space before word
328+
auto e = b;
329+
while (e < text.length() && !std::isspace(text[e]))
330+
++e; // scan until white space or end
331+
res = text.substr(b, e-b);
332+
}
333+
return res;
334+
}
335+
320336
std::string gnuplot::default_terminal_type() {
321337
static std::string terminal_type;
322338
const bool dont_know_term_type = terminal_type.empty();
323339
if (dont_know_term_type) {
324340
terminal_type = run_and_get_output("gnuplot -e \"show terminal\" 2>&1");
325-
terminal_type = std::regex_replace(terminal_type,
326-
std::regex("[^]*terminal type is ([^ ]+)[^]*"), "$1");
341+
terminal_type = word_after(terminal_type, "terminal type is ");
327342
const bool still_dont_know_term_type = terminal_type.empty();
328343
if (still_dont_know_term_type) {
329344
terminal_type = "qt";
@@ -334,51 +349,25 @@ namespace matplot::backend {
334349

335350
bool gnuplot::terminal_is_available(std::string_view term) {
336351
std::string msg = run_and_get_output("gnuplot -e \"set terminal " +
337-
std::string(term.data()) + "\" 2>&1");
352+
std::string{term} + "\" 2>&1");
338353
return msg.empty();
339354
}
340355

341-
std::tuple<int, int, int> gnuplot::gnuplot_version() {
342-
static std::tuple<int, int, int> version{0, 0, 0};
343-
const bool dont_know_gnuplot_version_yet =
344-
version == std::tuple<int, int, int>({0, 0, 0});
345-
if (dont_know_gnuplot_version_yet) {
346-
std::string version_str =
347-
run_and_get_output("gnuplot --version 2>&1");
348-
std::string version_major = std::regex_replace(
349-
version_str,
350-
std::regex("[^]*gnuplot (\\d+)\\.\\d+ patchlevel \\d+ *"),
351-
"$1");
352-
std::string version_minor = std::regex_replace(
353-
version_str,
354-
std::regex("[^]*gnuplot \\d+\\.(\\d+) patchlevel \\d+ *"),
355-
"$1");
356-
std::string version_patch = std::regex_replace(
357-
version_str,
358-
std::regex("[^]*gnuplot \\d+\\.\\d+ patchlevel (\\d+) *"),
359-
"$1");
360-
try {
361-
std::get<0>(version) = std::stoi(version_major);
362-
} catch (...) {
363-
std::get<0>(version) = 0;
364-
}
365-
try {
366-
std::get<1>(version) = std::stoi(version_minor);
367-
} catch (...) {
368-
std::get<1>(version) = 0;
369-
}
370-
try {
371-
std::get<2>(version) = std::stoi(version_patch);
372-
} catch (...) {
373-
std::get<2>(version) = 0;
374-
}
375-
const bool still_dont_know_gnuplot_version =
376-
version == std::tuple<int, int, int>({0, 0, 0});
377-
if (still_dont_know_gnuplot_version) {
378-
// assume it's 5.2.6 by convention
379-
version = std::tuple<int, int, int>({5, 2, 6});
356+
Version gnuplot::gnuplot_version() {
357+
static auto version = Version{0, 0, 0};
358+
if (!version) {
359+
const auto version_str = run_and_get_output("gnuplot --version 2>&1");
360+
const auto major = word_after(version_str, "gnuplot");
361+
const auto minor = word_after(major, ".");
362+
const auto patch = word_after(version_str, "patchlevel");
363+
if (!major.empty() && !minor.empty() && !patch.empty()) {
364+
std::from_chars(major.begin(), major.end(), version.major);
365+
std::from_chars(minor.begin(), major.end(), version.minor);
366+
std::from_chars(patch.begin(), major.end(), version.patch);
380367
}
381368
}
369+
if (!version)
370+
version = {5, 2, 6}; // assume by convention
382371
return version;
383372
}
384373

source/matplot/backend/gnuplot.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ namespace matplot::backend {
4747
/// Identify the default terminal type in the system
4848
static std::string default_terminal_type();
4949
static bool terminal_is_available(std::string_view);
50-
static std::tuple<int, int, int> gnuplot_version();
50+
static Version gnuplot_version();
5151
static bool terminal_has_title_option(const std::string &t);
5252
static bool terminal_has_size_option(const std::string &t);
5353
static bool terminal_has_position_option(const std::string &t);

source/matplot/core/axes_type.cpp

+10-12
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,7 @@ namespace matplot {
426426
run_command("set polar");
427427
}
428428
auto set_or_unset_axis = [this](class axis_type &ax,
429-
std::string axis_name,
429+
const std::string &axis_name,
430430
bool minor_ticks = false) {
431431
// cb is the only axis we don't unset if tics are empty
432432
// r-axis labels should still be handled even if axis is invisible since we use the grid
@@ -694,8 +694,7 @@ namespace matplot {
694694
// Gnuplot version needs to be 5.2.6+ for keyentry
695695
bool ok = true;
696696
if (parent_->backend_->consumes_gnuplot_commands()) {
697-
if (backend::gnuplot::gnuplot_version() <
698-
std::tuple<int, int, int>{5, 2, 6}) {
697+
if (backend::gnuplot::gnuplot_version() < backend::Version{5, 2, 6}) {
699698
ok = false;
700699
}
701700
}
@@ -916,9 +915,8 @@ namespace matplot {
916915
static bool msg_shown_once = false;
917916
// Gnuplot version needs to be 5.2.6+ for keyentry
918917
if (parent_->backend_->consumes_gnuplot_commands()) {
919-
std::tuple<int, int, int> v =
920-
backend::gnuplot::gnuplot_version();
921-
if (v < std::tuple<int, int, int>{5, 2, 6}) {
918+
const auto v = backend::gnuplot::gnuplot_version();
919+
if (v < backend::Version{5, 2, 6}) {
922920
if (!msg_shown_once) {
923921
std::cerr
924922
<< "You need gnuplot 5.2.6+ to include legends"
@@ -2745,9 +2743,9 @@ namespace matplot {
27452743
}
27462744

27472745
std::vector<function_line_handle>
2748-
axes_type::fplot(std::vector<function_line::function_type> equations,
2749-
std::array<double, 2> x_range,
2750-
std::vector<std::string> line_specs) {
2746+
axes_type::fplot(const std::vector<function_line::function_type> &equations,
2747+
const std::array<double, 2> &x_range,
2748+
const std::vector<std::string> &line_specs) {
27512749
axes_silencer temp_silencer_{this};
27522750
std::vector<function_line_handle> res;
27532751
auto it_line_specs = line_specs.begin();
@@ -2764,9 +2762,9 @@ namespace matplot {
27642762
}
27652763

27662764
std::vector<function_line_handle>
2767-
axes_type::fplot(std::vector<function_line::function_type> equations,
2768-
std::vector<double> x_range,
2769-
std::vector<std::string> line_specs) {
2765+
axes_type::fplot(const std::vector<function_line::function_type>& equations,
2766+
const std::vector<double>& x_range,
2767+
const std::vector<std::string>& line_specs) {
27702768
return this->fplot(equations, to_array<2>(x_range), line_specs);
27712769
}
27722770

source/matplot/core/axes_type.h

+6-6
Original file line numberDiff line numberDiff line change
@@ -979,15 +979,15 @@ namespace matplot {
979979

980980
/// Lambda function line plot - list of functions
981981
std::vector<function_line_handle>
982-
fplot(std::vector<function_line::function_type> equations,
983-
std::array<double, 2> x_range = {-5, 5},
984-
std::vector<std::string> line_specs = {});
982+
fplot(const std::vector<function_line::function_type> &equations,
983+
const std::array<double, 2> &x_range = {-5, 5},
984+
const std::vector<std::string> &line_specs = {});
985985

986986
/// Lambda function line plot - list of functions and line specs
987987
std::vector<function_line_handle>
988-
fplot(std::vector<function_line::function_type> equations,
989-
std::vector<double> x_range,
990-
std::vector<std::string> line_specs = {});
988+
fplot(const std::vector<function_line::function_type> &equations,
989+
const std::vector<double> &x_range,
990+
const std::vector<std::string> &line_specs = {});
991991

992992
using implicit_function_type = std::function<double(double, double)>;
993993

source/matplot/core/figure_type.cpp

+1-2
Original file line numberDiff line numberDiff line change
@@ -634,8 +634,7 @@ namespace matplot {
634634
// In gnuplot 5.5 we have the wall function to set the axes color
635635
// with a rectangle workaround, which does not work well for 3d.
636636
static const auto v = backend::gnuplot::gnuplot_version();
637-
const bool has_wall_option =
638-
std::get<0>(v) > 5 || (std::get<0>(v) == 5 && std::get<1>(v) >= 5);
637+
const bool has_wall_option = v >= backend::Version{5,5,0};
639638
// So we only plot the default background if it's not 3d or version is
640639
// higher than 5.5. Otherwise, gnuplot won't be able to set the axes
641640
// colors.

0 commit comments

Comments
 (0)