Skip to content

Fix missing std::char_traits<uint8_t> (if it is in fact missing). #1812 #1829

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
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
50 changes: 12 additions & 38 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 @@ -56,55 +57,28 @@ namespace streams
/// <typeparam name="_CharType">
/// The data type of the basic element of the stream.
/// </typeparam>
namespace detail
{
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;
}
};
}
template<typename _CharType> struct char_traits : detail::char_traits<_CharType> {
};
#if !defined(_WIN32)
template<>
struct char_traits<unsigned char> : private std::char_traits<char>
{
public:
typedef unsigned char char_type;

using std::char_traits<char>::eof;
using std::char_traits<char>::int_type;
using std::char_traits<char>::off_type;
using std::char_traits<char>::pos_type;

static size_t length(const unsigned char* str)
{
return std::char_traits<char>::length(reinterpret_cast<const char*>(str));
}

static void assign(unsigned char& left, const unsigned char& right) { left = right; }
static unsigned char* assign(unsigned char* left, size_t n, unsigned char value)
{
return reinterpret_cast<unsigned char*>(
std::char_traits<char>::assign(reinterpret_cast<char*>(left), n, static_cast<char>(value)));
}

static unsigned char* copy(unsigned char* left, const unsigned char* right, size_t n)
{
return reinterpret_cast<unsigned char*>(
std::char_traits<char>::copy(reinterpret_cast<char*>(left), reinterpret_cast<const char*>(right), n));
}

static unsigned char* move(unsigned char* left, const unsigned char* right, size_t n)
{
return reinterpret_cast<unsigned char*>(
std::char_traits<char>::move(reinterpret_cast<char*>(left), reinterpret_cast<const char*>(right), n));
}

template <> struct char_traits<unsigned char> : detail::char_traits<unsigned char> {
typedef typename std::char_traits<char>::int_type int_type;
static int_type eof() { return std::char_traits<char>::eof(); }
static int_type requires_async() { return eof() - 1; }
};
#endif
Expand Down
102 changes: 102 additions & 0 deletions Release/include/cpprest/details/char_traits.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
//
// 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> struct IntTypeFor {
typedef typename std::conditional<std::is_unsigned<T>::value, unsigned long long int, long long int>::type type;
};
template <> struct IntTypeFor<char> {
typedef typename std::char_traits<char>::int_type type;
};
template <> struct IntTypeFor<unsigned char> {
typedef typename std::make_unsigned<typename std::char_traits<char>::int_type>::type type;
};

template <typename T> class DetailCharTraits
{
public:
using char_type = T;
using int_type = typename IntTypeFor<T>::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
92 changes: 79 additions & 13 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 @@ -60,30 +61,95 @@ class basic_istream_helper
concurrency::streams::streambuf<CharType> m_buffer;
};

template<typename CharType>
template<typename CharType, class Traits = typename utility::CanUseStdCharTraits<CharType>::TraitsType>
struct Value2StringFormatter
{
struct SanitizeInput
{
const std::basic_string<char> &operator () (const std::basic_string<char> &input)
{
return input;
}
template <class InputTraits> std::basic_string<char> operator () (const std::basic_string<unsigned char, InputTraits> &input)
{
return {reinterpret_cast<const char *>(input.c_str()), input.size()};
}
const char *operator () (const char *input) {
return input;
}
const char *operator () (const unsigned char *input)
{
return reinterpret_cast<const char *>(input);
}
template <class T> T operator () (T input)
{
return input;
}
};
struct GenerateFormatOutput
{
std::basic_string<CharType,Traits> &&operator() (std::basic_string<CharType,Traits> &&result)
{
return std::move(result);
}
std::basic_string<CharType,Traits> operator() (const std::basic_string<char> &intermediate)
{
return {reinterpret_cast<const CharType *>(intermediate.c_str()), intermediate.size()};
}
};
template<typename T>
static std::basic_string<CharType> format(const T& val)
static std::basic_string<CharType, Traits> format(const T& val)
{
typename std::conditional<
sizeof(CharType) == 1,
std::basic_ostringstream<char>,
std::basic_ostringstream<typename std::make_signed<CharType>::type>
>::type ss;
SanitizeInput sanitizer;
ss << sanitizer(val);
typename std::conditional<
sizeof(CharType) == 1,
std::basic_string<char>,
std::basic_string<typename std::make_signed<CharType>::type>
>::type str = ss.str();
GenerateFormatOutput generateFormatOutput;
return generateFormatOutput(std::move(str));
}
};

template<class Traits, typename T>
struct Value2StringFormatterUint8Format
{
std::basic_string<uint8_t, Traits> operator () (const T& val)
{
std::basic_ostringstream<CharType> ss;
std::basic_ostringstream<char> ss;
ss << val;
return ss.str();
return reinterpret_cast<const uint8_t*>(ss.str().c_str());
}
};

template <class Traits>
struct Value2StringFormatterUint8Format<Traits, std::basic_string<uint8_t,Traits>>
{
std::basic_string<uint8_t, Traits> operator () (
const std::basic_string<uint8_t, typename utility::CanUseStdCharTraits<uint8_t>::TraitsType>& val)
{
Value2StringFormatterUint8Format<Traits,std::basic_string<char>> format;
return format(reinterpret_cast<const std::basic_string<char>&>(val));
}
};

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

static std::basic_string<uint8_t> format(const utf16string& 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 Down Expand Up @@ -262,7 +328,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 @@ -273,7 +339,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 All @@ -294,7 +360,7 @@ class basic_ostream
if (!_verify_and_return_task(details::_out_stream_msg, result)) return result;
// TODO in the future this could be improved to have Value2StringFormatter avoid another unnecessary copy
// by putting the string on the heap before calling the print string overload.
return print(details::Value2StringFormatter<CharType>::format(val));
return print(details::Value2StringFormatter<CharType, traits>::format(val));
}

/// <summary>
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
Loading