mirror of
https://github.com/red0124/ssp.git
synced 2025-01-23 13:05:20 +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 @@
|
|||||||
[![ubuntu-latest-icc](https://github.com/red0124/ssp/workflows/ubuntu-latest-icc-ci/badge.svg)](https://github.com/red0124/ssp/actions/workflows/ubuntu-latest-icc.yml)
|
[![ubuntu-latest-icc](https://github.com/red0124/ssp/workflows/ubuntu-latest-icc-ci/badge.svg)](https://github.com/red0124/ssp/actions/workflows/ubuntu-latest-icc.yml)
|
||||||
[![windows-msys2-gcc](https://github.com/red0124/ssp/workflows/win-msys2-gcc-ci/badge.svg)](https://github.com/red0124/ssp/actions/workflows/win-msys2-gcc.yml)
|
[![windows-msys2-gcc](https://github.com/red0124/ssp/workflows/win-msys2-gcc-ci/badge.svg)](https://github.com/red0124/ssp/actions/workflows/win-msys2-gcc.yml)
|
||||||
[![windows-msys2-clang](https://github.com/red0124/ssp/workflows/win-msys2-clang-ci/badge.svg)](https://github.com/red0124/ssp/actions/workflows/win-msys2-clang.yml)
|
[![windows-msys2-clang](https://github.com/red0124/ssp/workflows/win-msys2-clang-ci/badge.svg)](https://github.com/red0124/ssp/actions/workflows/win-msys2-clang.yml)
|
||||||
|
[![win-msvc-ci](https://github.com/red0124/ssp/workflows/win-msvc-ci/badge.svg)](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)
|
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
|
#pragma once
|
||||||
#include <cstdlib>
|
|
||||||
#include <cstring>
|
#include <string>
|
||||||
|
|
||||||
#ifdef CMAKE_GITHUB_CI
|
#ifdef CMAKE_GITHUB_CI
|
||||||
#include <doctest/doctest.h>
|
#include <doctest/doctest.h>
|
||||||
@ -9,40 +9,22 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct buffer {
|
struct buffer {
|
||||||
char* data_{nullptr};
|
std::string data_;
|
||||||
|
|
||||||
char* operator()(const char* data) {
|
char *operator()(const std::string &data) {
|
||||||
if (data_) {
|
data_ = data;
|
||||||
delete[] data_;
|
return data_.data();
|
||||||
}
|
|
||||||
data_ = new char[strlen(data) + 1];
|
|
||||||
strcpy(data_, data);
|
|
||||||
return data_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char* append(const char* data) {
|
char *append(const std::string &data) {
|
||||||
if (data_) {
|
data_ += data;
|
||||||
char* new_data_ = new char[strlen(data_) + strlen(data) + 1];
|
return data_.data();
|
||||||
strcpy(new_data_, data_);
|
|
||||||
strcat(new_data_, data);
|
|
||||||
delete[] data_;
|
|
||||||
data_ = new_data_;
|
|
||||||
return data_;
|
|
||||||
} else {
|
|
||||||
return operator()(data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char* append_overwrite_last(const char* data, size_t size) {
|
char *append_overwrite_last(const std::string &data, size_t size) {
|
||||||
data_[strlen(data_) - size] = '\0';
|
data_.resize(data_.size() - size);
|
||||||
return append(data);
|
return append(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
~buffer() {
|
|
||||||
if (data_) {
|
|
||||||
delete[] data_;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
[[maybe_unused]] inline buffer buff;
|
[[maybe_unused]] inline buffer buff;
|
||||||
|
@ -20,18 +20,15 @@ struct unique_file_name {
|
|||||||
const std::string name;
|
const std::string name;
|
||||||
|
|
||||||
unique_file_name()
|
unique_file_name()
|
||||||
: name{"random_" + std::to_string(i++) + time_now_rand() +
|
: name{"random_" + std::to_string(i++) + time_now_rand() + "_file.csv"} {}
|
||||||
"_file.csv"} {
|
|
||||||
}
|
|
||||||
|
|
||||||
~unique_file_name() {
|
~unique_file_name() { std::filesystem::remove(name); }
|
||||||
std::filesystem::remove(name);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void replace_all(std::string& s, const std::string& from,
|
void replace_all(std::string &s, const std::string &from,
|
||||||
const std::string& to) {
|
const std::string &to) {
|
||||||
if (from.empty()) return;
|
if (from.empty())
|
||||||
|
return;
|
||||||
size_t start_pos = 0;
|
size_t start_pos = 0;
|
||||||
while ((start_pos = s.find(from, start_pos)) != std::string::npos) {
|
while ((start_pos = s.find(from, start_pos)) != std::string::npos) {
|
||||||
s.replace(start_pos, from.length(), to);
|
s.replace(start_pos, from.length(), to);
|
||||||
@ -39,7 +36,7 @@ void replace_all(std::string& s, const std::string& from,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_if_crlf(std::string& s) {
|
void update_if_crlf(std::string &s) {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
replace_all(s, "\r\n", "\n");
|
replace_all(s, "\r\n", "\n");
|
||||||
#else
|
#else
|
||||||
@ -65,30 +62,28 @@ struct X {
|
|||||||
.append(delim)
|
.append(delim)
|
||||||
.append(s);
|
.append(s);
|
||||||
}
|
}
|
||||||
auto tied() const {
|
auto tied() const { return std::tie(i, d, s); }
|
||||||
return std::tie(i, d, s);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
std::enable_if_t<ss::has_m_tied_t<T>, bool> operator==(const T& lhs,
|
std::enable_if_t<ss::has_m_tied_t<T>, bool> operator==(const T &lhs,
|
||||||
const T& rhs) {
|
const T &rhs) {
|
||||||
return lhs.tied() == rhs.tied();
|
return lhs.tied() == rhs.tied();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static void make_and_write(const std::string& file_name,
|
static void make_and_write(const std::string &file_name,
|
||||||
const std::vector<T>& data,
|
const std::vector<T> &data,
|
||||||
const std::vector<std::string>& header = {}) {
|
const std::vector<std::string> &header = {}) {
|
||||||
std::ofstream out{file_name};
|
std::ofstream out{file_name};
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
std::vector<const char*> new_lines = {"\n"};
|
std::vector<const char *> new_lines = {"\n"};
|
||||||
#else
|
#else
|
||||||
std::vector<const char*> new_lines = {"\n", "\r\n"};
|
std::vector<const char *> new_lines = {"\n", "\r\n"};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
for (const auto& i : header) {
|
for (const auto &i : header) {
|
||||||
if (&i != &header.front()) {
|
if (&i != &header.front()) {
|
||||||
out << T::delim;
|
out << T::delim;
|
||||||
}
|
}
|
||||||
@ -125,7 +120,7 @@ TEST_CASE("parser test various cases") {
|
|||||||
i.emplace_back(ss::to_object<X>(a));
|
i.emplace_back(ss::to_object<X>(a));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& a : p2.iterate<int, double, std::string>()) {
|
for (const auto &a : p2.iterate<int, double, std::string>()) {
|
||||||
i2.emplace_back(ss::to_object<X>(a));
|
i2.emplace_back(ss::to_object<X>(a));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,7 +148,7 @@ TEST_CASE("parser test various cases") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
p2.ignore_next();
|
p2.ignore_next();
|
||||||
for (const auto& a : p2.iterate<tup>()) {
|
for (const auto &a : p2.iterate<tup>()) {
|
||||||
i2.emplace_back(ss::to_object<X>(a));
|
i2.emplace_back(ss::to_object<X>(a));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,7 +173,7 @@ TEST_CASE("parser test various cases") {
|
|||||||
i.push_back(p.get_object<X, int, double, std::string>());
|
i.push_back(p.get_object<X, int, double, std::string>());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto&& a : p2.iterate_object<X, int, double, std::string>()) {
|
for (auto &&a : p2.iterate_object<X, int, double, std::string>()) {
|
||||||
i2.push_back(std::move(a));
|
i2.push_back(std::move(a));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,7 +185,7 @@ TEST_CASE("parser test various cases") {
|
|||||||
ss::parser p{f.name, ","};
|
ss::parser p{f.name, ","};
|
||||||
std::vector<X> i;
|
std::vector<X> i;
|
||||||
|
|
||||||
for (auto&& a : p.iterate_object<X, int, double, std::string>()) {
|
for (auto &&a : p.iterate_object<X, int, double, std::string>()) {
|
||||||
i.push_back(std::move(a));
|
i.push_back(std::move(a));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,7 +218,7 @@ TEST_CASE("parser test various cases") {
|
|||||||
std::vector<X> i;
|
std::vector<X> i;
|
||||||
|
|
||||||
using tup = std::tuple<int, double, std::string>;
|
using tup = std::tuple<int, double, std::string>;
|
||||||
for (auto&& a : p.iterate_object<X, tup>()) {
|
for (auto &&a : p.iterate_object<X, tup>()) {
|
||||||
i.push_back(std::move(a));
|
i.push_back(std::move(a));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,7 +240,7 @@ TEST_CASE("parser test various cases") {
|
|||||||
ss::parser p{f.name, ","};
|
ss::parser p{f.name, ","};
|
||||||
std::vector<X> i;
|
std::vector<X> i;
|
||||||
|
|
||||||
for (auto&& a : p.iterate<X>()) {
|
for (auto &&a : p.iterate<X>()) {
|
||||||
i.push_back(std::move(a));
|
i.push_back(std::move(a));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,29 +256,28 @@ TEST_CASE("parser test various cases") {
|
|||||||
std::vector<X> i2;
|
std::vector<X> i2;
|
||||||
|
|
||||||
while (!p.eof()) {
|
while (!p.eof()) {
|
||||||
auto a =
|
auto a = p.get_object<X, ss::ax<int, excluded>, double, std::string>();
|
||||||
p.get_object<X, ss::ax<int, excluded>, double, std::string>();
|
|
||||||
if (p.valid()) {
|
if (p.valid()) {
|
||||||
i.push_back(a);
|
i.push_back(a);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto&& a : p2.iterate_object<X, ss::ax<int, excluded>, double,
|
for (auto &&a :
|
||||||
std::string>()) {
|
p2.iterate_object<X, ss::ax<int, excluded>, double, std::string>()) {
|
||||||
if (p2.valid()) {
|
if (p2.valid()) {
|
||||||
i2.push_back(std::move(a));
|
i2.push_back(std::move(a));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<X> expected;
|
std::vector<X> expected;
|
||||||
for (auto& x : data) {
|
for (auto &x : data) {
|
||||||
if (x.i != excluded) {
|
if (x.i != excluded) {
|
||||||
expected.push_back(x);
|
expected.push_back(x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::copy_if(data.begin(), data.end(), expected.begin(),
|
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(i, expected);
|
||||||
CHECK_EQ(i2, expected);
|
CHECK_EQ(i2, expected);
|
||||||
}
|
}
|
||||||
@ -302,7 +296,7 @@ TEST_CASE("parser test various cases") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto&& a :
|
for (auto &&a :
|
||||||
p2.iterate_object<X, ss::nx<int, 3>, double, std::string>()) {
|
p2.iterate_object<X, ss::nx<int, 3>, double, std::string>()) {
|
||||||
if (p2.valid()) {
|
if (p2.valid()) {
|
||||||
i2.push_back(std::move(a));
|
i2.push_back(std::move(a));
|
||||||
@ -330,7 +324,7 @@ TEST_CASE("parser test various cases") {
|
|||||||
i.push_back(p.get_next<X>());
|
i.push_back(p.get_next<X>());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto&& a : p2.iterate<X>()) {
|
for (auto &&a : p2.iterate<X>()) {
|
||||||
i2.push_back(std::move(a));
|
i2.push_back(std::move(a));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -344,20 +338,17 @@ struct test_struct {
|
|||||||
int i;
|
int i;
|
||||||
double d;
|
double d;
|
||||||
char c;
|
char c;
|
||||||
auto tied() {
|
auto tied() { return std::tie(i, d, c); }
|
||||||
return std::tie(i, d, c);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void expect_test_struct(const test_struct&) {
|
void expect_test_struct(const test_struct &) {}
|
||||||
}
|
|
||||||
|
|
||||||
// various scenarios
|
// various scenarios
|
||||||
TEST_CASE("parser test composite conversion") {
|
TEST_CASE("parser test composite conversion") {
|
||||||
unique_file_name f;
|
unique_file_name f;
|
||||||
{
|
{
|
||||||
std::ofstream out{f.name};
|
std::ofstream out{f.name};
|
||||||
for (auto& i :
|
for (auto &i :
|
||||||
{"10,a,11.1", "10,20,11.1", "junk", "10,11.1", "1,11.1,a", "junk",
|
{"10,a,11.1", "10,20,11.1", "junk", "10,11.1", "1,11.1,a", "junk",
|
||||||
"10,junk", "11,junk", "10,11.1,c", "10,20", "10,22.2,f"}) {
|
"10,junk", "11,junk", "10,11.1,c", "10,20", "10,22.2,f"}) {
|
||||||
out << i << std::endl;
|
out << i << std::endl;
|
||||||
@ -374,11 +365,11 @@ TEST_CASE("parser test composite conversion") {
|
|||||||
{
|
{
|
||||||
constexpr static auto expectedData = std::tuple{10, 'a', 11.1};
|
constexpr static auto expectedData = std::tuple{10, 'a', 11.1};
|
||||||
|
|
||||||
auto [d1, d2, d3, d4] =
|
auto [d1, d2, d3, d4] = p.try_next<int, int, double>(fail)
|
||||||
p.try_next<int, int, double>(fail)
|
|
||||||
.or_else<test_struct>(fail)
|
.or_else<test_struct>(fail)
|
||||||
.or_else<int, char, double>(
|
.or_else<int, char, double>([&](auto &&data) {
|
||||||
[](auto&& data) { CHECK_EQ(data, expectedData); })
|
CHECK_EQ(data, expectedData);
|
||||||
|
})
|
||||||
.on_error(fail)
|
.on_error(fail)
|
||||||
.or_else<test_tuple>(fail)
|
.or_else<test_tuple>(fail)
|
||||||
.values();
|
.values();
|
||||||
@ -396,7 +387,7 @@ TEST_CASE("parser test composite conversion") {
|
|||||||
constexpr static auto expectedData = std::tuple{10, 20, 11.1};
|
constexpr static auto expectedData = std::tuple{10, 20, 11.1};
|
||||||
|
|
||||||
auto [d1, d2, d3, d4] =
|
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);
|
CHECK_EQ(std::tie(i1, i2, d), expectedData);
|
||||||
})
|
})
|
||||||
.on_error(fail)
|
.on_error(fail)
|
||||||
@ -438,11 +429,10 @@ TEST_CASE("parser test composite conversion") {
|
|||||||
{
|
{
|
||||||
REQUIRE(!p.eof());
|
REQUIRE(!p.eof());
|
||||||
|
|
||||||
auto [d1, d2] =
|
auto [d1, d2] = p.try_next<int, double>([](auto &i, auto &d) {
|
||||||
p.try_next<int, double>([](auto& i, auto& d) {
|
|
||||||
REQUIRE_EQ(std::tie(i, d), std::tuple{10, 11.1});
|
REQUIRE_EQ(std::tie(i, d), std::tuple{10, 11.1});
|
||||||
})
|
})
|
||||||
.or_else<int, double>([](auto&, auto&) { FAIL(""); })
|
.or_else<int, double>([](auto &, auto &) { FAIL(""); })
|
||||||
.values();
|
.values();
|
||||||
|
|
||||||
REQUIRE(p.valid());
|
REQUIRE(p.valid());
|
||||||
@ -453,7 +443,7 @@ TEST_CASE("parser test composite conversion") {
|
|||||||
{
|
{
|
||||||
REQUIRE(!p.eof());
|
REQUIRE(!p.eof());
|
||||||
|
|
||||||
auto [d1, d2] = p.try_next<int, double>([](auto&, auto&) { FAIL(""); })
|
auto [d1, d2] = p.try_next<int, double>([](auto &, auto &) { FAIL(""); })
|
||||||
.or_else<test_struct>(expect_test_struct)
|
.or_else<test_struct>(expect_test_struct)
|
||||||
.values();
|
.values();
|
||||||
|
|
||||||
@ -466,8 +456,7 @@ TEST_CASE("parser test composite conversion") {
|
|||||||
{
|
{
|
||||||
REQUIRE(!p.eof());
|
REQUIRE(!p.eof());
|
||||||
|
|
||||||
auto [d1, d2, d3, d4, d5] =
|
auto [d1, d2, d3, d4, d5] = p.try_next<int, int, double>(fail)
|
||||||
p.try_next<int, int, double>(fail)
|
|
||||||
.or_object<test_struct, int, double, char>()
|
.or_object<test_struct, int, double, char>()
|
||||||
.or_else<test_struct>(expect_test_struct)
|
.or_else<test_struct>(expect_test_struct)
|
||||||
.or_else<test_tuple>(fail)
|
.or_else<test_tuple>(fail)
|
||||||
@ -530,7 +519,7 @@ TEST_CASE("parser test composite conversion") {
|
|||||||
|
|
||||||
auto [d1, d2, d3, d4] =
|
auto [d1, d2, d3, d4] =
|
||||||
p.try_next<int, int>([] { return false; })
|
p.try_next<int, int>([] { return false; })
|
||||||
.or_else<int, double>([](auto&) { return false; })
|
.or_else<int, double>([](auto &) { return false; })
|
||||||
.or_else<int, int>()
|
.or_else<int, int>()
|
||||||
.or_else<int, int>(fail)
|
.or_else<int, int>(fail)
|
||||||
.values();
|
.values();
|
||||||
@ -548,7 +537,7 @@ TEST_CASE("parser test composite conversion") {
|
|||||||
|
|
||||||
auto [d1, d2, d3, d4] =
|
auto [d1, d2, d3, d4] =
|
||||||
p.try_object<test_struct, int, double, char>([] { return false; })
|
p.try_object<test_struct, int, double, char>([] { return false; })
|
||||||
.or_else<int, double>([](auto&) { return false; })
|
.or_else<int, double>([](auto &) { return false; })
|
||||||
.or_object<test_struct, int, double, char>()
|
.or_object<test_struct, int, double, char>()
|
||||||
.or_else<int, int>(fail)
|
.or_else<int, int>(fail)
|
||||||
.values();
|
.values();
|
||||||
@ -564,35 +553,29 @@ TEST_CASE("parser test composite conversion") {
|
|||||||
CHECK(p.eof());
|
CHECK(p.eof());
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t move_called = 0;
|
|
||||||
|
|
||||||
struct my_string {
|
struct my_string {
|
||||||
char* data{nullptr};
|
char *data{nullptr};
|
||||||
|
|
||||||
my_string() = default;
|
my_string() = default;
|
||||||
|
|
||||||
~my_string() {
|
~my_string() { delete[] data; }
|
||||||
delete[] data;
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure no object is copied
|
// make sure no object is copied
|
||||||
my_string(const my_string&) = delete;
|
my_string(const my_string &) = delete;
|
||||||
my_string& operator=(const my_string&) = delete;
|
my_string &operator=(const my_string &) = delete;
|
||||||
|
|
||||||
my_string(my_string&& other) : data{other.data} {
|
my_string(my_string &&other) : data{other.data} {
|
||||||
move_called++;
|
|
||||||
other.data = nullptr;
|
other.data = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
my_string& operator=(my_string&& other) {
|
my_string &operator=(my_string &&other) {
|
||||||
move_called++;
|
|
||||||
data = other.data;
|
data = other.data;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline bool ss::extract(const char* begin, const char* end, my_string& s) {
|
inline bool ss::extract(const char *begin, const char *end, my_string &s) {
|
||||||
size_t size = end - begin;
|
size_t size = end - begin;
|
||||||
s.data = new char[size + 1];
|
s.data = new char[size + 1];
|
||||||
strncpy(s.data, begin, size);
|
strncpy(s.data, begin, size);
|
||||||
@ -604,65 +587,20 @@ struct xyz {
|
|||||||
my_string x;
|
my_string x;
|
||||||
my_string y;
|
my_string y;
|
||||||
my_string z;
|
my_string z;
|
||||||
auto tied() {
|
auto tied() { return std::tie(x, y, z); }
|
||||||
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") {
|
TEST_CASE("parser test the moving of parsed composite values") {
|
||||||
// to compile is enough
|
// to compile is enough
|
||||||
return;
|
return;
|
||||||
ss::parser p{"", ""};
|
ss::parser p{"", ""};
|
||||||
p.try_next<my_string, my_string, my_string>()
|
p.try_next<my_string, my_string, my_string>()
|
||||||
.or_else<my_string, my_string, my_string, my_string>([](auto&&) {})
|
.or_else<my_string, my_string, my_string, my_string>([](auto &&) {})
|
||||||
.or_else<my_string>([](auto&) {})
|
.or_else<my_string>([](auto &) {})
|
||||||
.or_else<xyz>([](auto&&) {})
|
.or_else<xyz>([](auto &&) {})
|
||||||
.or_object<xyz, my_string, my_string, my_string>([](auto&&) {})
|
.or_object<xyz, my_string, my_string, my_string>([](auto &&) {})
|
||||||
.or_else<std::tuple<my_string, my_string, my_string>>(
|
.or_else<std::tuple<my_string, my_string, my_string>>(
|
||||||
[](auto&, auto&, auto&) {});
|
[](auto &, auto &, auto &) {});
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("parser test error mode") {
|
TEST_CASE("parser test error mode") {
|
||||||
@ -681,7 +619,7 @@ TEST_CASE("parser test error mode") {
|
|||||||
CHECK_FALSE(p.error_msg().empty());
|
CHECK_FALSE(p.error_msg().empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string no_quote(const std::string& s) {
|
std::string no_quote(const std::string &s) {
|
||||||
if (!s.empty() && s[0] == '"') {
|
if (!s.empty() && s[0] == '"') {
|
||||||
return {std::next(begin(s)), std::prev(end(s))};
|
return {std::next(begin(s)), std::prev(end(s))};
|
||||||
}
|
}
|
||||||
@ -696,12 +634,12 @@ TEST_CASE("parser test csv on multiple lines with quotes") {
|
|||||||
{7, 8, "\"u\"\"\""},
|
{7, 8, "\"u\"\"\""},
|
||||||
{9, 10, "v"},
|
{9, 10, "v"},
|
||||||
{11, 12, "\"w\n\""}};
|
{11, 12, "\"w\n\""}};
|
||||||
for (auto& [_, __, s] : data) {
|
for (auto &[_, __, s] : data) {
|
||||||
update_if_crlf(s);
|
update_if_crlf(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
make_and_write(f.name, data);
|
make_and_write(f.name, data);
|
||||||
for (auto& [_, __, s] : data) {
|
for (auto &[_, __, s] : data) {
|
||||||
s = no_quote(s);
|
s = no_quote(s);
|
||||||
if (s[0] == 'u') {
|
if (s[0] == 'u') {
|
||||||
s = "u\"";
|
s = "u\"";
|
||||||
@ -716,7 +654,7 @@ TEST_CASE("parser test csv on multiple lines with quotes") {
|
|||||||
i.emplace_back(ss::to_object<X>(a));
|
i.emplace_back(ss::to_object<X>(a));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& [_, __, s] : i) {
|
for (auto &[_, __, s] : i) {
|
||||||
update_if_crlf(s);
|
update_if_crlf(s);
|
||||||
}
|
}
|
||||||
CHECK_EQ(i, data);
|
CHECK_EQ(i, data);
|
||||||
@ -728,25 +666,22 @@ TEST_CASE("parser test csv on multiple lines with quotes") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string no_escape(std::string& s) {
|
std::string no_escape(std::string &s) {
|
||||||
s.erase(std::remove(begin(s), end(s), '\\'), end(s));
|
s.erase(std::remove(begin(s), end(s), '\\'), end(s));
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("parser test csv on multiple lines with escapes") {
|
TEST_CASE("parser test csv on multiple lines with escapes") {
|
||||||
unique_file_name f;
|
unique_file_name f;
|
||||||
std::vector<X> data = {{1, 2, "x\\\nx\\\r\nx"},
|
std::vector<X> data = {
|
||||||
{5, 6, "z\\\nz\\\nz"},
|
{1, 2, "x\\\nx\\\r\nx"}, {5, 6, "z\\\nz\\\nz"}, {7, 8, "u"},
|
||||||
{7, 8, "u"},
|
{3, 4, "y\\\ny\\\ny"}, {9, 10, "v\\\\"}, {11, 12, "w\\\n"}};
|
||||||
{3, 4, "y\\\ny\\\ny"},
|
for (auto &[_, __, s] : data) {
|
||||||
{9, 10, "v\\\\"},
|
|
||||||
{11, 12, "w\\\n"}};
|
|
||||||
for (auto& [_, __, s] : data) {
|
|
||||||
update_if_crlf(s);
|
update_if_crlf(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
make_and_write(f.name, data);
|
make_and_write(f.name, data);
|
||||||
for (auto& [_, __, s] : data) {
|
for (auto &[_, __, s] : data) {
|
||||||
s = no_escape(s);
|
s = no_escape(s);
|
||||||
if (s == "v") {
|
if (s == "v") {
|
||||||
s = "v\\";
|
s = "v\\";
|
||||||
@ -761,7 +696,7 @@ TEST_CASE("parser test csv on multiple lines with escapes") {
|
|||||||
i.emplace_back(ss::to_object<X>(a));
|
i.emplace_back(ss::to_object<X>(a));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& [_, __, s] : i) {
|
for (auto &[_, __, s] : i) {
|
||||||
update_if_crlf(s);
|
update_if_crlf(s);
|
||||||
}
|
}
|
||||||
CHECK_EQ(i, data);
|
CHECK_EQ(i, data);
|
||||||
@ -807,7 +742,7 @@ TEST_CASE("parser test csv on multiple lines with quotes and escapes") {
|
|||||||
#endif
|
#endif
|
||||||
{9, 10, "just strings"}};
|
{9, 10, "just strings"}};
|
||||||
|
|
||||||
for (auto& [_, __, s] : i) {
|
for (auto &[_, __, s] : i) {
|
||||||
update_if_crlf(s);
|
update_if_crlf(s);
|
||||||
}
|
}
|
||||||
CHECK_EQ(i, data);
|
CHECK_EQ(i, data);
|
||||||
@ -833,8 +768,8 @@ TEST_CASE("parser test multiline restricted") {
|
|||||||
out << "19,20,just strings" << std::endl;
|
out << "19,20,just strings" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
ss::parser<ss::multiline_restricted<2>, ss::quote<'"'>, ss::escape<'\\'>>
|
ss::parser<ss::multiline_restricted<2>, ss::quote<'"'>, ss::escape<'\\'>> p{
|
||||||
p{f.name, ","};
|
f.name, ","};
|
||||||
std::vector<X> i;
|
std::vector<X> i;
|
||||||
|
|
||||||
while (!p.eof()) {
|
while (!p.eof()) {
|
||||||
@ -853,26 +788,23 @@ TEST_CASE("parser test multiline restricted") {
|
|||||||
{9, 10, "just\n\nstrings"},
|
{9, 10, "just\n\nstrings"},
|
||||||
{19, 20, "just strings"}};
|
{19, 20, "just strings"}};
|
||||||
|
|
||||||
for (auto& [_, __, s] : i) {
|
for (auto &[_, __, s] : i) {
|
||||||
update_if_crlf(s);
|
update_if_crlf(s);
|
||||||
}
|
}
|
||||||
CHECK_EQ(i, data);
|
CHECK_EQ(i, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename Tuple>
|
template <typename T, typename Tuple> struct has_type;
|
||||||
struct has_type;
|
|
||||||
|
|
||||||
template <typename T, typename... Us>
|
template <typename T, typename... Us>
|
||||||
struct has_type<T, std::tuple<Us...>>
|
struct has_type<T, std::tuple<Us...>>
|
||||||
: std::disjunction<std::is_same<T, Us>...> {};
|
: std::disjunction<std::is_same<T, Us>...> {};
|
||||||
|
|
||||||
void checkSize(size_t size1, size_t size2) {
|
void checkSize(size_t size1, size_t size2) { CHECK_EQ(size1, size2); }
|
||||||
CHECK_EQ(size1, size2);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename... Ts>
|
template <typename... Ts>
|
||||||
void testFields(const std::string file_name, const std::vector<X>& data,
|
void testFields(const std::string file_name, const std::vector<X> &data,
|
||||||
const std::vector<std::string>& fields) {
|
const std::vector<std::string> &fields) {
|
||||||
using CaseType = std::tuple<Ts...>;
|
using CaseType = std::tuple<Ts...>;
|
||||||
|
|
||||||
ss::parser p{file_name, ","};
|
ss::parser p{file_name, ","};
|
||||||
@ -880,7 +812,7 @@ void testFields(const std::string file_name, const std::vector<X>& data,
|
|||||||
p.use_fields(fields);
|
p.use_fields(fields);
|
||||||
std::vector<CaseType> i;
|
std::vector<CaseType> i;
|
||||||
|
|
||||||
for (const auto& a : p.iterate<CaseType>()) {
|
for (const auto &a : p.iterate<CaseType>()) {
|
||||||
i.push_back(a);
|
i.push_back(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -911,14 +843,14 @@ TEST_CASE("parser test various cases with header") {
|
|||||||
{7, 8, "u"}, {9, 10, "v"}, {11, 12, "w"}};
|
{7, 8, "u"}, {9, 10, "v"}, {11, 12, "w"}};
|
||||||
|
|
||||||
make_and_write(f.name, data, header);
|
make_and_write(f.name, data, header);
|
||||||
const auto& o = f.name;
|
const auto &o = f.name;
|
||||||
const auto& d = data;
|
const auto &d = data;
|
||||||
|
|
||||||
{
|
{
|
||||||
ss::parser<ss::string_error> p{f.name, ","};
|
ss::parser<ss::string_error> p{f.name, ","};
|
||||||
std::vector<X> i;
|
std::vector<X> i;
|
||||||
|
|
||||||
for (const auto& a : p.iterate<int, double, std::string>()) {
|
for (const auto &a : p.iterate<int, double, std::string>()) {
|
||||||
i.emplace_back(ss::to_object<X>(a));
|
i.emplace_back(ss::to_object<X>(a));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -930,7 +862,7 @@ TEST_CASE("parser test various cases with header") {
|
|||||||
std::vector<X> i;
|
std::vector<X> i;
|
||||||
|
|
||||||
p.ignore_next();
|
p.ignore_next();
|
||||||
for (const auto& a : p.iterate<int, double, std::string>()) {
|
for (const auto &a : p.iterate<int, double, std::string>()) {
|
||||||
i.emplace_back(ss::to_object<X>(a));
|
i.emplace_back(ss::to_object<X>(a));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -941,7 +873,7 @@ TEST_CASE("parser test various cases with header") {
|
|||||||
ss::parser<ss::ignore_header> p{f.name, ","};
|
ss::parser<ss::ignore_header> p{f.name, ","};
|
||||||
std::vector<X> i;
|
std::vector<X> i;
|
||||||
|
|
||||||
for (const auto& a : p.iterate<int, double, std::string>()) {
|
for (const auto &a : p.iterate<int, double, std::string>()) {
|
||||||
i.emplace_back(ss::to_object<X>(a));
|
i.emplace_back(ss::to_object<X>(a));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -996,8 +928,7 @@ TEST_CASE("parser test various cases with header") {
|
|||||||
p.use_fields(Str, Int, Dbl);
|
p.use_fields(Str, Int, Dbl);
|
||||||
|
|
||||||
{
|
{
|
||||||
auto [string_, int_, double_] =
|
auto [string_, int_, double_] = p.get_next<std::string, int, double>();
|
||||||
p.get_next<std::string, int, double>();
|
|
||||||
CHECK_EQ(double_, data[3].d);
|
CHECK_EQ(double_, data[3].d);
|
||||||
CHECK_EQ(int_, data[3].i);
|
CHECK_EQ(int_, data[3].i);
|
||||||
CHECK_EQ(string_, data[3].s);
|
CHECK_EQ(string_, data[3].s);
|
||||||
@ -1044,12 +975,12 @@ TEST_CASE("parser test various cases with header") {
|
|||||||
testFields<double, int, str>(o, d, {Dbl, Int, Str});
|
testFields<double, int, str>(o, d, {Dbl, Int, Str});
|
||||||
}
|
}
|
||||||
|
|
||||||
void testIgnoreEmpty(const std::vector<X>& data) {
|
void testIgnoreEmpty(const std::vector<X> &data) {
|
||||||
unique_file_name f;
|
unique_file_name f;
|
||||||
make_and_write(f.name, data);
|
make_and_write(f.name, data);
|
||||||
|
|
||||||
std::vector<X> expected;
|
std::vector<X> expected;
|
||||||
for (const auto& d : data) {
|
for (const auto &d : data) {
|
||||||
if (d.s != X::make_empty) {
|
if (d.s != X::make_empty) {
|
||||||
expected.push_back(d);
|
expected.push_back(d);
|
||||||
}
|
}
|
||||||
@ -1059,7 +990,7 @@ void testIgnoreEmpty(const std::vector<X>& data) {
|
|||||||
ss::parser<ss::string_error, ss::ignore_empty> p{f.name, ","};
|
ss::parser<ss::string_error, ss::ignore_empty> p{f.name, ","};
|
||||||
|
|
||||||
std::vector<X> i;
|
std::vector<X> i;
|
||||||
for (const auto& a : p.iterate<X>()) {
|
for (const auto &a : p.iterate<X>()) {
|
||||||
i.push_back(a);
|
i.push_back(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1070,7 +1001,7 @@ void testIgnoreEmpty(const std::vector<X>& data) {
|
|||||||
ss::parser<ss::string_error> p{f.name, ","};
|
ss::parser<ss::string_error> p{f.name, ","};
|
||||||
std::vector<X> i;
|
std::vector<X> i;
|
||||||
size_t n = 0;
|
size_t n = 0;
|
||||||
for (const auto& a : p.iterate<X>()) {
|
for (const auto &a : p.iterate<X>()) {
|
||||||
if (data.at(n).s == X::make_empty) {
|
if (data.at(n).s == X::make_empty) {
|
||||||
CHECK_FALSE(p.valid());
|
CHECK_FALSE(p.valid());
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user