mirror of
https://github.com/red0124/ssp.git
synced 2025-02-02 16:51:12 +01:00
Merge pull request #16 from red0124/feature/single_header
Feature/single header
This commit is contained in:
commit
d58644fd67
2
.gitignore
vendored
2
.gitignore
vendored
@ -6,3 +6,5 @@ build/
|
|||||||
hbuild/
|
hbuild/
|
||||||
subprojects/*
|
subprojects/*
|
||||||
!subprojects/*.wrap
|
!subprojects/*.wrap
|
||||||
|
ssp.cpp
|
||||||
|
ssp.bin
|
||||||
|
@ -69,6 +69,10 @@ Bill (Heath) Gates 65 3.3
|
|||||||
* [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 +82,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
|
||||||
|
|
||||||
|
@ -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';
|
||||||
|
34
script/single_header_generator.py
Executable file
34
script/single_header_generator.py
Executable 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))
|
@ -4,6 +4,7 @@ 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_proj = subproject('doctest')
|
||||||
|
@ -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";
|
||||||
|
132
test/test_extractions_without_fast_float.cpp
Normal file
132
test/test_extractions_without_fast_float.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -46,3 +46,36 @@ struct buffer {
|
|||||||
};
|
};
|
||||||
|
|
||||||
[[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));
|
||||||
|
14
test/test_single_header.sh
Executable file
14
test/test_single_header.sh
Executable 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
|
Loading…
Reference in New Issue
Block a user