mirror of
				https://github.com/red0124/ssp.git
				synced 2025-11-04 14:46:45 +01:00 
			
		
		
		
	Merge pull request #25 from red0124/feature/win_msvc_ci
Feature/win msvc ci
This commit is contained in:
		
						commit
						78c75abec7
					
				
							
								
								
									
										63
									
								
								.github/workflows/win-msvc.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								.github/workflows/win-msvc.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,63 @@
 | 
			
		||||
name: win-msvc-ci
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches:
 | 
			
		||||
      - master
 | 
			
		||||
      - feature/**
 | 
			
		||||
      - improvement/**
 | 
			
		||||
      - bugfix/**
 | 
			
		||||
 | 
			
		||||
  pull_request:
 | 
			
		||||
    branches:
 | 
			
		||||
      - master
 | 
			
		||||
      - feature/**
 | 
			
		||||
      - improvement/**
 | 
			
		||||
      - bugfix/**
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  ci:
 | 
			
		||||
    if: >-
 | 
			
		||||
      ! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
 | 
			
		||||
      ! contains(toJSON(github.event.commits.*.message), '[skip github]')
 | 
			
		||||
 | 
			
		||||
    defaults:
 | 
			
		||||
      run:
 | 
			
		||||
        shell: bash
 | 
			
		||||
 | 
			
		||||
    runs-on: ${{ matrix.config.os }}
 | 
			
		||||
 | 
			
		||||
    strategy:
 | 
			
		||||
      fail-fast: false
 | 
			
		||||
      matrix:
 | 
			
		||||
        config:
 | 
			
		||||
          - os: windows-2019
 | 
			
		||||
            vs: "Visual Studio 16 2019"
 | 
			
		||||
 | 
			
		||||
          - os: windows-latest
 | 
			
		||||
            vs: "Visual Studio 17 2022"
 | 
			
		||||
 | 
			
		||||
        build: [Debug, Release]
 | 
			
		||||
        platform: [Win32, x64]
 | 
			
		||||
 | 
			
		||||
    name: "${{matrix.config.vs}}:${{matrix.platform}}:${{matrix.build}}"
 | 
			
		||||
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: checkout
 | 
			
		||||
        uses: actions/checkout@v2
 | 
			
		||||
 | 
			
		||||
      - name: Install dependencies
 | 
			
		||||
        run: script/ci_install_deps.sh
 | 
			
		||||
 | 
			
		||||
      - name: Configure
 | 
			
		||||
        run: >-
 | 
			
		||||
          cmake -S test -B build -D CMAKE_BUILD_TYPE=${{matrix.build}}
 | 
			
		||||
          -G "${{matrix.config.vs}}" -A ${{matrix.platform}}
 | 
			
		||||
 | 
			
		||||
      - name: Build
 | 
			
		||||
        run: cmake --build build -j ${{steps.cores.outputs.count}}
 | 
			
		||||
 | 
			
		||||
      - name: Run
 | 
			
		||||
        working-directory: build
 | 
			
		||||
        run: >-
 | 
			
		||||
          ctest -C Debug --output-on-failure -j ${{steps.cores.outputs.count}}
 | 
			
		||||
@ -13,6 +13,7 @@
 | 
			
		||||
[](https://github.com/red0124/ssp/actions/workflows/ubuntu-latest-icc.yml)
 | 
			
		||||
[](https://github.com/red0124/ssp/actions/workflows/win-msys2-gcc.yml)
 | 
			
		||||
[](https://github.com/red0124/ssp/actions/workflows/win-msys2-clang.yml)
 | 
			
		||||
[](https://github.com/red0124/ssp/actions/workflows/win-msvc.yml)
 | 
			
		||||
 | 
			
		||||
A header only "csv" parser which is fast and versatile with modern C++ api. Requires compiler with C++17 support. [Can also be used to convert strings to specific types.](#The-converter)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
#include <cstdlib>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
#ifdef CMAKE_GITHUB_CI
 | 
			
		||||
#include <doctest/doctest.h>
 | 
			
		||||
@ -9,40 +9,22 @@
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
struct buffer {
 | 
			
		||||
    char* data_{nullptr};
 | 
			
		||||
  std::string data_;
 | 
			
		||||
 | 
			
		||||
    char* operator()(const char* data) {
 | 
			
		||||
        if (data_) {
 | 
			
		||||
            delete[] data_;
 | 
			
		||||
        }
 | 
			
		||||
        data_ = new char[strlen(data) + 1];
 | 
			
		||||
        strcpy(data_, data);
 | 
			
		||||
        return data_;
 | 
			
		||||
  char *operator()(const std::string &data) {
 | 
			
		||||
    data_ = data;
 | 
			
		||||
    return data_.data();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    char* append(const char* data) {
 | 
			
		||||
        if (data_) {
 | 
			
		||||
            char* new_data_ = new char[strlen(data_) + strlen(data) + 1];
 | 
			
		||||
            strcpy(new_data_, data_);
 | 
			
		||||
            strcat(new_data_, data);
 | 
			
		||||
            delete[] data_;
 | 
			
		||||
            data_ = new_data_;
 | 
			
		||||
            return data_;
 | 
			
		||||
        } else {
 | 
			
		||||
            return operator()(data);
 | 
			
		||||
        }
 | 
			
		||||
  char *append(const std::string &data) {
 | 
			
		||||
    data_ += data;
 | 
			
		||||
    return data_.data();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    char* append_overwrite_last(const char* data, size_t size) {
 | 
			
		||||
        data_[strlen(data_) - size] = '\0';
 | 
			
		||||
  char *append_overwrite_last(const std::string &data, size_t size) {
 | 
			
		||||
    data_.resize(data_.size() - size);
 | 
			
		||||
    return append(data);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    ~buffer() {
 | 
			
		||||
        if (data_) {
 | 
			
		||||
            delete[] data_;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
[[maybe_unused]] inline buffer buff;
 | 
			
		||||
 | 
			
		||||
@ -20,18 +20,15 @@ struct unique_file_name {
 | 
			
		||||
  const std::string name;
 | 
			
		||||
 | 
			
		||||
  unique_file_name()
 | 
			
		||||
        : name{"random_" + std::to_string(i++) + time_now_rand() +
 | 
			
		||||
               "_file.csv"} {
 | 
			
		||||
    }
 | 
			
		||||
      : name{"random_" + std::to_string(i++) + time_now_rand() + "_file.csv"} {}
 | 
			
		||||
 | 
			
		||||
    ~unique_file_name() {
 | 
			
		||||
        std::filesystem::remove(name);
 | 
			
		||||
    }
 | 
			
		||||
  ~unique_file_name() { std::filesystem::remove(name); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void replace_all(std::string &s, const std::string &from,
 | 
			
		||||
                 const std::string &to) {
 | 
			
		||||
    if (from.empty()) return;
 | 
			
		||||
  if (from.empty())
 | 
			
		||||
    return;
 | 
			
		||||
  size_t start_pos = 0;
 | 
			
		||||
  while ((start_pos = s.find(from, start_pos)) != std::string::npos) {
 | 
			
		||||
    s.replace(start_pos, from.length(), to);
 | 
			
		||||
@ -65,9 +62,7 @@ struct X {
 | 
			
		||||
        .append(delim)
 | 
			
		||||
        .append(s);
 | 
			
		||||
  }
 | 
			
		||||
    auto tied() const {
 | 
			
		||||
        return std::tie(i, d, s);
 | 
			
		||||
    }
 | 
			
		||||
  auto tied() const { return std::tie(i, d, s); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
@ -261,15 +256,14 @@ TEST_CASE("parser test various cases") {
 | 
			
		||||
    std::vector<X> i2;
 | 
			
		||||
 | 
			
		||||
    while (!p.eof()) {
 | 
			
		||||
            auto a =
 | 
			
		||||
                p.get_object<X, ss::ax<int, excluded>, double, std::string>();
 | 
			
		||||
      auto a = p.get_object<X, ss::ax<int, excluded>, double, std::string>();
 | 
			
		||||
      if (p.valid()) {
 | 
			
		||||
        i.push_back(a);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        for (auto&& a : p2.iterate_object<X, ss::ax<int, excluded>, double,
 | 
			
		||||
                                          std::string>()) {
 | 
			
		||||
    for (auto &&a :
 | 
			
		||||
         p2.iterate_object<X, ss::ax<int, excluded>, double, std::string>()) {
 | 
			
		||||
      if (p2.valid()) {
 | 
			
		||||
        i2.push_back(std::move(a));
 | 
			
		||||
      }
 | 
			
		||||
@ -283,7 +277,7 @@ TEST_CASE("parser test various cases") {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::copy_if(data.begin(), data.end(), expected.begin(),
 | 
			
		||||
                     [](const X& x) { return x.i != excluded; });
 | 
			
		||||
                 [&](const X &x) { return x.i != excluded; });
 | 
			
		||||
    CHECK_EQ(i, expected);
 | 
			
		||||
    CHECK_EQ(i2, expected);
 | 
			
		||||
  }
 | 
			
		||||
@ -344,13 +338,10 @@ struct test_struct {
 | 
			
		||||
  int i;
 | 
			
		||||
  double d;
 | 
			
		||||
  char c;
 | 
			
		||||
    auto tied() {
 | 
			
		||||
        return std::tie(i, d, c);
 | 
			
		||||
    }
 | 
			
		||||
  auto tied() { return std::tie(i, d, c); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void expect_test_struct(const test_struct&) {
 | 
			
		||||
}
 | 
			
		||||
void expect_test_struct(const test_struct &) {}
 | 
			
		||||
 | 
			
		||||
// various scenarios
 | 
			
		||||
TEST_CASE("parser test composite conversion") {
 | 
			
		||||
@ -374,11 +365,11 @@ TEST_CASE("parser test composite conversion") {
 | 
			
		||||
  {
 | 
			
		||||
    constexpr static auto expectedData = std::tuple{10, 'a', 11.1};
 | 
			
		||||
 | 
			
		||||
        auto [d1, d2, d3, d4] =
 | 
			
		||||
            p.try_next<int, int, double>(fail)
 | 
			
		||||
    auto [d1, d2, d3, d4] = p.try_next<int, int, double>(fail)
 | 
			
		||||
                                .or_else<test_struct>(fail)
 | 
			
		||||
                .or_else<int, char, double>(
 | 
			
		||||
                    [](auto&& data) { CHECK_EQ(data, expectedData); })
 | 
			
		||||
                                .or_else<int, char, double>([&](auto &&data) {
 | 
			
		||||
                                  CHECK_EQ(data, expectedData);
 | 
			
		||||
                                })
 | 
			
		||||
                                .on_error(fail)
 | 
			
		||||
                                .or_else<test_tuple>(fail)
 | 
			
		||||
                                .values();
 | 
			
		||||
@ -396,7 +387,7 @@ TEST_CASE("parser test composite conversion") {
 | 
			
		||||
    constexpr static auto expectedData = std::tuple{10, 20, 11.1};
 | 
			
		||||
 | 
			
		||||
    auto [d1, d2, d3, d4] =
 | 
			
		||||
            p.try_next<int, int, double>([](auto& i1, auto i2, double d) {
 | 
			
		||||
        p.try_next<int, int, double>([&](auto &i1, auto i2, double d) {
 | 
			
		||||
           CHECK_EQ(std::tie(i1, i2, d), expectedData);
 | 
			
		||||
         })
 | 
			
		||||
            .on_error(fail)
 | 
			
		||||
@ -438,8 +429,7 @@ TEST_CASE("parser test composite conversion") {
 | 
			
		||||
  {
 | 
			
		||||
    REQUIRE(!p.eof());
 | 
			
		||||
 | 
			
		||||
        auto [d1, d2] =
 | 
			
		||||
            p.try_next<int, double>([](auto& i, auto& d) {
 | 
			
		||||
    auto [d1, d2] = p.try_next<int, double>([](auto &i, auto &d) {
 | 
			
		||||
                       REQUIRE_EQ(std::tie(i, d), std::tuple{10, 11.1});
 | 
			
		||||
                     })
 | 
			
		||||
                        .or_else<int, double>([](auto &, auto &) { FAIL(""); })
 | 
			
		||||
@ -466,8 +456,7 @@ TEST_CASE("parser test composite conversion") {
 | 
			
		||||
  {
 | 
			
		||||
    REQUIRE(!p.eof());
 | 
			
		||||
 | 
			
		||||
        auto [d1, d2, d3, d4, d5] =
 | 
			
		||||
            p.try_next<int, int, double>(fail)
 | 
			
		||||
    auto [d1, d2, d3, d4, d5] = p.try_next<int, int, double>(fail)
 | 
			
		||||
                                    .or_object<test_struct, int, double, char>()
 | 
			
		||||
                                    .or_else<test_struct>(expect_test_struct)
 | 
			
		||||
                                    .or_else<test_tuple>(fail)
 | 
			
		||||
@ -564,28 +553,22 @@ TEST_CASE("parser test composite conversion") {
 | 
			
		||||
  CHECK(p.eof());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t move_called = 0;
 | 
			
		||||
 | 
			
		||||
struct my_string {
 | 
			
		||||
  char *data{nullptr};
 | 
			
		||||
 | 
			
		||||
  my_string() = default;
 | 
			
		||||
 | 
			
		||||
    ~my_string() {
 | 
			
		||||
        delete[] data;
 | 
			
		||||
    }
 | 
			
		||||
  ~my_string() { delete[] data; }
 | 
			
		||||
 | 
			
		||||
  // make sure no object is copied
 | 
			
		||||
  my_string(const my_string &) = delete;
 | 
			
		||||
  my_string &operator=(const my_string &) = delete;
 | 
			
		||||
 | 
			
		||||
  my_string(my_string &&other) : data{other.data} {
 | 
			
		||||
        move_called++;
 | 
			
		||||
    other.data = nullptr;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  my_string &operator=(my_string &&other) {
 | 
			
		||||
        move_called++;
 | 
			
		||||
    data = other.data;
 | 
			
		||||
    return *this;
 | 
			
		||||
  }
 | 
			
		||||
@ -604,54 +587,9 @@ struct xyz {
 | 
			
		||||
  my_string x;
 | 
			
		||||
  my_string y;
 | 
			
		||||
  my_string z;
 | 
			
		||||
    auto tied() {
 | 
			
		||||
        return std::tie(x, y, z);
 | 
			
		||||
    }
 | 
			
		||||
  auto tied() { return std::tie(x, y, z); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
TEST_CASE("parser test the moving of parsed values") {
 | 
			
		||||
    {
 | 
			
		||||
        unique_file_name f;
 | 
			
		||||
        {
 | 
			
		||||
            std::ofstream out{f.name};
 | 
			
		||||
            out << "x" << std::endl;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ss::parser p{f.name, ","};
 | 
			
		||||
        auto x = p.get_next<my_string>();
 | 
			
		||||
        CHECK_LE(move_called, 1);
 | 
			
		||||
        move_called = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    unique_file_name f;
 | 
			
		||||
    {
 | 
			
		||||
        std::ofstream out{f.name};
 | 
			
		||||
        out << "a,b,c" << std::endl;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        ss::parser p{f.name, ","};
 | 
			
		||||
        auto x = p.get_next<my_string, my_string, my_string>();
 | 
			
		||||
        CHECK_LE(move_called, 3);
 | 
			
		||||
        move_called = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        ss::parser p{f.name, ","};
 | 
			
		||||
        auto x = p.get_object<xyz, my_string, my_string, my_string>();
 | 
			
		||||
        CHECK_LE(move_called, 6);
 | 
			
		||||
        move_called = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        ss::parser p{f.name, ","};
 | 
			
		||||
        auto x = p.get_next<xyz>();
 | 
			
		||||
        CHECK_LE(move_called, 6);
 | 
			
		||||
        move_called = 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_CASE("parser test the moving of parsed composite values") {
 | 
			
		||||
  // to compile is enough
 | 
			
		||||
  return;
 | 
			
		||||
@ -735,12 +673,9 @@ std::string no_escape(std::string& s) {
 | 
			
		||||
 | 
			
		||||
TEST_CASE("parser test csv on multiple lines with escapes") {
 | 
			
		||||
  unique_file_name f;
 | 
			
		||||
    std::vector<X> data = {{1, 2, "x\\\nx\\\r\nx"},
 | 
			
		||||
                           {5, 6, "z\\\nz\\\nz"},
 | 
			
		||||
                           {7, 8, "u"},
 | 
			
		||||
                           {3, 4, "y\\\ny\\\ny"},
 | 
			
		||||
                           {9, 10, "v\\\\"},
 | 
			
		||||
                           {11, 12, "w\\\n"}};
 | 
			
		||||
  std::vector<X> data = {
 | 
			
		||||
      {1, 2, "x\\\nx\\\r\nx"}, {5, 6, "z\\\nz\\\nz"}, {7, 8, "u"},
 | 
			
		||||
      {3, 4, "y\\\ny\\\ny"},   {9, 10, "v\\\\"},      {11, 12, "w\\\n"}};
 | 
			
		||||
  for (auto &[_, __, s] : data) {
 | 
			
		||||
    update_if_crlf(s);
 | 
			
		||||
  }
 | 
			
		||||
@ -833,8 +768,8 @@ TEST_CASE("parser test multiline restricted") {
 | 
			
		||||
    out << "19,20,just strings" << std::endl;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    ss::parser<ss::multiline_restricted<2>, ss::quote<'"'>, ss::escape<'\\'>>
 | 
			
		||||
        p{f.name, ","};
 | 
			
		||||
  ss::parser<ss::multiline_restricted<2>, ss::quote<'"'>, ss::escape<'\\'>> p{
 | 
			
		||||
      f.name, ","};
 | 
			
		||||
  std::vector<X> i;
 | 
			
		||||
 | 
			
		||||
  while (!p.eof()) {
 | 
			
		||||
@ -859,16 +794,13 @@ TEST_CASE("parser test multiline restricted") {
 | 
			
		||||
  CHECK_EQ(i, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T, typename Tuple>
 | 
			
		||||
struct has_type;
 | 
			
		||||
template <typename T, typename Tuple> struct has_type;
 | 
			
		||||
 | 
			
		||||
template <typename T, typename... Us>
 | 
			
		||||
struct has_type<T, std::tuple<Us...>>
 | 
			
		||||
    : std::disjunction<std::is_same<T, Us>...> {};
 | 
			
		||||
 | 
			
		||||
void checkSize(size_t size1, size_t size2) {
 | 
			
		||||
    CHECK_EQ(size1, size2);
 | 
			
		||||
}
 | 
			
		||||
void checkSize(size_t size1, size_t size2) { CHECK_EQ(size1, size2); }
 | 
			
		||||
 | 
			
		||||
template <typename... Ts>
 | 
			
		||||
void testFields(const std::string file_name, const std::vector<X> &data,
 | 
			
		||||
@ -996,8 +928,7 @@ TEST_CASE("parser test various cases with header") {
 | 
			
		||||
    p.use_fields(Str, Int, Dbl);
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
            auto [string_, int_, double_] =
 | 
			
		||||
                p.get_next<std::string, int, double>();
 | 
			
		||||
      auto [string_, int_, double_] = p.get_next<std::string, int, double>();
 | 
			
		||||
      CHECK_EQ(double_, data[3].d);
 | 
			
		||||
      CHECK_EQ(int_, data[3].i);
 | 
			
		||||
      CHECK_EQ(string_, data[3].s);
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user