merge with master

This commit is contained in:
ado 2021-01-21 01:54:20 +01:00
commit bea7775118
18 changed files with 330 additions and 6306 deletions

101
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,101 @@
name: Continuous Integration
on:
push:
branches:
- master
- /^feature/$/
pull_request:
branches:
- master
- /^feature/$/
jobs:
clang_tests:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
strategy:
matrix:
version: [12, 11, 10, 9, 8, 7]
runs-on: ubuntu-latest
name: Clang ${{ matrix.version }}
container:
image: teeks99/clang-ubuntu:${{ matrix.version }}
options: -v /usr/local:/host_usr_local
env:
CC: clang-${{ matrix.version }}
CXX: clang++-${{ matrix.version }}
CXXFLAGS: -stdlib=libc++
steps:
- uses: actions/checkout@v1
- uses: friendlyanon/fetch-core-count@v1
id: cores
- name: CMake
run: echo "/host_usr_local/bin" >> $GITHUB_PATH
- name: Install dependencies
run: |
apt-get update
apt-get install -y git
script/ci_install_deps.sh
- name: Configure
run: cmake -S test -B build -D CMAKE_BUILD_TYPE=Debug
- name: Build
run: cmake --build build -j ${{ steps.cores.outputs.count }}
- name: Run
working-directory: build
run: ctest --output-on-failure -j ${{ steps.cores.outputs.count }}
gcc_tests:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
strategy:
matrix:
version: [10, 9, 8]
runs-on: ubuntu-latest
name: GCC ${{ matrix.version }}
container:
image: gcc:${{ matrix.version }}
options: -v /usr/local:/host_usr_local
steps:
- uses: actions/checkout@v1
- uses: friendlyanon/fetch-core-count@v1
id: cores
- name: CMake
run: echo "/host_usr_local/bin" >> $GITHUB_PATH
- name: Install dependencies
run: script/ci_install_deps.sh
- name: Configure
run: cmake -S test -B build -D CMAKE_BUILD_TYPE=Debug
- name: Build
run: cmake --build build -j ${{ steps.cores.outputs.count }}
- name: Run
working-directory: build
run: ctest --output-on-failure -j ${{ steps.cores.outputs.count }}

2
.gitignore vendored
View File

@ -2,3 +2,5 @@ compile_commands.json
.clang-format .clang-format
experiment/ experiment/
build/ build/
subprojects/*
!subprojects/*.wrap

84
CMakeLists.txt Normal file
View File

@ -0,0 +1,84 @@
cmake_minimum_required(VERSION 3.14)
project(
ssp
VERSION 0.0.1
DESCRIPTION "Static split parser"
HOMEPAGE_URL "https://github.com/red0124/ssp"
LANGUAGES CXX
)
# ---- Warning guard ----
# Protect dependents from this project's warnings if the guard isn't disabled
set(ssp_warning_guard SYSTEM)
if(ssp_INCLUDE_WITHOUT_SYSTEM)
set(ssp_warning_guard "")
endif()
# ---- Declare library ----
add_library(ssp INTERFACE)
add_library(ssp::ssp ALIAS ssp)
target_include_directories(
ssp
${ssp_warning_guard}
INTERFACE
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>"
)
target_compile_features(ssp INTERFACE cxx_std_17)
target_link_libraries(
ssp
INTERFACE
"$<$<AND:$<CXX_COMPILER_ID:AppleClang,Clang>,$<VERSION_LESS:$<CXX_COMPILER_VERSION>,9.0>>:c++fs>"
"$<$<AND:$<CXX_COMPILER_ID:GNU>,$<VERSION_LESS:$<CXX_COMPILER_VERSION>,9.1>>:stdc++fs>"
)
# ---- Install ----
include(CMakePackageConfigHelpers)
include(GNUInstallDirs)
set(ssp_directory "ssp-${PROJECT_VERSION}")
set(ssp_include_directory "${CMAKE_INSTALL_INCLUDEDIR}")
install(
DIRECTORY "${PROJECT_SOURCE_DIR}/include/"
DESTINATION "${ssp_include_directory}"
COMPONENT ssp_Development
)
install(
TARGETS ssp
EXPORT sspTargets
INCLUDES DESTINATION "${ssp_include_directory}"
)
write_basic_package_version_file(
ssp-config-version.cmake
COMPATIBILITY SameMajorVersion
ARCH_INDEPENDENT
)
set(ssp_install_cmakedir "${CMAKE_INSTALL_LIBDIR}/cmake/${ssp_directory}")
install(
FILES "${PROJECT_BINARY_DIR}/ssp-config-version.cmake"
DESTINATION "${ssp_install_cmakedir}"
COMPONENT ssp_Development
)
install(
EXPORT sspTargets
FILE ssp-config.cmake
NAMESPACE ssp::
DESTINATION "${ssp_install_cmakedir}"
COMPONENT ssp_Development
)
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
include(CPack)
endif()

View File

@ -63,14 +63,9 @@ Bill (Heath) Gates 65 3.3
``` ```
$ git clone https://github.com/red0124/ssp $ git clone https://github.com/red0124/ssp
$ cd ssp $ cd ssp
$ cmake --configure .
$ sudo make install $ sudo make install
``` ```
Run tests (optional):
```
$ make test
```
# Usage # Usage
## Conversions ## Conversions

View File

@ -9,9 +9,9 @@
#include <vector> #include <vector>
namespace ss { namespace ss {
INIT_HAS_METHOD(tied); INIT_HAS_METHOD(tied)
INIT_HAS_METHOD(ss_valid); INIT_HAS_METHOD(ss_valid)
INIT_HAS_METHOD(error); INIT_HAS_METHOD(error)
//////////////// ////////////////
// replace validator // replace validator
@ -151,7 +151,7 @@ public:
no_void_validator_tup_t<T, Ts...> convert(const split_input& elems) { no_void_validator_tup_t<T, Ts...> convert(const split_input& elems) {
if constexpr (sizeof...(Ts) == 0 && if constexpr (sizeof...(Ts) == 0 &&
is_instance_of<T, std::tuple>::value) { is_instance_of<T, std::tuple>::value) {
return convert_impl(elems, (T*){}); return convert_impl(elems, static_cast<T*>(nullptr));
} else if constexpr (tied_class_v<T, Ts...>) { } else if constexpr (tied_class_v<T, Ts...>) {
using arg_ref_tuple = using arg_ref_tuple =
@ -160,7 +160,8 @@ public:
using arg_tuple = using arg_tuple =
typename apply_trait<std::decay, arg_ref_tuple>::type; typename apply_trait<std::decay, arg_ref_tuple>::type;
return to_object<T>(convert_impl(elems, (arg_tuple*){})); return to_object<T>(
convert_impl(elems, static_cast<arg_tuple*>(nullptr)));
} else { } else {
return convert_impl<T, Ts...>(elems); return convert_impl<T, Ts...>(elems);
} }
@ -177,13 +178,9 @@ public:
: bool_error_ == false; : bool_error_ == false;
} }
const std::string& error_msg() const { const std::string& error_msg() const { return string_error_; }
return string_error_;
}
void set_error_mode(error_mode mode) { void set_error_mode(error_mode mode) { error_mode_ = mode; }
error_mode_ = mode;
}
// 'splits' string by given delimiter, returns vector of pairs which // 'splits' string by given delimiter, returns vector of pairs which
// contain the beginnings and the ends of each column of the string // contain the beginnings and the ends of each column of the string
@ -355,7 +352,7 @@ private:
no_void_validator_tup_t<Ts...> ret; no_void_validator_tup_t<Ts...> ret;
extract_multiple<0, 0, Ts...>(ret, elems); extract_multiple<0, 0, Ts...>(ret, elems);
return ret; return ret;
}; }
//////////////// ////////////////
// members // members

View File

@ -179,7 +179,7 @@ public:
using Ret = no_void_validator_tup_t<Ts...>; using Ret = no_void_validator_tup_t<Ts...>;
return try_invoke_and_make_composite< return try_invoke_and_make_composite<
std::optional<Ret>>(get_next<Ts...>(), std::forward<Fun>(fun)); std::optional<Ret>>(get_next<Ts...>(), std::forward<Fun>(fun));
}; }
// identical to try_next but returns composite with object instead of a // identical to try_next but returns composite with object instead of a
// tuple // tuple
@ -187,7 +187,7 @@ public:
composite<std::optional<T>> try_object(Fun&& fun = none{}) { composite<std::optional<T>> try_object(Fun&& fun = none{}) {
return try_invoke_and_make_composite< return try_invoke_and_make_composite<
std::optional<T>>(get_object<T, Ts...>(), std::forward<Fun>(fun)); std::optional<T>>(get_object<T, Ts...>(), std::forward<Fun>(fun));
}; }
private: private:
// tries to invoke the given function (see below), if the function // tries to invoke the given function (see below), if the function

View File

@ -1,10 +0,0 @@
install:
cp -r ./include/ss/ /usr/local/include/
uninstall:
rm -rf /usr/local/include/ss/*.hpp
test:
cd test && $(MAKE) -j4 && $(MAKE) test && $(MAKE) clean
.PHONY: install test

8
meson.build Normal file
View File

@ -0,0 +1,8 @@
project('ssp', 'cpp',
default_options :
['warning_level=3',
'cpp_std=c++17',
'buildtype=debug'])
includes = include_directories('include')
subdir('test')

17
script/ci_install_deps.sh Executable file
View File

@ -0,0 +1,17 @@
#!/bin/bash
JOBS=4
BUILD_TYPE=Debug
set -eux
git clone https://github.com/onqtam/doctest -b 2.4.4 --depth 1
cmake -S doctest -B doctest/build \
-D CMAKE_BUILD_TYPE=${BUILD_TYPE} \
-D DOCTEST_WITH_MAIN_IN_STATIC_LIB=NO \
-D DOCTEST_WITH_TESTS=NO
cmake --build doctest/build --config ${BUILD_TYPE} --target install -j ${JOBS}
rm -rf doctest

3
subprojects/doctest.wrap Normal file
View File

@ -0,0 +1,3 @@
[wrap-git]
url = https://github.com/onqtam/doctest
revision = 2.4.4

37
test/CMakeLists.txt Normal file
View File

@ -0,0 +1,37 @@
cmake_minimum_required(VERSION 3.14)
project(ssp_tests CXX)
# ---- Dependencies ----
set(
ssp_INCLUDE_WITHOUT_SYSTEM
YES
CACHE
INTERNAL
"Turn the warning guard off to have errors appear in test builds"
)
include(FetchContent)
FetchContent_Declare(ssp SOURCE_DIR "${PROJECT_SOURCE_DIR}/..")
FetchContent_MakeAvailable(ssp)
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
target_compile_options(ssp INTERFACE -Wall -Wextra)
endif()
find_package(doctest 2.4.4 CONFIG REQUIRED)
# for doctest_discover_tests
include(doctest)
# ---- Test ----
enable_testing()
foreach(name IN ITEMS test_splitter test_parser test_converter test_extractions)
add_executable("${name}" "${name}.cpp")
target_link_libraries("${name}" PRIVATE ssp::ssp doctest::doctest)
target_compile_definitions("${name}" PRIVATE
DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN CMAKE_GITHUB_CI)
doctest_discover_tests("${name}")
endforeach()

File diff suppressed because it is too large Load Diff

View File

@ -1,26 +0,0 @@
CXX=g++
CXXFLAGS=-Wall -Wextra -std=c++17 -lstdc++fs
TESTS=test_parser test_converter test_extractions test_splitter
all: $(TESTS)
# pattern rule, replacing built-in implicit .cpp-suffix rule
%: %.cpp
$(CXX) $(CXXFLAGS) $< -o $@
debug: CXXFLAGS += -g
debug: all
clean:
@$(RM) -fv $(TESTS)
@$(RM) *.csv
test:
@for i in $(TESTS); do \
./$$i; \
done
# don't use any implicit rules
.SUFFIXES:
# these rules won't actually build the targets they're named after
.PHONY: all clean run debug

19
test/meson.build Normal file
View File

@ -0,0 +1,19 @@
test_sources = files([
'test_main.cpp',
'test_splitter.cpp',
'test_converter.cpp',
'test_parser.cpp',
'test_extractions.cpp',
])
doctest_proj = subproject('doctest')
doctest_dep = doctest_proj.get_variable('doctest_dep')
test_exe = executable('test_ssp',
sources: test_sources,
dependencies: doctest_dep,
include_directories: includes,
cpp_args: '-lstdc++fs'
)
test('tests_ssp', test_exe)

View File

@ -1,8 +1,11 @@
#include <iostream>
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include "../include/ss/converter.hpp"
#include "doctest.h"
#include <algorithm> #include <algorithm>
#include <ss/converter.hpp>
#ifdef CMAKE_GITHUB_CI
#include <doctest/doctest.h>
#else
#include <doctest.h>
#endif
class buffer { class buffer {
constexpr static auto buff_size = 1024; constexpr static auto buff_size = 1024;
@ -83,17 +86,17 @@ TEST_CASE("testing valid conversions") {
{ {
auto tup = c.convert<int, double, void>(buff("5,6.6,junk")); auto tup = c.convert<int, double, void>(buff("5,6.6,junk"));
REQUIRE(c.valid()); REQUIRE(c.valid());
CHECK(tup == std::tuple{5, 6.6}); CHECK(tup == std::make_tuple(5, 6.6));
} }
{ {
auto tup = c.convert<int, void, double>(buff("5,junk,6.6")); auto tup = c.convert<int, void, double>(buff("5,junk,6.6"));
REQUIRE(c.valid()); REQUIRE(c.valid());
CHECK(tup == std::tuple{5, 6.6}); CHECK(tup == std::make_tuple(5, 6.6));
} }
{ {
auto tup = c.convert<void, int, double>(buff("junk;5;6.6"), ";"); auto tup = c.convert<void, int, double>(buff("junk;5;6.6"), ";");
REQUIRE(c.valid()); REQUIRE(c.valid());
CHECK(tup == std::tuple{5, 6.6}); CHECK(tup == std::make_tuple(5, 6.6));
} }
{ {
auto tup = auto tup =
@ -101,7 +104,7 @@ TEST_CASE("testing valid conversions") {
";"); ";");
REQUIRE(c.valid()); REQUIRE(c.valid());
REQUIRE(std::get<0>(tup).has_value()); REQUIRE(std::get<0>(tup).has_value());
CHECK(tup == std::tuple{5, 6.6}); CHECK(tup == std::make_tuple(5, 6.6));
} }
{ {
auto tup = auto tup =
@ -109,21 +112,21 @@ TEST_CASE("testing valid conversions") {
";"); ";");
REQUIRE(c.valid()); REQUIRE(c.valid());
REQUIRE(!std::get<0>(tup).has_value()); REQUIRE(!std::get<0>(tup).has_value());
CHECK(tup == std::tuple{std::nullopt, 6.6}); CHECK(tup == std::make_tuple(std::optional<int>{}, 6.6));
} }
{ {
auto tup = c.convert<void, std::variant<int, double>, auto tup = c.convert<void, std::variant<int, double>,
double>(buff("junk;5;6.6"), ";"); double>(buff("junk;5;6.6"), ";");
REQUIRE(c.valid()); REQUIRE(c.valid());
REQUIRE(std::holds_alternative<int>(std::get<0>(tup))); REQUIRE(std::holds_alternative<int>(std::get<0>(tup)));
CHECK(tup == std::tuple{std::variant<int, double>{5}, 6.6}); CHECK(tup == std::make_tuple(std::variant<int, double>{5}, 6.6));
} }
{ {
auto tup = c.convert<void, std::variant<int, double>, auto tup = c.convert<void, std::variant<int, double>,
double>(buff("junk;5.5;6.6"), ";"); double>(buff("junk;5.5;6.6"), ";");
REQUIRE(c.valid()); REQUIRE(c.valid());
REQUIRE(std::holds_alternative<double>(std::get<0>(tup))); REQUIRE(std::holds_alternative<double>(std::get<0>(tup)));
CHECK(tup == std::tuple{std::variant<int, double>{5.5}, 6.6}); CHECK(tup == std::make_tuple(std::variant<int, double>{5.5}, 6.6));
} }
} }
@ -182,13 +185,13 @@ TEST_CASE("testing ss:ax restriction (all except)") {
std::tuple<char, int> tup = std::tuple<char, int> tup =
c.convert<char, ss::ax<int, 1>>(buff("c,3")); c.convert<char, ss::ax<int, 1>>(buff("c,3"));
REQUIRE(c.valid()); REQUIRE(c.valid());
CHECK(tup == std::tuple{'c', 3}); CHECK(tup == std::make_tuple('c', 3));
} }
{ {
std::tuple<int, char> tup = std::tuple<int, char> tup =
c.convert<ss::ax<int, 1>, char>(buff("3,c")); c.convert<ss::ax<int, 1>, char>(buff("3,c"));
REQUIRE(c.valid()); REQUIRE(c.valid());
CHECK(tup == std::tuple{3, 'c'}); CHECK(tup == std::make_tuple(3, 'c'));
} }
} }
@ -218,12 +221,12 @@ TEST_CASE("testing ss:nx restriction (none except)") {
auto tup = auto tup =
c.convert<char, void, ss::nx<int, 0, 1, 2>>(buff("c,junk,1")); c.convert<char, void, ss::nx<int, 0, 1, 2>>(buff("c,junk,1"));
REQUIRE(c.valid()); REQUIRE(c.valid());
CHECK(tup == std::tuple{'c', 1}); CHECK(tup == std::make_tuple('c', 1));
} }
{ {
auto tup = c.convert<ss::nx<int, 1>, char>(buff("1,c")); auto tup = c.convert<ss::nx<int, 1>, char>(buff("1,c"));
REQUIRE(c.valid()); REQUIRE(c.valid());
CHECK(tup == std::tuple{1, 'c'}); CHECK(tup == std::make_tuple(1, 'c'));
} }
} }
@ -252,12 +255,12 @@ TEST_CASE("testing ss:ir restriction (in range)") {
{ {
auto tup = c.convert<char, void, ss::ir<int, 0, 1>>(buff("c,junk,1")); auto tup = c.convert<char, void, ss::ir<int, 0, 1>>(buff("c,junk,1"));
REQUIRE(c.valid()); REQUIRE(c.valid());
CHECK(tup == std::tuple{'c', 1}); CHECK(tup == std::make_tuple('c', 1));
} }
{ {
auto tup = c.convert<ss::ir<int, 1, 20>, char>(buff("1,c")); auto tup = c.convert<ss::ir<int, 1, 20>, char>(buff("1,c"));
REQUIRE(c.valid()); REQUIRE(c.valid());
CHECK(tup == std::tuple{1, 'c'}); CHECK(tup == std::make_tuple(1, 'c'));
} }
} }
@ -285,13 +288,13 @@ TEST_CASE("testing ss:oor restriction (out of range)") {
{ {
auto tup = c.convert<char, void, ss::oor<int, 4, 69>>(buff("c,junk,3")); auto tup = c.convert<char, void, ss::oor<int, 4, 69>>(buff("c,junk,3"));
REQUIRE(c.valid()); REQUIRE(c.valid());
CHECK(tup == std::tuple{'c', 3}); CHECK(tup == std::make_tuple('c', 3));
} }
{ {
auto tup = c.convert<ss::oor<int, 1, 2>, char>(buff("3,c")); auto tup = c.convert<ss::oor<int, 1, 2>, char>(buff("3,c"));
REQUIRE(c.valid()); REQUIRE(c.valid());
CHECK(tup == std::tuple{3, 'c'}); CHECK(tup == std::make_tuple(3, 'c'));
} }
} }
@ -335,7 +338,7 @@ TEST_CASE("testing ss:ne restriction (not empty)") {
auto tup = auto tup =
c.convert<std::optional<int>, ss::ne<std::string>>(buff("1,s")); c.convert<std::optional<int>, ss::ne<std::string>>(buff("1,s"));
REQUIRE(c.valid()); REQUIRE(c.valid());
CHECK(tup == std::tuple{1, "s"}); CHECK(tup == std::make_tuple(1, "s"));
} }
{ {
auto tup = c.convert<ss::ne<std::vector<int>>>(buff("{1 2 3}")); auto tup = c.convert<ss::ne<std::vector<int>>>(buff("{1 2 3}"));

View File

@ -1,8 +1,12 @@
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include <ss/extract.hpp>
#include "../include/ss/extract.hpp"
#include "doctest.h"
#include <algorithm> #include <algorithm>
#ifdef CMAKE_GITHUB_CI
#include <doctest/doctest.h>
#else
#include <doctest.h>
#endif
constexpr auto eps = 0.000001; constexpr auto eps = 0.000001;
using ld = long double; using ld = long double;

2
test/test_main.cpp Normal file
View File

@ -0,0 +1,2 @@
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include <doctest.h>

View File

@ -1,18 +1,20 @@
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include "../include/ss/parser.hpp"
#include "doctest.h"
#include <algorithm> #include <algorithm>
#include <filesystem> #include <filesystem>
#include <fstream>
#include <ss/parser.hpp>
#ifdef CMAKE_GITHUB_CI
#include <doctest/doctest.h>
#else
#include <doctest.h>
#endif
struct unique_file_name { struct unique_file_name {
const std::string name; const std::string name;
unique_file_name() : name{std::tmpnam(nullptr)} { unique_file_name() : name{std::tmpnam(nullptr)} {}
}
~unique_file_name() { ~unique_file_name() { std::filesystem::remove(name); }
std::filesystem::remove(name);
}
}; };
struct X { struct X {
@ -28,9 +30,7 @@ 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>
@ -167,13 +167,10 @@ 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("testing composite conversion") { TEST_CASE("testing composite conversion") {
@ -395,9 +392,7 @@ struct my_string {
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;
@ -428,9 +423,7 @@ 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("testing the moving of parsed values") { TEST_CASE("testing the moving of parsed values") {