Skip to content

Commit

Permalink
JSON Update.
Browse files Browse the repository at this point in the history
Working on this for another project, but relevant here.
  • Loading branch information
staleyLANL committed Oct 2, 2024
1 parent 8ac1aef commit 7fe2c2b
Show file tree
Hide file tree
Showing 21 changed files with 359 additions and 282 deletions.
8 changes: 3 additions & 5 deletions simple-json/src/json-array.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class array : public std::vector<value> {
array(vector &&from) : vector(std::move(from)) { }

// constructor: from std::vector<T convertible to value>
template<class T, class = std::enable_if_t<std::is_convertible_v<T,value>>>
template<class T, class = require<convertible<T,value>>>
array(const std::vector<T> &from) :
vector(from.begin(), from.end())
{ }
Expand All @@ -28,9 +28,7 @@ class array : public std::vector<value> {
// Assignment
// ------------------------

template<
class T,
class = std::enable_if_t<std::is_assignable_v<vector, T &&>>>
template<class T, class = require<assignable<vector, T &&>>>
array &operator=(T &&from)
{
vector::operator=(std::forward<T>(from));
Expand All @@ -42,7 +40,7 @@ class array : public std::vector<value> {
// ------------------------

template<class T = void, class U = void>
auto read(std::istream &is, const int = as_literal::none)
auto /* std::string */ read(std::istream &is, const int = as_literal::none)
-> decltype(number().read<T,U>(is,0)); // SFINAE: need number::read<T,U>

void write(std::ostream & = std::cout, const int = 0, const int = -1) const;
Expand Down
2 changes: 1 addition & 1 deletion simple-json/src/json-boolean.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class boolean {
boolean() : b(false) { }

// from bool (exactly)
template<class T, class = std::enable_if_t<std::is_same_v<T,bool>>>
template<class T, class = require<same<T,bool>>>
boolean(const T &from) : b(from) { }

// ------------------------
Expand Down
13 changes: 4 additions & 9 deletions simple-json/src/json-chars.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@
// };
// At the time of this writing, g++ appears to have it, but clang++ does not.

template<class = SIMPLE_JSON_FLOATING>
template<class = JSON_FLOATING>
class chars {
std::string str;

// SIZE here is overkill, but should certainly be adequate
static inline const size_t SIZE = 1000;
// SIZE here is overkill, but should be adequate
static inline const usize SIZE = 1000;
static inline char buf[SIZE];

public:
Expand All @@ -30,12 +30,7 @@ class chars {
chars() { }

// from float, double, or long double
template<
class T,
class = std::enable_if_t<
detail::isfloating<T> && detail::invar<T,number::variant>
>
>
template<class T, class = require<floating<T> && allowed<T,number::variant>>>
chars(
const T &from,
const std::chars_format format = std::chars_format::general
Expand Down
12 changes: 6 additions & 6 deletions simple-json/src/json-detail-post.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,32 +12,32 @@ std::string many(
const int prefix, const int suffix, const std::string &context
) {
vec.clear();
detail::expect(is,prefix,context);
expect(is,prefix,context);
const bool thisLiteral = litFlags & as;
std::string text(1,char(prefix));

int ch;
while ((ch = (is >> std::ws).peek()) != EOF && ch != suffix) {
// comma
if (vec.size()) {
detail::expect(is,',',context);
expect(is,',',context);
if (thisLiteral)
text += ',';
}

// value or key:value, with value read in-place for efficiency
value *vptr = nullptr;
if constexpr (std::is_same_v<ELEMENT,value>) {
if constexpr (same<ELEMENT,value>) {
// array
// ELEMENT == value (from array's base std::vector<value>)
vec.push_back(value());
vptr = &vec.back();
} else {
// object
// ELEMENT == pair (from object's base std::vector<pair>)
string key; // <== json::string, not std::string, to handle escapes
json::key key; // <== json::key, not std::string, to handle escapes
key.read<T,U>(is);
detail::expect(is,':',context);
expect(is,':',context);
if (thisLiteral)
text += '"' + key + "\":";
vec.push_back(ELEMENT(key,value()));
Expand All @@ -49,7 +49,7 @@ std::string many(
: vptr->read<T,U>(is,litFlags);
}

detail::expect(is,suffix,context);
expect(is,suffix,context);
return thisLiteral ? text + char(suffix) : "";
} // many

Expand Down
98 changes: 34 additions & 64 deletions simple-json/src/json-detail-pre.hpp
Original file line number Diff line number Diff line change
@@ -1,54 +1,21 @@

namespace detail {

// -----------------------------------------------------------------------------
// Classes
// -----------------------------------------------------------------------------

namespace detail {

// inVariant
// count == number of times T is exactly the same as a type in a std::variant
// count == number of times T is exactly the same as a type in the variant
template<class, class>
struct inVariant { };

template<class T, class... As>
struct inVariant<T, std::variant<As...>>
{
static inline constexpr int count = (std::is_same_v<T,As> + ...);
};

// toVariant
// count == number of times T is convertible to a type in a std::variant
template<class, class>
struct toVariant { };

template<class T, class A>
struct toVariant<T, std::variant<A>>
struct inVariant<T,std::variant<As...>>
{
static inline constexpr int count =
std::is_convertible_v<T,A>;
static inline constexpr int count = (same<T,As> + ...);
};

template<class T, class A, class... As>
struct toVariant<T, std::variant<A,As...>>
{
static inline constexpr int count =
std::is_convertible_v<T,A> + toVariant<T,std::variant<As...>>::count;
};

// For brevity
// invar
// tovar
// isintegral
// isfloating
template<class T, class VARIANT>
inline constexpr bool invar = inVariant<T,VARIANT>::count == 1;
template<class T, class VARIANT>
inline constexpr bool tovar = toVariant<T,VARIANT>::count == 1;
template<class T>
inline constexpr bool isintegral = std::is_integral_v<std::decay_t<T>>;
template<class T>
inline constexpr bool isfloating = std::is_floating_point_v<std::decay_t<T>>;

// variant2tuple
template<class>
struct variant2tuple { };
Expand All @@ -58,7 +25,7 @@ struct variant2tuple<std::variant<As...>> { using type = std::tuple<As...>; };


// -----------------------------------------------------------------------------
// Functions
// Small functions
// -----------------------------------------------------------------------------

// prefix
Expand All @@ -73,7 +40,7 @@ inline void inside(
std::ostream &os, const char ch, // ch is always a ',' separator, for now
const int indentNSpaces, const int indentLevel, bool &first
) {
const std::string spaces(indentNSpaces*indentLevel,' ');
const std::string spaces(indentNSpaces*indentLevel, ' ');
first
? indentNSpaces ? (os << '\n' << spaces) : (os )
: indentNSpaces ? (os << ch << '\n' << spaces) : (os << ch);
Expand All @@ -85,14 +52,14 @@ inline void suffix(
std::ostream &os, const char ch,
const int indentNSpaces, const int indentLevel, const bool first
) {
const std::string spaces(indentNSpaces*indentLevel,' ');
const std::string spaces(indentNSpaces*indentLevel, ' ');
first
? indentNSpaces ? (os << ch) : (os << ch)
: indentNSpaces ? (os << '\n' << spaces << ch) : (os << ch);
}

// token
// First, skip white space. Then, read and return a token. Depending on the
// First, skip whitespace. Then, read and return a token. Depending on the
// template argument, the token must consist of either (1) alpha characters
// only, or (2) alphanumeric characters, '.', '-', or '+'.
template<bool justAlpha> // <== alpha characters only
Expand Down Expand Up @@ -132,12 +99,12 @@ inline void expect(std::istream &is, const int want, const std::string &context)
}
}

// caseless
// Case-insensitive string comparison.
// The old C language strcasecmp() is nonstandard. A modern, true case
// insensitive string comparison is actually a tougher nut to crack than
// meets the eye, but the following should suffice for our purposes.
inline bool caseless(const std::string &one, const std::string &two)
// nocasecmp
// Case-insensitive std::string comparison.
// The old C language strcasecmp() is nonstandard. A modern, true caseless
// std::string comparison would depend on, e.g., locale; but the following
// should suffice for our purposes.
inline bool nocasecmp(const std::string &one, const std::string &two)
{
return std::equal(
one.begin(), one.end(),
Expand All @@ -156,32 +123,32 @@ std::string many(


// -----------------------------------------------------------------------------
// readableAs<T>(string,target)
// readableAs<T>(std::string,target)
// -----------------------------------------------------------------------------

// Return value: are the string's contents *completely* readable as a T? (For
// example, "123" is readable as an int, among other things. However, "123foo"
// is not. Neither is, for example, 3.1415927, which in other interpretations
// might read as the int 3.) If so, then the so-read value will be placed into
// target. Note that we allow target to be of a different type than T. In some
// calls it is; in others, TARGET is (derived from) a std::variant with T as
// an alternative. This is why we have both T and TARGET.
// Are the std::string's contents *completely* readable as a T? (For example,
// "123" is readable as an int, among other things. However, "123foo" is not.
// Neither is, for example, 3.1415927, which in other interpretations might read
// as the int 3.) If so, then the so-read value will be placed into target. Note
// that we allow target to be of a different type than T. In some calls it is;
// in others, TARGET is (derived from) a std::variant with T as an alternative.
// This is why we have both T and TARGET.
template<class T, class TARGET>
bool readableAs(const std::string &str, TARGET &target)
{
if constexpr (std::is_same_v<T,int>) {
size_t idx;
if constexpr (same<T,int>) {
usize idx;
try { target = std::stoi(str,&idx); } catch (...) { return false; }
while (isspace(str.data()[idx])) ++idx;
return str.data()[idx] == '\0';
} else if constexpr (isfloating<T>) {
} else if constexpr (floating<T>) {
char *end; using quad = long double;
if constexpr (std::is_same_v<T,float >) target = strtof (str.data(),&end);
if constexpr (std::is_same_v<T,double>) target = strtod (str.data(),&end);
if constexpr (std::is_same_v<T,quad >) target = strtold(str.data(),&end);
if constexpr (same<T,float >) target = strtof (str.data(),&end);
if constexpr (same<T,double>) target = strtod (str.data(),&end);
if constexpr (same<T,quad >) target = strtold(str.data(),&end);
if (end == str.data()) return false; // no conversion performed
while (isspace(*end)) ++end; // white space is fine at the end...
return *end == '\0'; // ...but only white space, nothing else, before \0
while (isspace(*end)) ++end; // whitespace is fine at the end...
return *end == '\0'; // ...but only whitespace, nothing else, before \0
} else {
// Can we successfully read a T, with nothing of substance remaining?
// The second condition means e.g. that "3.1415927" wouldn't pass as
Expand All @@ -193,3 +160,6 @@ bool readableAs(const std::string &str, TARGET &target)
}

} // namespace detail

template<class T, class VARIANT>
inline constexpr bool allowed = detail::inVariant<T,VARIANT>::count;
Loading

0 comments on commit 7fe2c2b

Please sign in to comment.