Skip to content
This repository was archived by the owner on Jun 1, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Fix compiling (missing std::char_traits<uint8_t>) on llvm for tests a…
…s well. #1812
  • Loading branch information
leakingmemory committed Jun 29, 2025
commit 6df13a8c0417ef700c0f164bcd0686ad46f66fd9
7 changes: 4 additions & 3 deletions Release/include/cpprest/astreambuf.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

#include "cpprest/asyncrt_utils.h"
#include "cpprest/details/basic_types.h"
#include "cpprest/details/char_traits.h"
#include "pplx/pplxtasks.h"
#include <atomic>
#include <cstring>
Expand Down Expand Up @@ -57,16 +58,16 @@ namespace streams
/// The data type of the basic element of the stream.
/// </typeparam>
template<typename _CharType>
struct char_traits : std::char_traits<_CharType>
struct char_traits : utility::CanUseStdCharTraits<_CharType>::TraitsType
{
/// <summary>
/// Some synchronous functions will return this value if the operation
/// requires an asynchronous call in a given situation.
/// </summary>
/// <returns>An <c>int_type</c> value which implies that an asynchronous call is required.</returns>
static typename std::char_traits<_CharType>::int_type requires_async()
static typename utility::CanUseStdCharTraits<_CharType>::TraitsType::int_type requires_async()
{
return std::char_traits<_CharType>::eof() - 1;
return utility::CanUseStdCharTraits<_CharType>::TraitsType::eof() - 1;
}
};
#if !defined(_WIN32)
Expand Down
92 changes: 92 additions & 0 deletions Release/include/cpprest/details/char_traits.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
//
// Created by sigsegv on 6/28/25.
//

#ifndef CPPRESTSDK_ROOT_CHAR_TRAITS_H
#define CPPRESTSDK_ROOT_CHAR_TRAITS_H

#include <type_traits>
#include <string>

namespace utility {

namespace detail {

template <typename T> class DetailCharTraits
{
public:
using char_type = T;
using int_type = std::char_traits<char>::int_type;
using off_type = std::streamoff;
using pos_type = std::streampos;
using state_type = mbstate_t;

static void assign(char_type& r, const char_type& a) noexcept { r = a; }
static char_type to_char_type(int_type c) noexcept { return char_type(c); }
static int_type to_int_type(char_type c) noexcept { return c; }
static bool eq(char_type a, char_type b) noexcept { return a == b; }
static bool lt(char_type a, char_type b) noexcept { return a < b; }
static int compare(const char_type* s1,const char_type* s2,size_t n){
for (; n--; ++s1, ++s2) {
if (!eq(*s1, *s2))
return lt(*s1,*s2)?-1:1;
}
return 0;
}
static size_t length(const char_type* s){
const char_type* p = s;
while (*p)
++p;
return size_t(p - s);
}
static const char_type* find(const char_type* s,size_t n,const char_type& a){
for (; n--; ++s)
{
if (eq(*s, a))
return s;
return nullptr;
}
}
static char_type* move (char_type* r,const char_type* s,size_t n){
return (char_type*)memmove(r, s, n * sizeof(char_type));
}
static char_type* copy (char_type* r,const char_type* s,size_t n){
return (char_type*)memcpy (r, s, n * sizeof(char_type));
}
static char_type* assign(char_type* r,size_t n,char_type a){
if (sizeof(char_type) == 1)
{
return (char_type*)memset(r, a, n);
}
else
{
for (char_type *s = r; n--; ++s)
{
*s = a;
}
}
}
static int_type eof() noexcept { return ~0u; }
static int_type not_eof(int_type c) noexcept { return c == eof() ? 0 : c; }
};

template <typename T, typename = bool> struct CanUseStdCharTraits : public std::false_type
{
public:
typedef DetailCharTraits<T> TraitsType;
};

template <typename T> struct CanUseStdCharTraits<T, decltype(std::char_traits<T>::eq(std::declval<T>(), std::declval<T>()))> : public std::true_type
{
public:
typedef std::char_traits<T> TraitsType;
};

}

template <typename T> struct CanUseStdCharTraits : detail::CanUseStdCharTraits<typename std::remove_const<typename std::remove_reference<T>::type>::type> {
};

}

#endif // CPPRESTSDK_ROOT_CHAR_TRAITS_H
91 changes: 13 additions & 78 deletions Release/include/cpprest/streams.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#define CASA_STREAMS_H

#include "cpprest/astreambuf.h"
#include "cpprest/details/char_traits.h"
#include <iosfwd>
#include <cstdio>

Expand Down Expand Up @@ -64,97 +65,31 @@ template<typename CharType>
struct Value2StringFormatter
{
template<typename T>
static std::basic_string<CharType> format(const T& val)
static std::basic_string<CharType, typename utility::CanUseStdCharTraits<CharType>::TraitsType> format(const T& val)
{
std::basic_ostringstream<CharType> ss;
std::basic_ostringstream<CharType, typename utility::CanUseStdCharTraits<CharType>::TraitsType> ss;
ss << val;
return ss.str();
}
};

template <typename T> class DetailCharTraits
{
public:
using char_type = T;
using int_type = unsigned int;
using off_type = std::streamoff;
using pos_type = std::streampos;
using state_type = mbstate_t;

static void assign(char_type& r, const char_type& a) noexcept { r = a; }
static char_type to_char_type(int_type c) noexcept { return char_type(c); }
static int_type to_int_type(char_type c) noexcept { return c; }
static bool eq(char_type a, char_type b) noexcept { return a == b; }
static bool lt(char_type a, char_type b) noexcept { return a < b; }
static int compare(const char_type* s1,const char_type* s2,size_t n){
for (; n--; ++s1, ++s2) {
if (!eq(*s1, *s2))
return lt(*s1,*s2)?-1:1;
}
return 0;
}
static size_t length(const char_type* s){
const char_type* p = s;
while (*p)
++p;
return size_t(p - s);
}
static const char_type* find(const char_type* s,size_t n,const char_type& a){
for (; n--; ++s)
{
if (eq(*s, a))
return s;
return nullptr;
}
}
static char_type* move (char_type* r,const char_type* s,size_t n){
return (char_type*)memmove(r, s, n * sizeof(char_type));
}
static char_type* copy (char_type* r,const char_type* s,size_t n){
return (char_type*)memcpy (r, s, n * sizeof(char_type));
}
static char_type* assign(char_type* r,size_t n,char_type a){
if (sizeof(char_type) == 1)
{
return (char_type*)memset(r, a, n);
}
else
{
for (char_type *s = r; n--; ++s)
{
*s = a;
}
}
}
static int_type eof() noexcept { return ~0u; }
static int_type not_eof(int_type c) noexcept { return c == eof() ? 0 : c; }
};

template <typename T, typename = bool> struct CanUseStdCharTraits : public std::false_type
{
public:
typedef DetailCharTraits<T> TraitsType;
};

template <typename T> struct CanUseStdCharTraits<T, decltype(std::char_traits<T>::eq(std::declval<T>(), std::declval<T>()))> : public std::true_type
{
public:
typedef std::char_traits<T> TraitsType;
};


template<>
struct Value2StringFormatter<uint8_t>
{
template<typename T>
static std::basic_string<uint8_t, CanUseStdCharTraits<uint8_t>::TraitsType> format(const T& val)
static std::basic_string<uint8_t, typename utility::CanUseStdCharTraits<uint8_t>::TraitsType> format(const T& val)
{
std::basic_ostringstream<char> ss;
ss << val;
return reinterpret_cast<const uint8_t*>(ss.str().c_str());
}

static std::basic_string<uint8_t, CanUseStdCharTraits<uint8_t>::TraitsType> format(const utf16string& val)
template <> std::basic_string<uint8_t, typename utility::CanUseStdCharTraits<uint8_t>::TraitsType> format<std::basic_string<uint8_t, typename utility::CanUseStdCharTraits<uint8_t>::TraitsType>>(const std::basic_string<uint8_t, typename utility::CanUseStdCharTraits<uint8_t>::TraitsType> &val)
{
return format(reinterpret_cast<const std::basic_string<char> &>(val));
}

static std::basic_string<uint8_t, typename utility::CanUseStdCharTraits<uint8_t>::TraitsType> format(const utf16string& val)
{
return format(utility::conversions::utf16_to_utf8(val));
}
Expand All @@ -173,7 +108,7 @@ template<typename CharType>
class basic_ostream
{
public:
typedef char_traits<CharType> traits;
typedef typename utility::CanUseStdCharTraits<CharType>::TraitsType traits;
typedef typename traits::int_type int_type;
typedef typename traits::pos_type pos_type;
typedef typename traits::off_type off_type;
Expand Down Expand Up @@ -333,7 +268,7 @@ class basic_ostream
/// Write the specified string to the output stream.
/// </summary>
/// <param name="str">Input string.</param>
pplx::task<size_t> print(const std::basic_string<CharType>& str) const
pplx::task<size_t> print(const std::basic_string<CharType,traits>& str) const
{
pplx::task<size_t> result;
if (!_verify_and_return_task(details::_out_stream_msg, result)) return result;
Expand All @@ -344,7 +279,7 @@ class basic_ostream
}
else
{
auto sharedStr = std::make_shared<std::basic_string<CharType>>(str);
auto sharedStr = std::make_shared<std::basic_string<CharType,traits>>(str);
return helper()->m_buffer.putn_nocopy(sharedStr->c_str(), sharedStr->size()).then([sharedStr](size_t size) {
return size;
});
Expand Down
15 changes: 8 additions & 7 deletions Release/tests/functional/streams/memstream_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#include "cpprest/details/char_traits.h"
#include "stdafx.h"
#if defined(__cplusplus_winrt)
#include <wrl.h>
Expand All @@ -32,7 +33,7 @@ void streambuf_putc(StreamBufferType& wbuf)
{
VERIFY_IS_TRUE(wbuf.can_write());

std::basic_string<typename StreamBufferType::char_type> s;
std::basic_string<typename StreamBufferType::char_type,typename utility::CanUseStdCharTraits<typename StreamBufferType::char_type>::TraitsType> s;
s.push_back((typename StreamBufferType::char_type)0);
s.push_back((typename StreamBufferType::char_type)1);
s.push_back((typename StreamBufferType::char_type)2);
Expand Down Expand Up @@ -137,7 +138,7 @@ void streambuf_putn(StreamBufferType& wbuf)
{
VERIFY_IS_TRUE(wbuf.can_write());

std::basic_string<typename StreamBufferType::char_type> s;
std::basic_string<typename StreamBufferType::char_type,typename utility::CanUseStdCharTraits<typename StreamBufferType::char_type>::TraitsType> s;
s.push_back((typename StreamBufferType::char_type)0);
s.push_back((typename StreamBufferType::char_type)1);
s.push_back((typename StreamBufferType::char_type)2);
Expand Down Expand Up @@ -169,7 +170,7 @@ void streambuf_putn(concurrency::streams::rawptr_buffer<CharType>& wbuf)

typedef concurrency::streams::rawptr_buffer<CharType> StreamBufferType;

std::basic_string<CharType> s;
std::basic_string<CharType,typename CanUseStdCharTraits<CharType>::TraitsType> s;
s.push_back((CharType)0);
s.push_back((CharType)1);
s.push_back((CharType)2);
Expand Down Expand Up @@ -198,7 +199,7 @@ void streambuf_putn(concurrency::streams::container_buffer<CollectionType>& wbuf
typedef concurrency::streams::container_buffer<CollectionType> StreamBufferType;
typedef typename concurrency::streams::container_buffer<CollectionType>::char_type CharType;

std::basic_string<CharType> s;
std::basic_string<CharType, typename utility::CanUseStdCharTraits<CharType>::TraitsType> s;
s.push_back((CharType)0);
s.push_back((CharType)1);
s.push_back((CharType)2);
Expand Down Expand Up @@ -553,7 +554,7 @@ void streambuf_putn_getn(StreamBufferType& rwbuf)
VERIFY_IS_TRUE(rwbuf.can_read());
VERIFY_IS_TRUE(rwbuf.can_write());
VERIFY_IS_FALSE(rwbuf.is_eof());
std::basic_string<typename StreamBufferType::char_type> s;
std::basic_string<typename StreamBufferType::char_type, typename utility::CanUseStdCharTraits<typename StreamBufferType::char_type>::TraitsType> s;
s.push_back((typename StreamBufferType::char_type)0);
s.push_back((typename StreamBufferType::char_type)1);
s.push_back((typename StreamBufferType::char_type)2);
Expand Down Expand Up @@ -684,7 +685,7 @@ void streambuf_close_read_with_pending_read(StreamBufferType& rwbuf)
VERIFY_IS_TRUE(rwbuf.can_write());

// Write 4 characters
std::basic_string<typename StreamBufferType::char_type> s;
std::basic_string<typename StreamBufferType::char_type, typename utility::CanUseStdCharTraits<typename StreamBufferType::char_type>::TraitsType> s;
s.push_back((typename StreamBufferType::char_type)0);
s.push_back((typename StreamBufferType::char_type)1);
s.push_back((typename StreamBufferType::char_type)2);
Expand Down Expand Up @@ -726,7 +727,7 @@ void streambuf_close_write_with_pending_read(StreamBufferType& rwbuf)
VERIFY_IS_TRUE(rwbuf.can_write());

// Write 4 characters
std::basic_string<typename StreamBufferType::char_type> s;
std::basic_string<typename StreamBufferType::char_type, typename utility::CanUseStdCharTraits<typename StreamBufferType::char_type>::TraitsType> s;
s.push_back((typename StreamBufferType::char_type)0);
s.push_back((typename StreamBufferType::char_type)1);
s.push_back((typename StreamBufferType::char_type)2);
Expand Down
7 changes: 5 additions & 2 deletions Release/tests/functional/streams/stdstream_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "cpprest/filestream.h"
#include "cpprest/producerconsumerstream.h"
#include "cpprest/rawptrstream.h"
#include "cpprest/details/char_traits.h"

#if (!defined(_WIN32) || !defined(CPPREST_EXCLUDE_WEBSOCKETS)) && !defined(__cplusplus_winrt)
#include <boost/interprocess/streams/bufferstream.hpp>
Expand Down Expand Up @@ -303,7 +304,8 @@ SUITE(stdstreambuf_tests)

const std::streamsize iterations = 100;

const std::string the_alphabet("abcdefghijklmnopqrstuvwxyz");
const char *the_alphabet_characters = "abcdefghijklmnopqrstuvwxyz";
const std::basic_string<uint8_t,typename utility::CanUseStdCharTraits<uint8_t>::TraitsType> the_alphabet(reinterpret_cast<const uint8_t *>(the_alphabet_characters));

auto writer = pplx::create_task([ostream, iterations, the_alphabet]() {
auto os = ostream;
Expand Down Expand Up @@ -341,7 +343,8 @@ SUITE(stdstreambuf_tests)

const std::streamsize iterations = 100;

const std::string the_alphabet("abcdefghijklmnopqrstuvwxyz");
const char *the_alphabet_chars = "abcdefghijklmnopqrstuvwxyz";
const std::basic_string<uint8_t,typename CanUseStdCharTraits<uint8_t>::TraitsType> the_alphabet(reinterpret_cast<const uint8_t *>(the_alphabet_chars));

auto writer = pplx::create_task([ostream, iterations, the_alphabet]() {
auto os = ostream;
Expand Down