Skip to content

Commit ffba31a

Browse files
authored
Merge pull request #1 from cppalliance/from_chars
Add CUDA support to `from_chars` for integers
2 parents 02583a1 + 977fb1f commit ffba31a

31 files changed

+2998
-26
lines changed

.github/workflows/ci.yml

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -765,3 +765,62 @@ jobs:
765765

766766
- name: Run tests
767767
run: ci/build.sh
768+
769+
770+
cuda-cmake-test:
771+
strategy:
772+
fail-fast: false
773+
774+
runs-on: gpu-runner-1
775+
776+
steps:
777+
- uses: Jimver/cuda-toolkit@v0.2.25
778+
id: cuda-toolkit
779+
with:
780+
cuda: '12.8.0'
781+
method: 'network'
782+
sub-packages: '["nvcc"]'
783+
784+
- name: Output CUDA information
785+
run: |
786+
echo "Installed cuda version is: ${{steps.cuda-toolkit.outputs.cuda}}"+
787+
echo "Cuda install location: ${{steps.cuda-toolkit.outputs.CUDA_PATH}}"
788+
nvcc -V
789+
- uses: actions/checkout@v4
790+
791+
- name: Install Packages
792+
run: |
793+
sudo apt-get install -y cmake make
794+
- name: Setup Boost
795+
run: |
796+
echo GITHUB_REPOSITORY: $GITHUB_REPOSITORY
797+
LIBRARY=${GITHUB_REPOSITORY#*/}
798+
echo LIBRARY: $LIBRARY
799+
echo "LIBRARY=$LIBRARY" >> $GITHUB_ENV
800+
echo GITHUB_BASE_REF: $GITHUB_BASE_REF
801+
echo GITHUB_REF: $GITHUB_REF
802+
REF=${GITHUB_BASE_REF:-$GITHUB_REF}
803+
REF=${REF#refs/heads/}
804+
echo REF: $REF
805+
BOOST_BRANCH=develop && [ "$REF" == "master" ] && BOOST_BRANCH=master || true
806+
echo BOOST_BRANCH: $BOOST_BRANCH
807+
cd ..
808+
git clone -b $BOOST_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root
809+
cd boost-root
810+
mkdir -p libs/$LIBRARY
811+
cp -r $GITHUB_WORKSPACE/* libs/$LIBRARY
812+
git submodule update --init tools/boostdep
813+
python3 tools/boostdep/depinst/depinst.py --git_args "--jobs 3" $LIBRARY
814+
- name: Configure
815+
run: |
816+
cd ../boost-root
817+
mkdir __build__ && cd __build__
818+
cmake -DBOOST_INCLUDE_LIBRARIES=$LIBRARY -DBUILD_TESTING=ON -DCMAKE_CUDA_COMPILER=/usr/local/cuda/bin/nvcc -DBOOST_CHARCONV_ENABLE_CUDA=1 -DCMAKE_CUDA_ARCHITECTURES="75;86" -DCUDA_TOOLKIT_ROOT_DIR=/usr/local/cuda-12.8 -DCMAKE_CUDA_STANDARD=17 ..
819+
- name: Build tests
820+
run: |
821+
cd ../boost-root/__build__
822+
cmake --build . --target tests -j $(nproc)
823+
- name: Run tests
824+
run: |
825+
cd ../boost-root/__build__
826+
ctest --output-on-failure --no-tests=error

include/boost/charconv/detail/config.hpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
#endif
2121

2222
// Use 128-bit integers and suppress warnings for using extensions
23-
#if defined(BOOST_HAS_INT128)
23+
#if defined(BOOST_HAS_INT128) && !defined(__NVCC__)
2424
# define BOOST_CHARCONV_HAS_INT128
2525
# define BOOST_CHARCONV_INT128_MAX static_cast<boost::int128_type>((static_cast<boost::uint128_type>(1) << 127) - 1)
2626
# define BOOST_CHARCONV_INT128_MIN (-BOOST_CHARCONV_INT128_MAX - 1)
@@ -201,5 +201,10 @@ static_assert(std::is_same<long double, __float128>::value, "__float128 should b
201201

202202
#endif
203203

204+
#ifdef __NVCC__
205+
# define BOOST_CHARCONV_HOST_DEVICE __host__ __device__
206+
#else
207+
# define BOOST_CHARCONV_HOST_DEVICE
208+
#endif
204209

205210
#endif // BOOST_CHARCONV_DETAIL_CONFIG_HPP

include/boost/charconv/detail/from_chars_integer_impl.hpp

Lines changed: 101 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222

2323
namespace boost { namespace charconv { namespace detail {
2424

25+
#ifndef __NVCC__
26+
2527
static constexpr unsigned char uchar_values[] =
2628
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
2729
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
@@ -83,9 +85,33 @@ static constexpr double log_2_table[] =
8385
0.193426403617
8486
};
8587

88+
#endif // __NVCC__
89+
8690
// Convert characters for 0-9, A-Z, a-z to 0-35. Anything else is 255
87-
constexpr unsigned char digit_from_char(char val) noexcept
91+
BOOST_CHARCONV_HOST_DEVICE constexpr unsigned char digit_from_char(const char val) noexcept
8892
{
93+
#ifdef __NVCC__
94+
95+
constexpr unsigned char uchar_values[] =
96+
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
97+
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
98+
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
99+
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255,
100+
255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
101+
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 255, 255, 255, 255, 255,
102+
255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
103+
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 255, 255, 255, 255, 255,
104+
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
105+
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
106+
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
107+
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
108+
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
109+
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
110+
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
111+
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255};
112+
113+
#endif // __NVCC__
114+
89115
return uchar_values[static_cast<unsigned char>(val)];
90116
}
91117

@@ -111,9 +137,75 @@ constexpr unsigned char digit_from_char(char val) noexcept
111137

112138
#endif
113139

140+
#ifdef __NVCC__
141+
142+
template <typename T>
143+
__host__ __device__ constexpr T get_max_value()
144+
{
145+
using UT = typename std::make_unsigned<T>::type;
146+
return std::is_signed<T>::value
147+
? static_cast<T>(static_cast<UT>(-1) >> 1)
148+
: static_cast<T>(static_cast<UT>(-1));
149+
}
150+
151+
#else
152+
153+
template <typename T>
154+
constexpr T get_max_value()
155+
{
156+
return (std::numeric_limits<T>::max)();
157+
}
158+
159+
#endif
160+
114161
template <typename Integer, typename Unsigned_Integer>
115-
BOOST_CXX14_CONSTEXPR from_chars_result from_chars_integer_impl(const char* first, const char* last, Integer& value, int base) noexcept
162+
BOOST_CHARCONV_HOST_DEVICE BOOST_CXX14_CONSTEXPR from_chars_result from_chars_integer_impl(const char* first, const char* last, Integer& value, int base) noexcept
116163
{
164+
#ifdef __NVCC__
165+
166+
constexpr double log_2_table[] =
167+
{
168+
0.0,
169+
0.0,
170+
1.0,
171+
0.630929753571,
172+
0.5,
173+
0.430676558073,
174+
0.386852807235,
175+
0.356207187108,
176+
0.333333333333,
177+
0.315464876786,
178+
0.301029995664,
179+
0.289064826318,
180+
0.278942945651,
181+
0.270238154427,
182+
0.262649535037,
183+
0.255958024810,
184+
0.25,
185+
0.244650542118,
186+
0.239812466568,
187+
0.235408913367,
188+
0.231378213160,
189+
0.227670248697,
190+
0.224243824218,
191+
0.221064729458,
192+
0.218104291986,
193+
0.215338279037,
194+
0.212746053553,
195+
0.210309917857,
196+
0.208014597677,
197+
0.205846832460,
198+
0.203795047091,
199+
0.201849086582,
200+
0.2,
201+
0.198239863171,
202+
0.196561632233,
203+
0.194959021894,
204+
0.193426403617
205+
};
206+
207+
#endif // __NVCC__
208+
117209
Unsigned_Integer result = 0;
118210
Unsigned_Integer overflow_value = 0;
119211
Unsigned_Integer max_digit = 0;
@@ -155,8 +247,8 @@ BOOST_CXX14_CONSTEXPR from_chars_result from_chars_integer_impl(const char* firs
155247
else
156248
#endif
157249
{
158-
overflow_value = static_cast<Unsigned_Integer>((std::numeric_limits<Integer>::max)());
159-
max_digit = static_cast<Unsigned_Integer>((std::numeric_limits<Integer>::max)());
250+
overflow_value = static_cast<Unsigned_Integer>(get_max_value<Integer>());
251+
max_digit = static_cast<Unsigned_Integer>(get_max_value<Integer>());
160252
}
161253

162254
if (is_negative)
@@ -181,8 +273,8 @@ BOOST_CXX14_CONSTEXPR from_chars_result from_chars_integer_impl(const char* firs
181273
else
182274
#endif
183275
{
184-
overflow_value = (std::numeric_limits<Unsigned_Integer>::max)();
185-
max_digit = (std::numeric_limits<Unsigned_Integer>::max)();
276+
overflow_value = get_max_value<Unsigned_Integer>();
277+
max_digit = get_max_value<Unsigned_Integer>();
186278
}
187279
}
188280

@@ -310,7 +402,7 @@ BOOST_CXX14_CONSTEXPR from_chars_result from_chars_integer_impl(const char* firs
310402

311403
// Only from_chars for integer types is constexpr (as of C++23)
312404
template <typename Integer>
313-
BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(const char* first, const char* last, Integer& value, int base = 10) noexcept
405+
BOOST_CHARCONV_HOST_DEVICE BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(const char* first, const char* last, Integer& value, int base = 10) noexcept
314406
{
315407
using Unsigned_Integer = typename std::make_unsigned<Integer>::type;
316408
return detail::from_chars_integer_impl<Integer, Unsigned_Integer>(first, last, value, base);
@@ -325,10 +417,12 @@ BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars128(const char* first,
325417
}
326418
#endif
327419

420+
#ifndef __NVCC__
328421
BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars128(const char* first, const char* last, uint128& value, int base = 10) noexcept
329422
{
330423
return from_chars_integer_impl<uint128, uint128>(first, last, value, base);
331424
}
425+
#endif
332426

333427
}}} // Namespaces
334428

include/boost/charconv/detail/from_chars_result.hpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#ifndef BOOST_CHARCONV_DETAIL_FROM_CHARS_RESULT_HPP
66
#define BOOST_CHARCONV_DETAIL_FROM_CHARS_RESULT_HPP
77

8+
#include <boost/charconv/detail/config.hpp>
89
#include <system_error>
910

1011
namespace boost { namespace charconv {
@@ -22,17 +23,17 @@ struct from_chars_result_t
2223
// ERANGE = result_out_of_range
2324
std::errc ec;
2425

25-
friend constexpr bool operator==(const from_chars_result_t<UC>& lhs, const from_chars_result_t<UC>& rhs) noexcept
26+
BOOST_CHARCONV_HOST_DEVICE friend constexpr bool operator==(const from_chars_result_t<UC>& lhs, const from_chars_result_t<UC>& rhs) noexcept
2627
{
2728
return lhs.ptr == rhs.ptr && lhs.ec == rhs.ec;
2829
}
2930

30-
friend constexpr bool operator!=(const from_chars_result_t<UC>& lhs, const from_chars_result_t<UC>& rhs) noexcept
31+
BOOST_CHARCONV_HOST_DEVICE friend constexpr bool operator!=(const from_chars_result_t<UC>& lhs, const from_chars_result_t<UC>& rhs) noexcept
3132
{
3233
return !(lhs == rhs); // NOLINT : Expression can not be simplified since this is the definition
3334
}
3435

35-
constexpr explicit operator bool() const noexcept { return ec == std::errc{}; }
36+
BOOST_CHARCONV_HOST_DEVICE constexpr explicit operator bool() const noexcept { return ec == std::errc{}; }
3637
};
3738
using from_chars_result = from_chars_result_t<char>;
3839

include/boost/charconv/from_chars.hpp

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,48 +19,48 @@ namespace boost { namespace charconv {
1919

2020
// integer overloads
2121

22-
BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(const char* first, const char* last, bool& value, int base = 10) noexcept = delete;
23-
BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(const char* first, const char* last, char& value, int base = 10) noexcept
22+
BOOST_CHARCONV_HOST_DEVICE BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(const char* first, const char* last, bool& value, int base = 10) noexcept = delete;
23+
BOOST_CHARCONV_HOST_DEVICE BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(const char* first, const char* last, char& value, int base = 10) noexcept
2424
{
2525
return detail::from_chars(first, last, value, base);
2626
}
27-
BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(const char* first, const char* last, signed char& value, int base = 10) noexcept
27+
BOOST_CHARCONV_HOST_DEVICE BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(const char* first, const char* last, signed char& value, int base = 10) noexcept
2828
{
2929
return detail::from_chars(first, last, value, base);
3030
}
31-
BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(const char* first, const char* last, unsigned char& value, int base = 10) noexcept
31+
BOOST_CHARCONV_HOST_DEVICE BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(const char* first, const char* last, unsigned char& value, int base = 10) noexcept
3232
{
3333
return detail::from_chars(first, last, value, base);
3434
}
35-
BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(const char* first, const char* last, short& value, int base = 10) noexcept
35+
BOOST_CHARCONV_HOST_DEVICE BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(const char* first, const char* last, short& value, int base = 10) noexcept
3636
{
3737
return detail::from_chars(first, last, value, base);
3838
}
39-
BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(const char* first, const char* last, unsigned short& value, int base = 10) noexcept
39+
BOOST_CHARCONV_HOST_DEVICE BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(const char* first, const char* last, unsigned short& value, int base = 10) noexcept
4040
{
4141
return detail::from_chars(first, last, value, base);
4242
}
43-
BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(const char* first, const char* last, int& value, int base = 10) noexcept
43+
BOOST_CHARCONV_HOST_DEVICE BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(const char* first, const char* last, int& value, int base = 10) noexcept
4444
{
4545
return detail::from_chars(first, last, value, base);
4646
}
47-
BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(const char* first, const char* last, unsigned int& value, int base = 10) noexcept
47+
BOOST_CHARCONV_HOST_DEVICE BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(const char* first, const char* last, unsigned int& value, int base = 10) noexcept
4848
{
4949
return detail::from_chars(first, last, value, base);
5050
}
51-
BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(const char* first, const char* last, long& value, int base = 10) noexcept
51+
BOOST_CHARCONV_HOST_DEVICE BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(const char* first, const char* last, long& value, int base = 10) noexcept
5252
{
5353
return detail::from_chars(first, last, value, base);
5454
}
55-
BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(const char* first, const char* last, unsigned long& value, int base = 10) noexcept
55+
BOOST_CHARCONV_HOST_DEVICE BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(const char* first, const char* last, unsigned long& value, int base = 10) noexcept
5656
{
5757
return detail::from_chars(first, last, value, base);
5858
}
59-
BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(const char* first, const char* last, long long& value, int base = 10) noexcept
59+
BOOST_CHARCONV_HOST_DEVICE BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(const char* first, const char* last, long long& value, int base = 10) noexcept
6060
{
6161
return detail::from_chars(first, last, value, base);
6262
}
63-
BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(const char* first, const char* last, unsigned long long& value, int base = 10) noexcept
63+
BOOST_CHARCONV_HOST_DEVICE BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(const char* first, const char* last, unsigned long long& value, int base = 10) noexcept
6464
{
6565
return detail::from_chars(first, last, value, base);
6666
}

test/CMakeLists.txt

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,24 @@ include(BoostTestJamfile OPTIONAL RESULT_VARIABLE HAVE_BOOST_TEST)
66

77
if(HAVE_BOOST_TEST)
88

9-
# https://crascit.com/2015/03/28/enabling-cxx11-in-cmake/
10-
set(CMAKE_CXX_EXTENSIONS OFF)
11-
boost_test_jamfile(FILE Jamfile LINK_LIBRARIES Boost::charconv Boost::core Boost::assert)
9+
enable_testing()
10+
11+
if(BOOST_CHARCONV_ENABLE_CUDA)
12+
13+
message(STATUS "Building Boost.charconv with CUDA")
14+
15+
find_package(CUDA REQUIRED)
16+
enable_language(CUDA)
17+
set(CMAKE_CUDA_EXTENSIONS OFF)
18+
19+
boost_test_jamfile(FILE cuda_jamfile LINK_LIBRARIES Boost::charconv Boost::core Boost::assert ${CUDA_LIBRARIES} INCLUDE_DIRECTORIES ${CUDA_INCLUDE_DIRS} )
20+
21+
else()
22+
23+
# https://crascit.com/2015/03/28/enabling-cxx11-in-cmake/
24+
set(CMAKE_CXX_EXTENSIONS OFF)
25+
boost_test_jamfile(FILE Jamfile LINK_LIBRARIES Boost::charconv Boost::core Boost::assert)
26+
27+
endif()
1228

1329
endif()

0 commit comments

Comments
 (0)