Skip to content

Commit

Permalink
Number: Add {std,fast_float}::from_chars wrappers
Browse files Browse the repository at this point in the history
  • Loading branch information
dscharrer committed Apr 9, 2022
1 parent c076fee commit efe6058
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 4 deletions.
18 changes: 15 additions & 3 deletions CMakeLists.txt
Expand Up @@ -109,6 +109,7 @@ suboption(USE_LTO "Use link-time code generation" BOOL ${default_USE_LTO})
suboption(WERROR "Turn warnings into errors" BOOL ${CONTINUOUS_INTEGRATION})

suboption(CXX_STD_VERSION "Maximum C++ standard version to enable" STRING 2017)
option(USE_CXX17_FROM_CHARS_FLOAT "Use std::from_chars(float) instead of fast_float::from_chars" OFF)
if(DEVELOPER OR CMAKE_BUILD_TYPE STREQUAL "Debug")
set(default_DEBUG ON)
else()
Expand Down Expand Up @@ -639,9 +640,15 @@ endif()

if(NOT CXX_STD_VERSION LESS 2011)
enable_cxx_version(${CXX_STD_VERSION})
if(WIN32)
check_cxx17("std::fstream(wchar*)" ARX_HAVE_CXX17_FSTREAM_WCHAR ALWAYS)
endif()
endif()
if(WIN32)
check_cxx17("std::fstream(wchar*)" ARX_HAVE_CXX17_FSTREAM_WCHAR ALWAYS)
endif()
check_cxx17("std::from_chars(int)" ARX_HAVE_CXX17_FROM_CHARS_INT 1914)
if(USE_CXX17_FROM_CHARS_FLOAT)
check_cxx17("std::from_chars(float)" ARX_HAVE_CXX17_FROM_CHARS_FLOAT 1924)
else()
set(ARX_HAVE_CXX17_FROM_CHARS_FLOAT 0)
endif()

if(MSVC)
Expand Down Expand Up @@ -1233,6 +1240,7 @@ set(SCRIPT_SOURCES
)

set(UTIL_SOURCES
src/util/Number.cpp
src/util/String.cpp
)

Expand Down Expand Up @@ -1392,6 +1400,10 @@ endif()

list(APPEND ARX_LIBRARIES ${BASE_LIBRARIES})

if(NOT ARX_HAVE_CXX17_FROM_CHARS_FLOAT)
include_directories(SYSTEM thirdparty/fast_float/include)
endif()

if(NOT MSVC)
check_link_library(Boost Boost_LIBRARIES)
endif()
Expand Down
17 changes: 17 additions & 0 deletions cmake/check/cxx17-std-from_chars-float.cpp
@@ -0,0 +1,17 @@

#include <charconv>
#include <string_view>

int main(int argc, const char * argv[]) {

if(argc < 1) {
return 0;
}

std::string_view string(argv[0]);

float result = 0.f;
std::from_chars(string.data(), string.data() + string.length(), result);

return static_cast<int>(result);
}
18 changes: 18 additions & 0 deletions cmake/check/cxx17-std-from_chars-int.cpp
@@ -0,0 +1,18 @@

#include <charconv>
#include <string_view>
#include <cstddef>

int main(int argc, const char * argv[]) {

if(argc < 1) {
return 0;
}

std::string_view string(argv[0]);

std::int32_t result = 0;
std::from_chars(string.data(), string.data() + string.length(), result);

return static_cast<int>(result);
}
4 changes: 4 additions & 0 deletions src/platform/PlatformConfig.h.in
Expand Up @@ -12,6 +12,10 @@
// C++17 features
// wide character support for filenames in fstream
#cmakedefine01 ARX_HAVE_CXX17_FSTREAM_WCHAR
// std::from_chars(float)
#cmakedefine01 ARX_HAVE_CXX17_FROM_CHARS_FLOAT
// std::from_chars(int)
#cmakedefine01 ARX_HAVE_CXX17_FROM_CHARS_INT

// GCC extensions
// __attribute__((alloc_align(i)))
Expand Down
107 changes: 107 additions & 0 deletions src/util/Number.cpp
@@ -0,0 +1,107 @@
/*
* Copyright 2022 Arx Libertatis Team (see the AUTHORS file)
*
* This file is part of Arx Libertatis.
*
* Arx Libertatis is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Arx Libertatis is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Arx Libertatis. If not, see <http://www.gnu.org/licenses/>.
*/

#include "util/Number.h"

#include "platform/PlatformConfig.h"

#if ARX_HAVE_CXX17_FROM_CHARS_INT ||ARX_HAVE_CXX17_FROM_CHARS_FLOAT
#include <charconv>
#endif

#if !ARX_HAVE_CXX17_FROM_CHARS_INT
#include "boost/lexical_cast.hpp"
#endif

#if !ARX_HAVE_CXX17_FROM_CHARS_FLOAT
#include "fast_float/fast_float.h"
#endif

#include "util/String.h"


namespace util {

//! Convert the start of string to a float similar to \ref atoi
[[nodiscard]] std::optional<s32> toInt(std::string_view string, bool allowTrailingGarbage) noexcept {

if(!string.empty() && string[0] == '+') {
string.remove_prefix(1);
}

#if ARX_HAVE_CXX17_FROM_CHARS_INT
s32 value{};
auto result = std::from_chars(string.data(), string.data() + string.length(), value);
if(result.ec == std::errc() && (allowTrailingGarbage || result.ptr == string.data() + string.length())) {
return value;
} else {
return { };
}
#else
if(allowTrailingGarbage) {
size_t end = 0;
if(end != string.length() && string[end] == '-') {
end++;
}
if(end != string.length() && string[end] >= '0' && string[end] <= '9') {
end++;
}
string = string.substr(0, end);
}
try {
return boost::lexical_cast<s32>(string);
} catch(...) {
return { };
}
#endif

}

//! Convert the start of string to a float similar to \ref atof
[[nodiscard]] std::optional<float> toFloat(std::string_view string, bool allowTrailingGarbage) noexcept {

if(!string.empty() && string[0] == '+') {
string.remove_prefix(1);
}

float value{};

#if ARX_HAVE_CXX17_FROM_CHARS_FLOAT
auto result = std::from_chars(string.data(), string.data() + string.length(), value);
#else
auto result = fast_float::from_chars(string.data(), string.data() + string.length(), value);
#endif

if(result.ec == std::errc() && (allowTrailingGarbage || result.ptr == string.data() + string.length())) {
return value;
} else {
return { };
}

}

s32 parseInt(std::string_view string) noexcept {
return toInt(trimLeft(string), true).value_or(0);
}

float parseFloat(std::string_view string) noexcept {
return toFloat(trimLeft(string), true).value_or(0.f);
}

} // namespace util
44 changes: 44 additions & 0 deletions src/util/Number.h
@@ -0,0 +1,44 @@
/*
* Copyright 2022 Arx Libertatis Team (see the AUTHORS file)
*
* This file is part of Arx Libertatis.
*
* Arx Libertatis is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Arx Libertatis is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Arx Libertatis. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef ARX_UTIL_NUMBER_H
#define ARX_UTIL_NUMBER_H

#include <optional>
#include <string_view>

#include "platform/Platform.h"

namespace util {

//! Convert the start of string to a float similar to \ref atoi but don't allow leading whitespace
[[nodiscard]] std::optional<s32> toInt(std::string_view string, bool allowTrailingGarbage = false) noexcept;

//! Convert the start of string to a float similar to \ref atof but don't allow leading whitespace
[[nodiscard]] std::optional<float> toFloat(std::string_view string, bool allowTrailingGarbage = false) noexcept;

//! Convert the start of string to a float similar to \ref atoi
[[nodiscard]] s32 parseInt(std::string_view string) noexcept;

//! Convert the start of string to a float similar to \ref atof
[[nodiscard]] float parseFloat(std::string_view string) noexcept;

} // namespace util

#endif // ARX_UTIL_NUMBER_H
1 change: 0 additions & 1 deletion src/util/String.cpp
Expand Up @@ -84,5 +84,4 @@ std::string getDateTimeString() {
return localTimeString.str();
}


} // namespace util

0 comments on commit efe6058

Please sign in to comment.