61 Commits

Author SHA1 Message Date
red0124
57abdb3923 Merge pull request #26 from red0124/bugfix/terminated_escape_on_quoted_multiline
Fix bug where the parsing of data containing escaped new lines within…
2023-07-31 23:05:52 +02:00
ado
27ef7f2e21 Fix bug where the parsing of data containing escaped new lines within quotes would produce bad outputs 2023-07-31 21:49:15 +02:00
ado
f58091cf02 Fix test_parser.cpp formating 2023-07-29 16:51:34 +02:00
red0124
78c75abec7 Merge pull request #25 from red0124/feature/win_msvc_ci
Feature/win msvc ci
2023-07-29 16:25:26 +02:00
ado
76091263f2 Fix win-msvc ci badge 2023-07-29 16:11:27 +02:00
ado
d99cba274e Add win-msvc ci badge 2023-07-29 16:08:33 +02:00
ado
e70fc05646 Fix msvc ci configure step 2023-07-29 15:39:51 +02:00
ado
21d1096edf Fix msvc ci yaml issues 2023-07-29 15:37:53 +02:00
ado
72b74a3b64 Fix msvc ci configure step 2023-07-29 15:35:29 +02:00
ado
b7f6009eb9 Fix msvc ci yaml issues 2023-07-29 15:30:05 +02:00
ado
bdfd896861 Fix msvc vs 16 ci 2023-07-29 15:17:39 +02:00
ado
d6bc8086b2 Fix msvc ci configure step 2023-07-29 15:03:25 +02:00
ado
f762736da2 Fix msvc ci configure step 2023-07-29 14:59:49 +02:00
ado
739077a8db Fix msvc ci vs version names 2023-07-29 14:54:25 +02:00
ado
3dcb2ce2ef Add vs16 for msvc ci 2023-07-29 14:22:41 +02:00
ado
e4d9e10ac3 Update msvc ci 2023-07-29 14:14:02 +02:00
ado
a98742945b Update test helpers 2023-07-29 13:56:00 +02:00
ado
5d6c2c4af5 Remove move count unit test from parser tests 2023-07-29 01:44:19 +02:00
ado
58857fff2d Add config option for ctest within win-msvc.yml 2023-07-29 01:18:11 +02:00
ado
e6a7e09975 Add config option for ctest within win-msvc.yml 2023-07-29 01:18:01 +02:00
ado
705ce422f0 Add constexpr values to lambda to handle msvc issue with respect to captured constexpr values 2023-07-29 01:11:29 +02:00
ado
75f5ee2a55 Add _CRT_SECURE_NO_WARNINGS define to remove msvc warnings 2023-07-29 00:56:08 +02:00
ado
6baeb2d598 Add _CRT_SECURE_NO_WARNINGS define to remove msvc warnings 2023-07-29 00:53:04 +02:00
ado
7b1f49d304 Add _CRT_SECURE_NO_WARNINGS define to remove msvc warnings 2023-07-29 00:49:06 +02:00
ado
515ddad997 Add _CRT_SECURE_NO_WARNINGS define to remove msvc warnings 2023-07-29 00:45:33 +02:00
ado
fd39b5eef2 Try msvc ci 2023-07-29 00:38:27 +02:00
red0124
77185cb366 Merge pull request #24 from red0124/improvement/markdown_cicd_badge_link
Add hyperlink for cicd badges
2023-07-28 21:30:10 +02:00
ado
ad991d6a7d Add hyperlink for cicd badges 2023-07-28 21:28:32 +02:00
red0124
304ca6ef0f Merge pull request #23 from red0124/bugfix/single_header_include_update
Update ssp.hpp to handle issue fixed in the previous commit
2023-07-25 11:24:54 +02:00
ado
103ff33f21 Update ssp.hpp to handle issue fixed in the previous commit 2023-07-25 11:13:32 +02:00
red0124
8b928de086 Merge pull request #22 from red0124/bugfix/multiline_on_custom_delimiter
Fix bug where the default delimiter would be used for multiline data
2023-07-25 00:46:43 +02:00
ado
6edce88d79 Fix bug where the default delimiter would be used for multiline data 2023-07-25 00:36:53 +02:00
red0124
6196f7796b Merge pull request #21 from red0124/improvement/markdown_labels
Update README links to sections
2023-07-20 00:21:38 +02:00
ado
5672aa635e Update README links to sections 2023-07-20 00:19:31 +02:00
ado
3eefac93b1 Fix README issues 2023-05-25 01:10:37 +02:00
ado
774f452689 fix typo 2023-03-19 19:48:22 +01:00
ado
448066b173 Update version 2023-02-18 14:20:32 +01:00
red0124
a7eca6064a Merge pull request #20 from red0124/improvement/conditional_t
Replace ss::ternary_t with std::conditional_t
2023-02-12 12:57:36 +01:00
ado
a4ecbd4dc8 Replace ss::ternary_t with std::conditional_t 2023-02-12 12:45:49 +01:00
red0124
a9e9783e6a Merge pull request #19 from red0124/improvement/doctest_update
Make project used forked doctest
2023-02-12 12:38:34 +01:00
ado
04edf1e532 Make project used forked doctest 2023-02-12 12:23:25 +01:00
red0124
8dcb69aa2c Merge pull request #18 from red0124/improvement/cmake_update
Update CMake files
2023-02-09 20:20:27 +01:00
ado
db2a72c18b Update CMake files 2023-02-08 21:54:44 +01:00
red0124
0e28a06799 Merge pull request #17 from red0124/improvement/meson_update
Improvement/meson update
2023-02-07 23:29:29 +01:00
ado
2218b01b81 Merge remote-tracking branch 'origin/master' into improvement/meson_update 2023-02-07 23:22:36 +01:00
ado
f777b04eb8 Fix failing mingw unit test 2023-02-01 22:45:50 +01:00
ado
7831bbd735 Add forcefallback wrap_mode option in meson.build 2023-02-01 01:22:43 +01:00
ado
6efb39b2db Add version number to meson.build 2023-02-01 00:57:32 +01:00
ado
f2b49e6d6c Update meson dependency usage 2023-02-01 00:52:23 +01:00
ado
5cb3c65b24 Update doctest revision 2023-01-31 21:23:04 +01:00
red0124
d58644fd67 Merge pull request #16 from red0124/feature/single_header
Feature/single header
2022-03-30 20:26:51 +02:00
ado
56447eb1d6 update README 2022-03-30 20:21:23 +02:00
ado
86d732e743 update README 2022-03-30 20:20:13 +02:00
ado
031ab5f7fc update README 2022-03-30 20:18:18 +02:00
ado
420625b25c update README 2022-03-30 20:15:14 +02:00
ado
9fa9edb24d add spp.hpp 2022-03-30 20:11:55 +02:00
ado
a2d72cdef3 remove unused script 2022-03-30 19:56:57 +02:00
ado
c214975ca0 add unit tests for conversion without the fast float library 2022-03-30 19:54:50 +02:00
ado
d328f7d59d add test_single_header.sh 2022-03-30 18:35:10 +02:00
ado
62055f03c7 add script to generate single header 2022-03-30 18:14:30 +02:00
ado
999992e579 fix README typos 2022-03-29 12:31:59 +02:00
22 changed files with 3384 additions and 237 deletions

63
.github/workflows/win-msvc.yml vendored Normal file
View 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}}

2
.gitignore vendored
View File

@@ -6,3 +6,5 @@ build/
hbuild/ hbuild/
subprojects/* subprojects/*
!subprojects/*.wrap !subprojects/*.wrap
ssp.cpp
ssp.bin

View File

@@ -2,8 +2,8 @@ cmake_minimum_required(VERSION 3.14)
project( project(
ssp ssp
VERSION 0.0.1 VERSION 1.4.0
DESCRIPTION "Static split parser" DESCRIPTION "csv parser"
HOMEPAGE_URL "https://github.com/red0124/ssp" HOMEPAGE_URL "https://github.com/red0124/ssp"
LANGUAGES CXX LANGUAGES CXX
) )
@@ -11,22 +11,23 @@ project(
# ---- Warning guard ---- # ---- Warning guard ----
# Protect dependents from this project's warnings if the guard isn't disabled # Protect dependents from this project's warnings if the guard isn't disabled
set(ssp_warning_guard SYSTEM) set(SSP_WARNING_GUARD SYSTEM)
if(ssp_INCLUDE_WITHOUT_SYSTEM) if(SSP_INCLUDE_WITHOUT_SYSTEM)
set(ssp_warning_guard "") set(SSP_WARNING_GUARD "")
endif() endif()
# ---- Dependencies ---- # ---- Dependencies ----
include(FetchContent) include(FetchContent)
FetchContent_Declare( fetchcontent_declare(
fast_float fast_float
GIT_REPOSITORY https://github.com/red0124/fast_float.git GIT_REPOSITORY https://github.com/red0124/fast_float.git
GIT_TAG origin/meson GIT_TAG origin/meson
GIT_SHALLOW TRUE) GIT_SHALLOW TRUE
)
FetchContent_MakeAvailable(fast_float) fetchcontent_makeavailable(fast_float)
set(fast_float_source_dir "${FETCHCONTENT_BASE_DIR}/fast_float-src") set(FAST_FLOAT_SOURCE_DIR "${FETCHCONTENT_BASE_DIR}/fast_float-src")
# ---- Declare library ---- # ---- Declare library ----
@@ -35,10 +36,10 @@ add_library(ssp::ssp ALIAS ssp)
target_include_directories( target_include_directories(
ssp ssp
${ssp_warning_guard} ${SSP_WARNING_GUARD}
INTERFACE INTERFACE
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>" "$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>"
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/fast_float>" "$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/fast_float>"
) )
target_compile_features(ssp INTERFACE cxx_std_17) target_compile_features(ssp INTERFACE cxx_std_17)
@@ -46,8 +47,8 @@ target_compile_features(ssp INTERFACE cxx_std_17)
target_link_libraries( target_link_libraries(
ssp ssp
INTERFACE INTERFACE
"$<$<AND:$<CXX_COMPILER_ID:AppleClang,Clang>,$<VERSION_LESS:$<CXX_COMPILER_VERSION>,9.0>>:c++fs>" "$<$<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>" "$<$<AND:$<CXX_COMPILER_ID:GNU>,$<VERSION_LESS:$<CXX_COMPILER_VERSION>,9.1>>:stdc++fs>"
) )
# ---- Install ---- # ---- Install ----
@@ -55,19 +56,21 @@ target_link_libraries(
include(CMakePackageConfigHelpers) include(CMakePackageConfigHelpers)
include(GNUInstallDirs) include(GNUInstallDirs)
set(ssp_directory "ssp-${PROJECT_VERSION}") set(SSP_DIRECTORY "ssp-${PROJECT_VERSION}")
set(ssp_include_directory "${CMAKE_INSTALL_INCLUDEDIR}") set(SSP_INCLUDE_DIRECTORY "${CMAKE_INSTALL_INCLUDEDIR}")
install( install(
DIRECTORY "${PROJECT_SOURCE_DIR}/include/" "${fast_float_source_dir}/include/" DIRECTORY
DESTINATION "${ssp_include_directory}" "${PROJECT_SOURCE_DIR}/include/"
"${FAST_FLOAT_SOURCE_DIR}/include/"
DESTINATION "${SSP_INCLUDE_DIRECTORY}"
COMPONENT ssp_Development COMPONENT ssp_Development
) )
install( install(
TARGETS ssp TARGETS ssp
EXPORT sspTargets EXPORT sspTargets
INCLUDES DESTINATION "${ssp_include_directory}" INCLUDES DESTINATION "${SSP_INCLUDE_DIRECTORY}"
) )
write_basic_package_version_file( write_basic_package_version_file(
@@ -76,11 +79,11 @@ write_basic_package_version_file(
ARCH_INDEPENDENT ARCH_INDEPENDENT
) )
set(ssp_install_cmakedir "${CMAKE_INSTALL_LIBDIR}/cmake/${ssp_directory}") set(SSP_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/${SSP_DIRECTORY}")
install( install(
FILES "${PROJECT_BINARY_DIR}/ssp-config-version.cmake" FILES "${PROJECT_BINARY_DIR}/ssp-config-version.cmake"
DESTINATION "${ssp_install_cmakedir}" DESTINATION "${SSP_INSTALL_CMAKEDIR}"
COMPONENT ssp_Development COMPONENT ssp_Development
) )
@@ -88,10 +91,10 @@ install(
EXPORT sspTargets EXPORT sspTargets
FILE ssp-config.cmake FILE ssp-config.cmake
NAMESPACE ssp:: NAMESPACE ssp::
DESTINATION "${ssp_install_cmakedir}" DESTINATION "${SSP_INSTALL_CMAKEDIR}"
COMPONENT ssp_Development COMPONENT ssp_Development
) )
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
include(CPack) include(CPack)
endif() endif()

View File

@@ -8,11 +8,12 @@
``` ```
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT)
![ubuntu-latest-gcc](https://github.com/red0124/ssp/workflows/ubuntu-latest-gcc-ci/badge.svg) [![ubuntu-latest-gcc](https://github.com/red0124/ssp/workflows/ubuntu-latest-gcc-ci/badge.svg)](https://github.com/red0124/ssp/actions/workflows/ubuntu-latest-gcc.yml)
![ubuntu-latest-clang](https://github.com/red0124/ssp/workflows/ubuntu-latest-clang-ci/badge.svg) [![ubuntu-latest-clang](https://github.com/red0124/ssp/workflows/ubuntu-latest-clang-ci/badge.svg)](https://github.com/red0124/ssp/actions/workflows/ubuntu-latest-clang.yml)
![ubuntu-latest-icc](https://github.com/red0124/ssp/workflows/ubuntu-latest-icc-ci/badge.svg) [![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) [![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) [![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)
@@ -53,22 +54,26 @@ Brian S. Wolfe 40 1.9
Bill (Heath) Gates 65 3.3 Bill (Heath) Gates 65 3.3
``` ```
# Features # Features
* [Works on any type](#Custom-conversions) * [Works on any type](#custom-conversions)
* Easy to use * Easy to use
* No exceptions * No exceptions
* [Works with headers](#Headers) * [Works with headers](#headers)
* [Works with quotes, escapes and spacings](#Setup) * [Works with quotes, escapes and spacings](#setup)
* [Works with values containing new lines](#Multiline) * [Works with values containing new lines](#multiline)
* [Columns and rows can be ignored](#Special-types) * [Columns and rows can be ignored](#special-types)
* Works with any type of delimiter * Works with any type of delimiter
* Can return whole objects composed of converted values * Can return whole objects composed of converted values
* [Descriptive error handling can be enabled](#Error-handling) * [Descriptive error handling can be enabled](#error-handling)
* [Restrictions can be added for each column](#Restrictions) * [Restrictions can be added for each column](#restrictions)
* [Works with `std::optional` and `std::variant`](#Special-types) * [Works with `std::optional` and `std::variant`](#special-types)
* Works with **`CRLF`** and **`LF`** * Works with **`CRLF`** and **`LF`**
* [Conversions can be chained if invalid](#Substitute-conversions) * [Conversions can be chained if invalid](#substitute-conversions)
* Fast * Fast
# Single header
The library can be used with a single header file **`ssp.hpp`**, but it sufferes a slight performance loss when converting floating point values since the **`fast_float`** library is not present within the file.
# Installation # Installation
```shell ```shell
@@ -78,8 +83,8 @@ $ cmake --configure .
$ sudo make install $ sudo make install
``` ```
*Note, this will also install the fast_float library* *Note, this will also install the fast_float library*
The library supports [CMake](#Cmake) and [meson](#Meson) build systems The library supports [CMake](#Cmake) and [meson](#Meson) build systems
# Usage # Usage
@@ -113,7 +118,7 @@ The header can be ignored using the **`ss::ignore_header`** [setup](#Setup) opti
```cpp ```cpp
ss::parser<ss::ignore_header> p{file_name}; ss::parser<ss::ignore_header> p{file_name};
``` ```
The fields with which the parser works with can be modified at any given time. The paser can also check if a field is present within the header by using the **`has_field`** method. The fields with which the parser works with can be modified at any given time. The praser can also check if a field is present within the header by using the **`field_exists`** method.
```cpp ```cpp
// ... // ...
ss::parser p{"students.csv", ","}; ss::parser p{"students.csv", ","};
@@ -374,7 +379,7 @@ if(std::holds_alternative<float>(grade)) {
``` ```
## Restrictions ## Restrictions
Custom **`restrictions`** can be used to narrow down the conversions of unwanted values. **`ss::ir`** (in range) and **`ss::ne`** (none empty) are one of those: Custom **`restrictions`** can be used to narrow down the conversions of unwanted values. **`ss::ir`** (in range) and **`ss::ne`** (none empty) are some of those:
```cpp ```cpp
// ss::ne makes sure that the name is not empty // ss::ne makes sure that the name is not empty
// ss::ir makes sure that the grade will be in range [0, 10] // ss::ir makes sure that the grade will be in range [0, 10]
@@ -468,7 +473,6 @@ The delimiter is " ", and the number of columns varies depending on which shape
```cpp ```cpp
ss::parser p{"shapes.txt", " "}; ss::parser p{"shapes.txt", " "};
if (!p.valid()) { if (!p.valid()) {
std::cout << p.error_msg() << std::endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@@ -510,7 +514,7 @@ while (!p.eof()) {
``` ```
It is quite hard to make an error this way since most things will be checked at compile time. It is quite hard to make an error this way since most things will be checked at compile time.
The **`try_next`** method works in a similar way as **`get_next`** but returns a **`composit`** which holds a **`tuple`** with an **`optional`** to the **`tuple`** returned by **`get_next`**. This **`composite`** has an **`or_else`** method (looks a bit like **`tl::expected`**) which is able to try additional conversions if the previous failed. **`or_else`** also returns a **`composite`**, but in its tuple is the **`optional`** to the **`tuple`** of the previous conversions and an **`optional`** to the **`tuple`** of the new conversion. (sounds more complicated than it is. The **`try_next`** method works in a similar way as **`get_next`** but returns a **`composite`** which holds a **`tuple`** with an **`optional`** to the **`tuple`** returned by **`get_next`**. This **`composite`** has an **`or_else`** method (looks a bit like **`tl::expected`**) which is able to try additional conversions if the previous failed. **`or_else`** also returns a **`composite`**, but in its tuple is the **`optional`** to the **`tuple`** of the previous conversions and an **`optional`** to the **`tuple`** of the new conversion. (sounds more complicated than it is.
To fetch the **`tuple`** from the **`composite`** the **`values`** method is used. The value of the above used conversion would look something like this: To fetch the **`tuple`** from the **`composite`** the **`values`** method is used. The value of the above used conversion would look something like this:
```cpp ```cpp
@@ -628,6 +632,5 @@ revision = origin/master
``` ```
Then simply fetch the dependency and it is ready to be used: Then simply fetch the dependency and it is ready to be used:
```meson ```meson
ssp_sub = subproject('ssp') ssp_dep = dependency('ssp')
ssp_dep = ssp_sub.get_variable('ssp_dep')
``` ```

View File

@@ -102,7 +102,7 @@ class converter {
constexpr static auto string_error = setup<Matchers...>::string_error; constexpr static auto string_error = setup<Matchers...>::string_error;
constexpr static auto default_delimiter = ","; constexpr static auto default_delimiter = ",";
using error_type = ss::ternary_t<string_error, std::string, bool>; using error_type = std::conditional_t<string_error, std::string, bool>;
public: public:
// parses line with given delimiter, returns a 'T' object created with // parses line with given delimiter, returns a 'T' object created with
@@ -194,7 +194,7 @@ private:
//////////////// ////////////////
const split_data& resplit(line_ptr_type new_line, ssize_t new_size, const split_data& resplit(line_ptr_type new_line, ssize_t new_size,
const std::string& delim = default_delimiter) { const std::string& delim) {
return splitter_.resplit(new_line, new_size, delim); return splitter_.resplit(new_line, new_size, delim);
} }

View File

@@ -2,21 +2,27 @@
#include "type_traits.hpp" #include "type_traits.hpp"
#include <cstring> #include <cstring>
#include <fast_float/fast_float.h>
#include <functional> #include <functional>
#include <limits> #include <limits>
#include <optional> #include <optional>
#include <stdexcept>
#include <string> #include <string>
#include <string_view> #include <string_view>
#include <variant> #include <variant>
#ifndef SSP_DISABLE_FAST_FLOAT
#include <fast_float/fast_float.h>
#else
#include <charconv>
#endif
namespace ss { namespace ss {
//////////////// ////////////////
// number converters // number converters
//////////////// ////////////////
#ifndef SSP_DISABLE_FAST_FLOAT
template <typename T> template <typename T>
std::enable_if_t<std::is_floating_point_v<T>, std::optional<T>> to_num( std::enable_if_t<std::is_floating_point_v<T>, std::optional<T>> to_num(
const char* const begin, const char* const end) { const char* const begin, const char* const end) {
@@ -29,6 +35,22 @@ std::enable_if_t<std::is_floating_point_v<T>, std::optional<T>> to_num(
return ret; return ret;
} }
#else
template <typename T>
std::enable_if_t<std::is_floating_point_v<T>, std::optional<T>> to_num(
const char* const begin, const char* const end) {
T ret;
auto [ptr, ec] = std::from_chars(begin, end, ret);
if (ec != std::errc() || ptr != end) {
return std::nullopt;
}
return ret;
}
#endif
inline std::optional<short> from_char(char c) { inline std::optional<short> from_char(char c) {
if (c >= '0' && c <= '9') { if (c >= '0' && c <= '9') {
return c - '0'; return c - '0';

View File

@@ -17,7 +17,7 @@ class parser {
constexpr static auto string_error = setup<Matchers...>::string_error; constexpr static auto string_error = setup<Matchers...>::string_error;
using multiline = typename setup<Matchers...>::multiline; using multiline = typename setup<Matchers...>::multiline;
using error_type = ss::ternary_t<string_error, std::string, bool>; using error_type = std::conditional_t<string_error, std::string, bool>;
constexpr static bool escaped_multiline_enabled = constexpr static bool escaped_multiline_enabled =
multiline::enabled && setup<Matchers...>::escape::enabled; multiline::enabled && setup<Matchers...>::escape::enabled;
@@ -151,8 +151,8 @@ public:
template <bool get_object, typename T, typename... Ts> template <bool get_object, typename T, typename... Ts>
struct iterable { struct iterable {
struct iterator { struct iterator {
using value = using value = std::conditional_t<get_object, T,
ss::ternary_t<get_object, T, no_void_validator_tup_t<T, Ts...>>; no_void_validator_tup_t<T, Ts...>>;
iterator() : parser_{nullptr} { iterator() : parser_{nullptr} {
} }
@@ -600,6 +600,8 @@ private:
if constexpr (quoted_multiline_enabled) { if constexpr (quoted_multiline_enabled) {
while (unterminated_quote()) { while (unterminated_quote()) {
size -= next_line_converter_.size_shifted();
if (multiline_limit_reached(limit)) { if (multiline_limit_reached(limit)) {
return true; return true;
} }
@@ -623,7 +625,8 @@ private:
} }
} }
next_line_converter_.resplit(next_line_buffer_, size); next_line_converter_.resplit(next_line_buffer_, size,
delim_);
} }
} }
@@ -664,9 +667,6 @@ private:
} }
void undo_remove_eol(char* buffer, size_t& string_end) { void undo_remove_eol(char* buffer, size_t& string_end) {
if (next_line_converter_.unterminated_quote()) {
string_end -= next_line_converter_.size_shifted();
}
if (crlf_) { if (crlf_) {
std::copy_n("\r\n\0", 3, buffer + string_end); std::copy_n("\r\n\0", 3, buffer + string_end);
string_end += 2; string_end += 2;

View File

@@ -109,10 +109,10 @@ struct get_matcher<Matcher, T, Ts...> {
struct is_matcher : is_instance_of_matcher<U, Matcher> {}; struct is_matcher : is_instance_of_matcher<U, Matcher> {};
static_assert(count_v<is_matcher, T, Ts...> <= 1, static_assert(count_v<is_matcher, T, Ts...> <= 1,
"the same matcher is cannot" "the same matcher cannot"
"be defined multiple times"); "be defined multiple times");
using type = ternary_t<is_matcher<T>::value, T, using type = std::conditional_t<is_matcher<T>::value, T,
typename get_matcher<Matcher, Ts...>::type>; typename get_matcher<Matcher, Ts...>::type>;
}; };
template <template <char...> class Matcher> template <template <char...> class Matcher>
@@ -149,8 +149,8 @@ struct get_multiline;
template <typename T, typename... Ts> template <typename T, typename... Ts>
struct get_multiline<T, Ts...> { struct get_multiline<T, Ts...> {
using type = ternary_t<is_instance_of_multiline<T>::value, T, using type = std::conditional_t<is_instance_of_multiline<T>::value, T,
typename get_multiline<Ts...>::type>; typename get_multiline<Ts...>::type>;
}; };
template <> template <>
@@ -227,8 +227,10 @@ public:
using quote = get_matcher_t<quote, Ts...>; using quote = get_matcher_t<quote, Ts...>;
using escape = get_matcher_t<escape, Ts...>; using escape = get_matcher_t<escape, Ts...>;
using trim_left = ternary_t<trim_all::enabled, trim_all, trim_left_only>; using trim_left =
using trim_right = ternary_t<trim_all::enabled, trim_all, trim_right_only>; std::conditional_t<trim_all::enabled, trim_all, trim_left_only>;
using trim_right =
std::conditional_t<trim_all::enabled, trim_all, trim_right_only>;
using multiline = get_multiline_t<Ts...>; using multiline = get_multiline_t<Ts...>;
constexpr static bool string_error = (count_string_error == 1); constexpr static bool string_error = (count_string_error == 1);

View File

@@ -23,10 +23,10 @@ private:
constexpr static auto string_error = setup<Ts...>::string_error; constexpr static auto string_error = setup<Ts...>::string_error;
constexpr static auto is_const_line = !quote::enabled && !escape::enabled; constexpr static auto is_const_line = !quote::enabled && !escape::enabled;
using error_type = ss::ternary_t<string_error, std::string, bool>; using error_type = std::conditional_t<string_error, std::string, bool>;
public: public:
using line_ptr_type = ternary_t<is_const_line, const char*, char*>; using line_ptr_type = std::conditional_t<is_const_line, const char*, char*>;
bool valid() const { bool valid() const {
if constexpr (string_error) { if constexpr (string_error) {

View File

@@ -357,26 +357,6 @@ struct is_instance_of<Template, Template<Ts...>> {
template <template <typename...> class Template, typename... Ts> template <template <typename...> class Template, typename... Ts>
constexpr bool is_instance_of_v = is_instance_of<Template, Ts...>::value; constexpr bool is_instance_of_v = is_instance_of<Template, Ts...>::value;
////////////////
// ternary
////////////////
template <bool B, typename T, typename U>
struct ternary;
template <typename T, typename U>
struct ternary<true, T, U> {
using type = T;
};
template <typename T, typename U>
struct ternary<false, T, U> {
using type = U;
};
template <bool B, typename T, typename U>
using ternary_t = typename ternary<B, T, U>::type;
//////////////// ////////////////
// tuple to struct // tuple to struct
//////////////// ////////////////

View File

@@ -1,17 +1,24 @@
project('ssp', 'cpp', project(
'ssp',
['cpp'],
default_options : default_options :
['warning_level=3', ['warning_level=3',
'cpp_std=c++17', 'cpp_std=c++17',
'buildtype=debugoptimized']) 'buildtype=debugoptimized',
'wrap_mode=forcefallback'],
version: '1.4.0',
meson_version:'>=0.54.0')
fast_float_sub = subproject('fast_float') fast_float_dep = dependency('fast_float')
fast_float_dep = fast_float_sub.get_variable('fast_float_dep')
ssp_dep = declare_dependency( ssp_dep = declare_dependency(
include_directories: include_directories('include'), include_directories: include_directories('include'),
dependencies: fast_float_dep dependencies: fast_float_dep
) )
meson.override_dependency('ssp', ssp_dep)
if not meson.is_subproject() if not meson.is_subproject()
subdir('test') subdir('test')
endif endif

View File

@@ -5,7 +5,7 @@ BUILD_TYPE=Debug
set -eux set -eux
git clone https://github.com/onqtam/doctest -b 2.4.4 --depth 1 git clone https://github.com/red0124/doctest -b master --depth 1
cmake -S doctest -B doctest/build \ cmake -S doctest -B doctest/build \
-D CMAKE_BUILD_TYPE=${BUILD_TYPE} \ -D CMAKE_BUILD_TYPE=${BUILD_TYPE} \

View File

@@ -0,0 +1,34 @@
#!/bin/python3
headers_dir = 'include/ss/'
headers = ['type_traits.hpp',
'function_traits.hpp',
'restrictions.hpp',
'common.hpp',
'setup.hpp',
'splitter.hpp',
'extract.hpp',
'converter.hpp',
'parser.hpp']
combined_file = []
includes = []
for header in headers:
with open(headers_dir + header) as f:
for line in f.read().splitlines():
if '#include "' in line or '#include <fast_float' in line:
continue
if '#include <' in line:
includes.append(line)
continue
if '#pragma once' not in line:
combined_file.append(line)
includes = sorted(set(includes))
print('\n'.join(includes))
print('#define SSP_DISABLE_FAST_FLOAT')
print('\n'.join(combined_file))

2949
ssp.hpp Normal file

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -4,34 +4,38 @@ project(ssp_tests CXX)
# ---- Dependencies ---- # ---- Dependencies ----
set(
ssp_INCLUDE_WITHOUT_SYSTEM
YES
CACHE
INTERNAL
"Turn the warning guard off to have errors appear in test builds"
)
include(FetchContent) include(FetchContent)
FetchContent_Declare(ssp SOURCE_DIR "${PROJECT_SOURCE_DIR}/..") fetchcontent_declare(ssp SOURCE_DIR "${PROJECT_SOURCE_DIR}/..")
FetchContent_MakeAvailable(ssp) fetchcontent_makeavailable(ssp)
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
target_compile_options(ssp INTERFACE -Wall -Wextra) target_compile_options(ssp INTERFACE -Wall -Wextra)
endif() endif()
find_package(doctest 2.4.4 CONFIG REQUIRED) include(FetchContent)
# for doctest_discover_tests fetchcontent_declare(
include(doctest) DOCTEST
GIT_REPOSITORY https://github.com/red0124/doctest
GIT_TAG origin/master
GIT_SHALLOW TRUE
)
fetchcontent_makeavailable(DOCTEST)
set(DOCTEST "${FETCHCONTENT_BASE_DIR}/doctest-src")
# ---- Test ---- # ---- Test ----
enable_testing() enable_testing()
foreach(name IN ITEMS test_splitter test_parser test_converter test_extractions) foreach(name IN ITEMS test_splitter test_parser test_converter test_extractions)
add_executable("${name}" "${name}.cpp") add_executable("${name}" "${name}.cpp")
target_link_libraries("${name}" PRIVATE ssp::ssp fast_float doctest::doctest) target_link_libraries(
target_compile_definitions("${name}" PRIVATE "${name}"
DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN CMAKE_GITHUB_CI) PRIVATE ssp::ssp fast_float doctest::doctest
doctest_discover_tests("${name}") )
target_compile_definitions(
"${name}"
PRIVATE DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN CMAKE_GITHUB_CI
)
add_test(NAME "${name}" COMMAND "${name}")
endforeach() endforeach()

View File

@@ -4,10 +4,10 @@ test_sources = files([
'test_converter.cpp', 'test_converter.cpp',
'test_parser.cpp', 'test_parser.cpp',
'test_extractions.cpp', 'test_extractions.cpp',
'test_extractions_without_fast_float.cpp',
]) ])
doctest_proj = subproject('doctest') doctest_dep = dependency('doctest')
doctest_dep = doctest_proj.get_variable('doctest_dep')
test_exe = executable( test_exe = executable(
'test_ssp', 'test_ssp',

View File

@@ -2,23 +2,6 @@
#include <algorithm> #include <algorithm>
#include <ss/extract.hpp> #include <ss/extract.hpp>
#define CHECK_FLOATING_CONVERSION(input, type) \
{ \
auto eps = std::numeric_limits<type>::min(); \
std::string s = #input; \
auto t = ss::to_num<type>(s.c_str(), s.c_str() + s.size()); \
REQUIRE(t.has_value()); \
CHECK_LT(std::abs(t.value() - type(input)), eps); \
} \
{ \
/* check negative too */ \
auto eps = std::numeric_limits<type>::min(); \
auto s = std::string("-") + #input; \
auto t = ss::to_num<type>(s.c_str(), s.c_str() + s.size()); \
REQUIRE(t.has_value()); \
CHECK_LT(std::abs(t.value() - type(-input)), eps); \
}
TEST_CASE("testing extract functions for floating point values") { TEST_CASE("testing extract functions for floating point values") {
CHECK_FLOATING_CONVERSION(123.456, float); CHECK_FLOATING_CONVERSION(123.456, float);
CHECK_FLOATING_CONVERSION(123.456, double); CHECK_FLOATING_CONVERSION(123.456, double);
@@ -70,13 +53,6 @@ TEST_CASE("extract test functions for decimal values") {
CHECK_DECIMAL_CONVERSION(1234567891011, ull); CHECK_DECIMAL_CONVERSION(1234567891011, ull);
} }
#define CHECK_INVALID_CONVERSION(input, type) \
{ \
std::string s = input; \
auto t = ss::to_num<type>(s.c_str(), s.c_str() + s.size()); \
CHECK_FALSE(t.has_value()); \
}
TEST_CASE("extract test functions for numbers with invalid inputs") { TEST_CASE("extract test functions for numbers with invalid inputs") {
// negative unsigned value // negative unsigned value
CHECK_INVALID_CONVERSION("-1234", ul); CHECK_INVALID_CONVERSION("-1234", ul);
@@ -200,15 +176,6 @@ TEST_CASE("extract test functions for std::optional") {
} }
} }
#define REQUIRE_VARIANT(var, el, type) \
{ \
auto ptr = std::get_if<type>(&var); \
REQUIRE(ptr); \
REQUIRE_EQ(el, *ptr); \
}
#define CHECK_NOT_VARIANT(var, type) CHECK(!std::holds_alternative<type>(var));
TEST_CASE("extract test functions for std::variant") { TEST_CASE("extract test functions for std::variant") {
{ {
std::string s = "22"; std::string s = "22";

View File

@@ -0,0 +1,132 @@
#include "test_helpers.hpp"
#include <algorithm>
#define SSP_DISABLE_FAST_FLOAT
#include <ss/extract.hpp>
TEST_CASE(
"testing extract functions for floating point values without fast float") {
CHECK_FLOATING_CONVERSION(123.456, float);
CHECK_FLOATING_CONVERSION(123.456, double);
CHECK_FLOATING_CONVERSION(69, float);
CHECK_FLOATING_CONVERSION(69, double);
CHECK_FLOATING_CONVERSION(420., float);
CHECK_FLOATING_CONVERSION(420., double);
CHECK_FLOATING_CONVERSION(0.123, float);
CHECK_FLOATING_CONVERSION(0.123, double);
CHECK_FLOATING_CONVERSION(123e4, float);
CHECK_FLOATING_CONVERSION(123e4, double);
}
TEST_CASE("extract test functions for numbers with invalid inputs without fast "
"float") {
// floating pint for int
CHECK_INVALID_CONVERSION("123.4", int);
// random input for float
CHECK_INVALID_CONVERSION("xxx1", float);
}
TEST_CASE("extract test functions for std::variant without fast float") {
{
std::string s = "22";
{
std::variant<int, double, std::string> var;
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
CHECK_NOT_VARIANT(var, double);
CHECK_NOT_VARIANT(var, std::string);
REQUIRE_VARIANT(var, 22, int);
}
{
std::variant<double, int, std::string> var;
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
CHECK_NOT_VARIANT(var, int);
CHECK_NOT_VARIANT(var, std::string);
REQUIRE_VARIANT(var, 22, double);
}
{
std::variant<std::string, double, int> var;
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
CHECK_NOT_VARIANT(var, int);
CHECK_NOT_VARIANT(var, double);
REQUIRE_VARIANT(var, "22", std::string);
}
{
std::variant<int> var;
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
REQUIRE_VARIANT(var, 22, int);
}
}
{
std::string s = "22.2";
{
std::variant<int, double, std::string> var;
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
CHECK_NOT_VARIANT(var, int);
CHECK_NOT_VARIANT(var, std::string);
REQUIRE_VARIANT(var, 22.2, double);
}
{
std::variant<double, int, std::string> var;
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
CHECK_NOT_VARIANT(var, int);
CHECK_NOT_VARIANT(var, std::string);
REQUIRE_VARIANT(var, 22.2, double);
}
{
std::variant<std::string, double, int> var;
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
CHECK_NOT_VARIANT(var, int);
CHECK_NOT_VARIANT(var, double);
REQUIRE_VARIANT(var, "22.2", std::string);
}
}
{
std::string s = "2.2.2";
{
std::variant<int, double, std::string> var;
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
CHECK_NOT_VARIANT(var, int);
CHECK_NOT_VARIANT(var, double);
REQUIRE_VARIANT(var, "2.2.2", std::string);
}
{
std::variant<double, std::string, int> var;
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
CHECK_NOT_VARIANT(var, int);
CHECK_NOT_VARIANT(var, double);
REQUIRE_VARIANT(var, "2.2.2", std::string);
}
{
std::variant<std::string, double, int> var;
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
CHECK_NOT_VARIANT(var, int);
CHECK_NOT_VARIANT(var, double);
REQUIRE_VARIANT(var, "2.2.2", std::string);
}
{
std::variant<int, double> var;
REQUIRE_FALSE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
REQUIRE_VARIANT(var, int{}, int);
CHECK_NOT_VARIANT(var, double);
}
{
std::variant<double, int> var;
REQUIRE_FALSE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
REQUIRE_VARIANT(var, double{}, double);
CHECK_NOT_VARIANT(var, int);
}
{
std::variant<int> var;
REQUIRE_FALSE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
REQUIRE_VARIANT(var, int{}, int);
}
}
}

View File

@@ -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,55 @@
#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;
#define CHECK_FLOATING_CONVERSION(input, type) \
{ \
auto eps = std::numeric_limits<type>::min(); \
std::string s = #input; \
auto t = ss::to_num<type>(s.c_str(), s.c_str() + s.size()); \
REQUIRE(t.has_value()); \
CHECK_LT(std::abs(t.value() - type(input)), eps); \
} \
{ \
/* check negative too */ \
auto eps = std::numeric_limits<type>::min(); \
auto s = std::string("-") + #input; \
auto t = ss::to_num<type>(s.c_str(), s.c_str() + s.size()); \
REQUIRE(t.has_value()); \
CHECK_LT(std::abs(t.value() - type(-input)), eps); \
}
#define CHECK_INVALID_CONVERSION(input, type) \
{ \
std::string s = input; \
auto t = ss::to_num<type>(s.c_str(), s.c_str() + s.size()); \
CHECK_FALSE(t.has_value()); \
}
#define REQUIRE_VARIANT(var, el, type) \
{ \
auto ptr = std::get_if<type>(&var); \
REQUIRE(ptr); \
REQUIRE_EQ(el, *ptr); \
}
#define CHECK_NOT_VARIANT(var, type) CHECK(!std::holds_alternative<type>(var));

View File

@@ -283,7 +283,7 @@ TEST_CASE("parser test various cases") {
} }
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);
} }
@@ -378,7 +378,7 @@ TEST_CASE("parser test composite conversion") {
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) { CHECK_EQ(data, expectedData); }) [&](auto&& data) { CHECK_EQ(data, expectedData); })
.on_error(fail) .on_error(fail)
.or_else<test_tuple>(fail) .or_else<test_tuple>(fail)
.values(); .values();
@@ -396,7 +396,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)
@@ -564,8 +564,6 @@ 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};
@@ -580,12 +578,10 @@ struct my_string {
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;
} }
@@ -609,52 +605,6 @@ struct xyz {
} }
}; };
TEST_CASE("parser test the moving of parsed values") {
size_t move_called_one_col;
{
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_LT(move_called, 3);
move_called_one_col = move_called;
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_one_col);
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_one_col);
move_called = 0;
}
{
ss::parser p{f.name, ","};
auto x = p.get_next<xyz>();
CHECK_LE(move_called, 6 * move_called_one_col);
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;

14
test/test_single_header.sh Executable file
View File

@@ -0,0 +1,14 @@
#!/bin/bash
set -x
set -e
python3 script/single_header_generator.py > ssp.cpp
echo 'int main(){ ss::parser p{""}; p.get_next<int, float>(); return 0; }' \
>> ssp.cpp
g++ -std=c++17 ssp.cpp -o ssp.bin -Wall -Wextra
./ssp.bin
rm ssp.cpp ssp.bin