Skip to content
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

Dev/issues/13 floating point num get #16

Merged
merged 9 commits into from
Oct 17, 2023
Merged
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
4 changes: 3 additions & 1 deletion src/estd/internal/impl/dynamic_array.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "../../allocators/fixed.h"
#include "../../allocators/handle_desc.h"
#include "../fwd/dynamic_array.h"
#include "../../traits/char_traits.h"

namespace estd { namespace internal { namespace impl {

Expand Down Expand Up @@ -187,8 +188,9 @@ struct dynamic_array_length<TAllocator, true, true>
{
const value_type* s = &hd.clock();
const size_type max_size = hd.size();
typedef estd::char_traits<value_type> char_traits;

size_type sz = strlen(s);
size_type sz = char_traits::length(s);

hd.cunlock();

Expand Down
127 changes: 105 additions & 22 deletions src/estd/internal/locale/iterated/num_get.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#pragma once

#include "../cbase.h"
#include "../numpunct.h"
#include "../fwd.h"
#include "../../ios_base.h"
#include "../../chooser.h"
Expand All @@ -26,15 +27,16 @@

namespace estd { namespace iterated {

template <unsigned base, typename TChar, class TLocale>
template <unsigned base, typename Char, class Locale>
struct num_get
{
typedef TLocale locale_type;
typedef TChar char_type;
typedef Locale locale_type;
typedef Char char_type;
typedef cbase<char_type, base, locale_type> cbase_type;
//typedef ctype<char_type, locale_type> ctype_type;
typedef typename cbase_type::optional_type optional_type;
typedef typename cbase_type::int_type int_type;
typedef numpunct<char_type, locale_type> numpunct_type;

enum state
{
Expand All @@ -50,17 +52,22 @@ struct num_get

struct _state
{
// NOTE: Not yet used
// DEBT: Once we do use it, consider optimizing out when not an integer
unsigned decimal_place_ : 8;

state state_ : 4;
//bool is_signed : 1;

_state() : state_(Start) //, is_signed(false)
_state() : decimal_place_(0), state_(Start) //, is_signed(false)
{}

} state_;

// 'true_type' means this is an integer
// 'false_type' means this is the unsigned flavor
template <bool positive, typename T>
inline static bool raise_and_add(int_type n, T& v, false_type)
inline static bool raise_and_add(int_type n, T& v, true_type, false_type)
{
// Undefined/bad state. Same as 'default' case switch
// DEBT: Logging this and other internal failures would be nice. In this case,
Expand All @@ -70,23 +77,54 @@ struct num_get
return estd::internal::raise_and_add(v, base, n);
}

// 'true_type' means this is the signed flavor
// #1 'true_type' means this is an integer
// #2 'true_type' means this is the signed flavor
template <bool positive, typename T>
ESTD_CPP_CONSTEXPR_RET static bool raise_and_add(int_type n, T& v, true_type)
ESTD_CPP_CONSTEXPR_RET static bool raise_and_add(int_type n, T& v, true_type, true_type)
{
return positive ?
estd::internal::raise_and_add(v, base, n) :
estd::internal::raise_and_sub(v, base, n);
}

// 'false_type' means this is a float or double
// 'true_type' means this is the signed flavor
template <bool positive, typename T>
bool raise_and_add(int_type n, T& v, false_type, true_type)
{
// TODO: Assert that base is 10

v *= base; // NOTE: Is always base 10 here
if(positive)
v += n;
else
v -= n;

if(state_.decimal_place_ != 0)
{
// DEBT: Side effects
++state_.decimal_place_;
}

return true;
}

///
/// @tparam positive
/// @tparam T
/// @param c
/// @param err
/// @param v
/// @return false if 'c' is generally parseable as part of a number, true otherwise
template <bool positive, typename T>
bool nominal(char_type c, ios_base::iostate& err, T& v)
{
typedef estd::bool_constant<numeric_limits<T>::is_integer> is_integer;
optional_type n = cbase_type::from_char(c);

if(n.has_value())
{
if (!raise_and_add<positive>(n.value(), v, is_signed<T>()))
if (!raise_and_add<positive>(n.value(), v, is_integer(), is_signed<T>()))
{
state_.state_ = Overflow;
err |= ios_base::failbit;
Expand All @@ -103,15 +141,58 @@ struct num_get

return false;
}
else
{
// TODO: Account for thousands separators
// NOTE: Probably should use is_floating_point here instead, though so far
// is_integer is in no danger of causing us grief
if(is_integer() == false && c == numpunct_type::decimal_point())
{
state_.decimal_place_ = 1;
return false;
}
}

return true;
}

// integer variety (noop)
template <typename T>
static ESTD_CPP_CONSTEXPR_RET bool finalize(T& v, true_type)
{
return {};
}

// floating point variety
template <typename T>
void finalize(T& v, false_type)
{
if(state_.decimal_place_ != 0)
{
T divider = 1;

// DEBT: Optimize ala
// https://stackoverflow.com/questions/18581560/any-way-faster-than-pow-to-compute-an-integer-power-of-10-in-c

for(int i = state_.decimal_place_; --i;)
divider *= base;

v /= divider;
}
}

// NOTE: This method never sets eof bit
// NOTE: Doing autoinit because compilers sometimes don't trust that we really do initialize
// v, causing "maybe-uninitialized" warning. To handle this, external parties may elect to
// init to zero instead of us.
// DEBT: Guard against availability of autoinit with something more cohesive than just __cplusplus
///
/// @tparam autoinit
/// @tparam T
/// @param c
/// @param err
/// @param v
/// @return true indicates processing is complete
template <
#if __cplusplus >= 201103L
bool autoinit = true,
Expand Down Expand Up @@ -189,20 +270,20 @@ struct num_get
}

// Just a bit of future proofing
num_get(TLocale) {}
num_get() {}
ESTD_CPP_CONSTEXPR_RET num_get(locale_type) {}
ESTD_CPP_DEFAULT_CTOR(num_get)
};



template <typename TChar, class TLocale>
struct num_get<0, TChar, TLocale>
template <typename Char, class Locale>
struct num_get<0, Char, Locale>
{
union
{
num_get<8, TChar, TLocale> base8;
num_get<10, TChar, TLocale> base10;
num_get<16, TChar, TLocale> base16;
num_get<8, Char, Locale> base8;
num_get<10, Char, Locale> base10;
num_get<16, Char, Locale> base16;
};
};

Expand All @@ -224,6 +305,8 @@ struct bool_get<TChar, TLocale, false> : num_get<2, TChar, TLocale>
v = false;
break;

// Yes, it is the case that '1' is expected to appear on parse failure for bool
// As per https://en.cppreference.com/w/cpp/locale/num_get/get 'Stage 3'
default:
err |= ios_base::failbit;
#if __has_cpp_attribute(fallthrough)
Expand Down Expand Up @@ -257,11 +340,11 @@ struct bool_get<TChar, TLocale, false> : num_get<2, TChar, TLocale>
};

// alpha version
template <typename TChar, class TLocale>
struct bool_get<TChar, TLocale, true>
template <typename Char, class Locale>
struct bool_get<Char, Locale, true>
{
typedef TChar char_type;
typedef TLocale locale_type;
typedef Char char_type;
typedef Locale locale_type;
typedef estd::numpunct<char_type, locale_type> numpunct_type;

enum state
Expand Down Expand Up @@ -310,9 +393,9 @@ struct bool_get<TChar, TLocale, true>

#if __cplusplus >= 201103L
// DEBT: Not instance-locale compat
bool_get(locale_type l) : names { numpunct_type::truename(), numpunct_type::falsename() }
constexpr explicit bool_get(locale_type l) : names { numpunct_type::truename(), numpunct_type::falsename() }
{}
bool_get() : names { numpunct_type::truename(), numpunct_type::falsename() }
constexpr bool_get() : names { numpunct_type::truename(), numpunct_type::falsename() }
{}
#else
bool_get() { set_names(locale_type()); }
Expand All @@ -325,4 +408,4 @@ struct bool_get<TChar, TLocale, true>

}}

#include "../../macro/pop.h"
#include "../../macro/pop.h"
48 changes: 40 additions & 8 deletions src/estd/internal/locale/num_get.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,32 +34,39 @@ class num_get
// incoming by way of str.
// Documentation [1] indicates to favor the latter
template <unsigned base, class TIncomingLocale, class T>
static iter_type get_signed_integer(iter_type i, iter_type end,
static iter_type get_signed_number(iter_type i, iter_type end,
ios_base::iostate& err, TIncomingLocale l, T& v)
{
iterated::num_get<base, char_type, TIncomingLocale> n(l);
typedef bool_constant<numeric_limits<T>::is_integer> is_integer;

// DEBT: iterated::num_get sometimes does this too
v = 0;

for(; i != end; ++i)
{
#if __cplusplus >= 201103L
if(n.template get<false>(*i, err, v)) return i;
if(n.template get<false>(*i, err, v))
#else
if(n.get(*i, err, v)) return i;
if(n.get(*i, err, v))
#endif
{
n.finalize(v, is_integer());
return i;
}
}

err |= ios_base::eofbit;
n.finalize(v, is_integer());
return i;
}


template <unsigned base, class TIncomingLocale, class T>
inline static iter_type get_signed_integer(iter_type i, iter_type end,
inline static iter_type get_signed_number(iter_type i, iter_type end,
ios_base&, ios_base::iostate& err, TIncomingLocale l, T& v)
{
return get_signed_integer<base>(i, end, err,l, v);
return get_signed_number<base>(i, end, err,l, v);
}
};

Expand All @@ -73,15 +80,24 @@ class num_get
inline static iter_type get_signed_integer(iter_type i, iter_type end,
ios_base::iostate& err, istream_type& str, T& v)
{
return _helper::template get_signed_integer<base>(
return _helper::template get_signed_number<base>(
i, end, str, err, str.getloc(),v);
}

template <unsigned base, class T>
inline static iter_type get_unsigned_integer(iter_type i, iter_type end,
ios_base::iostate& err, istream_type& str, T& v)
{
return _helper::template get_signed_integer<base>(
return _helper::template get_signed_number<base>(
i, end, str, err, str.getloc(), v);
}

// For float and double
template <class T>
inline static iter_type get_float(iter_type i, iter_type end,
ios_base::iostate& err, istream_type& str, T& v)
{
return _helper::template get_signed_number<10>(
i, end, str, err, str.getloc(), v);
}

Expand Down Expand Up @@ -115,6 +131,7 @@ class num_get
// https://stackoverflow.com/questions/9285657/sfinae-differentiation-between-signed-and-unsigned
// for the hybrid overload/SFINAE approach below

// unsigned int variety
// types after 'v':
// 'true_type' = is integer
// 'false_type' = unsigned
Expand Down Expand Up @@ -150,6 +167,7 @@ class num_get
}
}

// signed int variety
// types after 'v':
// 'true_type' = is integer
// 'true_type' = signed
Expand Down Expand Up @@ -184,6 +202,20 @@ class num_get
}
}

// Floating point variety
// types after 'v':
// 'false_type' = is integer
// 'true_type' = signed
template <class T>
static iter_type get(iter_type in, iter_type end,
istream_type& str, ios_base::iostate& err,
T& v,
estd::false_type, estd::true_type)
{
return get_float(in, end, err, str, v);
}

// bool variety
// types after 'v':
// 'true_type' = is integer
// 'false_type' = unsigned
Expand All @@ -208,7 +240,7 @@ class num_get
template <unsigned base, typename T>
inline iter_type get(iter_type in, iter_type end, ios_base::iostate& err, T& v) const
{
return _helper::template get_signed_integer<base>(in, end, err, locale_type(), v);
return _helper::template get_signed_number<base>(in, end, err, locale_type(), v);
}


Expand Down
Loading