From 004174952513f9be73b6e75934fbae4a3a0a93f1 Mon Sep 17 00:00:00 2001 From: Vincent La Date: Sat, 30 Mar 2019 17:29:16 -0700 Subject: [PATCH] C++11 compatibility (#19) * Successfully compiled on g++ 6.4 with -std=c++11 * Resolved all MSVC compiler warnings * Code clean up --- CMakeLists.txt | 10 ++++++- CMakeSettings.json | 30 +++++++++++++++++++ codecov.yml | 2 +- include/csv.hpp | 8 ++++- .../{string_view.hpp => compatibility.hpp} | 6 ++-- include/internal/csv_reader.cpp | 24 +++++---------- include/internal/csv_reader.hpp | 13 ++++---- include/internal/csv_row.cpp | 5 ++-- include/internal/csv_row.hpp | 8 +++-- include/internal/csv_stat.cpp | 2 +- include/internal/csv_utility.cpp | 8 +++-- .../{csv_utility.h => csv_utility.hpp} | 8 +++++ include/internal/csv_writer.hpp | 2 +- include/internal/data_type.cpp | 2 +- include/internal/data_type.h | 2 +- programs/csv_bench.cpp | 3 +- programs/csv_info.cpp | 2 +- tests/main.cpp | 4 +++ tests/test_csv_row.cpp | 4 +-- tests/test_data_type.cpp | 23 +++++++++----- 20 files changed, 113 insertions(+), 53 deletions(-) create mode 100644 CMakeSettings.json rename include/internal/{string_view.hpp => compatibility.hpp} (70%) rename include/internal/{csv_utility.h => csv_utility.hpp} (83%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 07bb37d8..d3c20429 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,13 +6,21 @@ set(CMAKE_CXX_STANDARD 17) if (MSVC) # Make Visual Studio report accurate C++ version # See: https://devblogs.microsoft.com/cppblog/msvc-now-correctly-reports-__cplusplus/ - set(CMAKE_CXX_FLAGS "/Zc:__cplusplus") + set(CMAKE_CXX_FLAGS "/EHsc /Zc:__cplusplus") + + if(CMAKE_BUILD_TYPE MATCHES Debug) + # /Wall emits warnings about the C++ standard library + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") + endif(CMAKE_BUILD_TYPE MATCHES Debug) + else() set(CMAKE_CXX_FLAGS "-pthread") set(CMAKE_CXX_FLAGS_RELEASE "-O3") set(CMAKE_CXX_FLAGS_DEBUG "-Og -g -lgcov --coverage") endif(MSVC) +message("CSV for C++ ${CMAKE_BUILD_TYPE} Build with ${CMAKE_CXX_COMPILER}") + set(SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/include/internal/) set(TEST_DIR ${CMAKE_CURRENT_LIST_DIR}/tests) diff --git a/CMakeSettings.json b/CMakeSettings.json new file mode 100644 index 00000000..f82f8033 --- /dev/null +++ b/CMakeSettings.json @@ -0,0 +1,30 @@ +{ + "configurations": [ + { + "name": "x64-Release", + "generator": "Ninja", + "configurationType": "RelWithDebInfo", + "inheritEnvironments": [ + "msvc_x64_x64" + ], + "buildRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\build\\${name}", + "installRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "-v", + "ctestCommandArgs": "" + }, + { + "name": "x64-Debug", + "generator": "Ninja", + "configurationType": "Debug", + "inheritEnvironments": [ + "msvc_x64_x64" + ], + "buildRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\build\\${name}", + "installRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "-v", + "ctestCommandArgs": "" + } + ] +} \ No newline at end of file diff --git a/codecov.yml b/codecov.yml index 7eb45ebb..c75dcbee 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,3 +1,3 @@ ignore: - - "include/external" + - "include/external" - "tests" \ No newline at end of file diff --git a/include/csv.hpp b/include/csv.hpp index ba515deb..4055e667 100644 --- a/include/csv.hpp +++ b/include/csv.hpp @@ -2,9 +2,15 @@ #ifndef CSV_HPP #define CSV_HPP +// Add more compiler specific debug logic here +#define NDEBUG +#if _DEBUG + #undef NDEBUG +#endif + #include "internal/csv_reader.hpp" #include "internal/csv_stat.hpp" -#include "internal/csv_utility.h" +#include "internal/csv_utility.hpp" #include "internal/csv_writer.hpp" #endif \ No newline at end of file diff --git a/include/internal/string_view.hpp b/include/internal/compatibility.hpp similarity index 70% rename from include/internal/string_view.hpp rename to include/internal/compatibility.hpp index e5ad43b4..d5ed3f29 100644 --- a/include/internal/string_view.hpp +++ b/include/internal/compatibility.hpp @@ -1,12 +1,12 @@ #pragma once #include "../external/string_view.hpp" +#define SUPPRESS_UNUSED_WARNING(x) x + namespace csv { - using namespace std::literals; - using namespace nonstd::literals; using namespace nonstd; - #if __cplusplus == 201703L + #if __cplusplus >= 201703L #include using string_view = std::string_view; #else diff --git a/include/internal/csv_reader.cpp b/include/internal/csv_reader.cpp index 1117ab20..8d6d3367 100644 --- a/include/internal/csv_reader.cpp +++ b/include/internal/csv_reader.cpp @@ -12,11 +12,6 @@ namespace csv { namespace internals { - bool is_equal(double a, double b, double epsilon) { - /** Returns true if two doubles are about the same */ - return std::abs(a - b) < epsilon; - } - std::string format_row(const std::vector& row, const std::string& delim) { /** Print a CSV row */ std::stringstream ret; @@ -46,10 +41,6 @@ namespace csv { return ret; } - void GiantStringBuffer::operator+=(const char ch) { - *(this->buffer) += ch; - } - size_t GiantStringBuffer::size() const { // Return size of current row return (this->buffer->size() - this->current_end); @@ -266,7 +257,7 @@ namespace csv { strict = format.strict; // Read first 500KB of CSV - read_csv(filename, 500000, false); + read_csv(filename, 500000); } /** @brief Return the format of the original raw CSV */ @@ -288,9 +279,9 @@ namespace csv { * csv::CSV_NOT_FOUND otherwise. */ int CSVReader::index_of(const std::string& col_name) const { - auto col_names = this->get_col_names(); - for (size_t i = 0; i < col_names.size(); i++) - if (col_names[i] == col_name) return (int)i; + auto _col_names = this->get_col_names(); + for (size_t i = 0; i < _col_names.size(); i++) + if (_col_names[i] == col_name) return (int)i; return CSV_NOT_FOUND; } @@ -454,12 +445,11 @@ namespace csv { * @brief Parse a CSV file using multiple threads * * @param[in] nrows Number of rows to read. Set to -1 to read entire file. - * @param[in] close Close file after reading? * * @see CSVReader::read_row() * */ - void CSVReader::read_csv(const std::string& filename, const size_t& bytes, bool close) { + void CSVReader::read_csv(const std::string& filename, const size_t& bytes) { if (!this->infile) { #ifdef _MSC_BUILD // Silence compiler warnings in Microsoft Visual C++ @@ -489,7 +479,7 @@ namespace csv { this->feed_buffer.push_back(std::move(buffer)); this->feed_cond.notify_one(); - buffer = std::make_unique(BUFFER_UPPER_LIMIT); // New pointer + buffer = std::unique_ptr(new char[BUFFER_UPPER_LIMIT]); // New pointer line_buffer = buffer.get(); } } @@ -532,7 +522,7 @@ namespace csv { bool CSVReader::read_row(CSVRow &row) { if (this->records.empty()) { if (!this->eof()) { - this->read_csv("", ITERATION_CHUNK_SIZE, false); + this->read_csv("", ITERATION_CHUNK_SIZE); } else return false; // Stop reading } diff --git a/include/internal/csv_reader.hpp b/include/internal/csv_reader.hpp index 46539713..ecae8311 100644 --- a/include/internal/csv_reader.hpp +++ b/include/internal/csv_reader.hpp @@ -12,17 +12,16 @@ #include "data_type.h" #include "csv_format.hpp" #include "csv_row.hpp" -#include "string_view.hpp" +#include "compatibility.hpp" namespace csv { /** @brief Integer indicating a requested column wasn't found. */ const int CSV_NOT_FOUND = -1; /** @namespace csv::internals - * @brief Stuff that is generally not of interest to end-users - */ + * @brief Stuff that is generally not of interest to end-users + */ namespace internals { - bool is_equal(double a, double b, double epsilon = 0.001); std::string type_name(const DataType& dtype); std::string format_row(const std::vector& row, const std::string& delim = ", "); @@ -32,7 +31,6 @@ namespace csv { size_t size() const; std::string* get(); std::string* operator->(); - void operator+=(const char); std::shared_ptr buffer = nullptr; size_t current_end = 0; @@ -125,7 +123,7 @@ namespace csv { std::vector get_col_names() const; int index_of(const std::string& col_name) const; ///@} - + /** @name CSV Metadata: Attributes */ ///@{ RowCount row_num = 0; /**< @brief How many lines have @@ -208,8 +206,7 @@ namespace csv { void feed(std::unique_ptr&&); /**< @brief Helper for read_csv_worker() */ void read_csv( const std::string& filename, - const size_t& bytes = ITERATION_CHUNK_SIZE, - bool close = true + const size_t& bytes = ITERATION_CHUNK_SIZE ); void read_csv_worker(); ///@} diff --git a/include/internal/csv_row.cpp b/include/internal/csv_row.cpp index 1dc9a170..6773b136 100644 --- a/include/internal/csv_row.cpp +++ b/include/internal/csv_row.cpp @@ -1,3 +1,4 @@ +#include #include #include "csv_row.hpp" @@ -151,11 +152,11 @@ namespace csv { } CSVRow::reverse_iterator CSVRow::rbegin() const { - return std::make_reverse_iterator(this->end()); + return std::reverse_iterator(this->end()); } CSVRow::reverse_iterator CSVRow::rend() const { - return std::make_reverse_iterator(this->begin()); + return std::reverse_iterator(this->begin()); } CSVRow::iterator::iterator(const CSVRow* _reader, int _i) diff --git a/include/internal/csv_row.hpp b/include/internal/csv_row.hpp index ed59f61d..98ec14ad 100644 --- a/include/internal/csv_row.hpp +++ b/include/internal/csv_row.hpp @@ -2,7 +2,7 @@ // Auxiliary data structures for CSV parser #include "data_type.h" -#include "string_view.hpp" +#include "compatibility.hpp" #include #include @@ -80,7 +80,7 @@ namespace csv { private: long double value = 0; - csv::string_view sv; + csv::string_view sv = ""; int _type = -1; void get_value(); }; @@ -160,6 +160,10 @@ namespace csv { bool operator==(const iterator&) const; bool operator!=(const iterator& other) const { return !operator==(other); } + #ifndef NDEBUG + friend CSVRow; + #endif + private: const CSVRow * daddy = nullptr; // Pointer to parent std::shared_ptr field = nullptr; // Current field pointed at diff --git a/include/internal/csv_stat.cpp b/include/internal/csv_stat.cpp index 5eefc6ca..f75592cc 100644 --- a/include/internal/csv_stat.cpp +++ b/include/internal/csv_stat.cpp @@ -13,7 +13,7 @@ namespace csv { * methods like get_mean(), get_counts(), etc... can be used to retrieve statistics. */ while (!this->eof()) { - this->read_csv("", ITERATION_CHUNK_SIZE, false); + this->read_csv("", ITERATION_CHUNK_SIZE); this->calc(); } diff --git a/include/internal/csv_utility.cpp b/include/internal/csv_utility.cpp index 7dc7202c..3989016c 100644 --- a/include/internal/csv_utility.cpp +++ b/include/internal/csv_utility.cpp @@ -1,7 +1,7 @@ #include #include "constants.hpp" -#include "csv_utility.h" +#include "csv_utility.hpp" #include "csv_reader.hpp" namespace csv { @@ -65,7 +65,11 @@ namespace csv { CSVFileInfo get_file_info(const std::string& filename) { CSVReader reader(filename); CSVFormat format = reader.get_format(); - for (auto& row : reader); + for (auto& row : reader) { + #ifndef NDEBUG + SUPPRESS_UNUSED_WARNING(row); + #endif + } CSVFileInfo info = { filename, diff --git a/include/internal/csv_utility.h b/include/internal/csv_utility.hpp similarity index 83% rename from include/internal/csv_utility.h rename to include/internal/csv_utility.hpp index 8a96b7e4..6b79f17c 100644 --- a/include/internal/csv_utility.h +++ b/include/internal/csv_utility.hpp @@ -32,4 +32,12 @@ namespace csv { int get_col_pos(const std::string filename, const std::string col_name, const CSVFormat format = GUESS_CSV); ///@} + + namespace internals { + template + inline bool is_equal(T a, T b, T epsilon = 0.001) { + /** Returns true if two doubles are about the same */ + return std::abs(a - b) < epsilon; + } + } } \ No newline at end of file diff --git a/include/internal/csv_writer.hpp b/include/internal/csv_writer.hpp index b34b1373..90f6d93a 100644 --- a/include/internal/csv_writer.hpp +++ b/include/internal/csv_writer.hpp @@ -75,7 +75,7 @@ namespace csv { */ for (size_t i = 0, ilen = record.size(); i < ilen; i++) { - out << csv_escape(record[i]); + out << csv_escape(record[i], quote_minimal); if (i + 1 != ilen) out << Delim; } diff --git a/include/internal/data_type.cpp b/include/internal/data_type.cpp index 891ee628..7aa1514d 100644 --- a/include/internal/data_type.cpp +++ b/include/internal/data_type.cpp @@ -1,5 +1,5 @@ #include "data_type.h" -#include "string_view.hpp" +#include "compatibility.hpp" namespace csv { namespace internals { diff --git a/include/internal/data_type.h b/include/internal/data_type.h index cea1d4c7..9a3474ca 100644 --- a/include/internal/data_type.h +++ b/include/internal/data_type.h @@ -3,7 +3,7 @@ #include #include -#include "string_view.hpp" +#include "compatibility.hpp" namespace csv { /** Enumerates the different CSV field types that are diff --git a/programs/csv_bench.cpp b/programs/csv_bench.cpp index 220be977..528d4dc2 100644 --- a/programs/csv_bench.cpp +++ b/programs/csv_bench.cpp @@ -1,8 +1,9 @@ // Calculate benchmarks for CSV parser -#include "csv_parser.hpp" +#include "csv.hpp" #include #include +#include int main(int argc, char** argv) { using namespace csv; diff --git a/programs/csv_info.cpp b/programs/csv_info.cpp index f367cf22..b34aef4b 100644 --- a/programs/csv_info.cpp +++ b/programs/csv_info.cpp @@ -1,4 +1,4 @@ -#include "csv_parser.hpp" +#include "csv.hpp" #include int main(int argc, char** argv) { diff --git a/tests/main.cpp b/tests/main.cpp index 063e8787..f505ad15 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -1,2 +1,6 @@ #define CATCH_CONFIG_MAIN + +// For Catch + MSVC +#define _SILENCE_CXX17_UNCAUGHT_EXCEPTION_DEPRECATION_WARNING + #include "catch.hpp" \ No newline at end of file diff --git a/tests/test_csv_row.cpp b/tests/test_csv_row.cpp index bf0963d9..11b60a49 100644 --- a/tests/test_csv_row.cpp +++ b/tests/test_csv_row.cpp @@ -42,7 +42,7 @@ TEST_CASE("CSVRow Test", "[test_csv_row]") { try { auto dne = row[4].get<>(); } - catch (std::runtime_error& err) { + catch (std::runtime_error&) { error_caught = true; } @@ -53,7 +53,7 @@ TEST_CASE("CSVRow Test", "[test_csv_row]") { try { row["Col5"].get<>(); } - catch (std::runtime_error& err) { + catch (std::runtime_error&) { error_caught = true; } diff --git a/tests/test_data_type.cpp b/tests/test_data_type.cpp index 3b1ec46b..3b1b3443 100644 --- a/tests/test_data_type.cpp +++ b/tests/test_data_type.cpp @@ -2,6 +2,8 @@ #include "csv.hpp" #include +#define INT_IS_LONG_INT + using namespace csv; using namespace csv::internals; @@ -46,25 +48,30 @@ TEST_CASE( "Recognize Floats Properly", "[dtype_float]" ) { long double out; REQUIRE(data_type(float_a, &out) == CSV_DOUBLE); - REQUIRE(is_equal(out, 3.14)); + REQUIRE(is_equal(out, 3.14L)); REQUIRE(data_type(float_b, &out) == CSV_DOUBLE); - REQUIRE(is_equal(out, -3.14)); + REQUIRE(is_equal(out, -3.14L)); REQUIRE(data_type(e, &out) == CSV_DOUBLE); - REQUIRE(is_equal(out, 2.71828)); + REQUIRE(is_equal(out, 2.71828L)); } TEST_CASE("Integer Overflow", "[int_overflow]") { - const long double _INT_MAX = (long double)std::numeric_limits::max(); - const long double _LONG_MAX = (long double)std::numeric_limits::max(); - const long double _LONG_LONG_MAX = (long double)std::numeric_limits::max(); + constexpr long double _INT_MAX = (long double)std::numeric_limits::max(); + constexpr long double _LONG_MAX = (long double)std::numeric_limits::max(); + constexpr long double _LONG_LONG_MAX = (long double)std::numeric_limits::max(); std::string s; long double out; s = std::to_string((long long)_INT_MAX + 1); + + #if __cplusplus == 201703L + if constexpr (_INT_MAX == _LONG_MAX) { + #else if (_INT_MAX == _LONG_MAX) { + #endif REQUIRE(data_type(s, &out) == CSV_LONG_LONG_INT); } else { @@ -78,7 +85,7 @@ TEST_CASE( "Recognize Sub-Unit Double Values", "[regression_double]" ) { std::string s("0.15"); long double out; REQUIRE(data_type(s, &out) == CSV_DOUBLE); - REQUIRE(is_equal(out, 0.15)); + REQUIRE(is_equal(out, 0.15L)); } TEST_CASE( "Recognize Double Values", "[regression_double2]" ) { @@ -86,7 +93,7 @@ TEST_CASE( "Recognize Double Values", "[regression_double2]" ) { long double out; std::string s; - for (double i = 0; i <= 2.0; i += 0.01) { + for (long double i = 0; i <= 2.0; i += 0.01) { s = std::to_string(i); REQUIRE(data_type(s, &out) == CSV_DOUBLE); REQUIRE(is_equal(out, i));