Seed Core_IO from doc snapshot
This commit is contained in:
227
tjp/core/io/json/IO.h
Normal file
227
tjp/core/io/json/IO.h
Normal file
@@ -0,0 +1,227 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../IO.h"
|
||||
#include "../IO_dispatch.h"
|
||||
#include "../IO_.h"
|
||||
#include "../IO+config.h"
|
||||
|
||||
|
||||
#include <tjp/core/types/Types.h>
|
||||
#include <tjp/core/type_traits/is_mappish.hpp>
|
||||
#include <tjp/core/type_traits/is_string.hpp>
|
||||
#include <tjp/core/type_traits/is_iterable.hpp>
|
||||
|
||||
#include <tjp/core/string/to_string.h>
|
||||
#include <tjp/core/assert/debug_assert.h>
|
||||
|
||||
#include <tjp/core/string/to_string.hpp>
|
||||
#include <tjp/core/string/from_string.hpp>
|
||||
#include <tjp/core/system/System.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <optional>
|
||||
|
||||
|
||||
|
||||
//#ifdef DEBUG
|
||||
//#define IO_JSON_LOG std::cerr
|
||||
//#else
|
||||
#define IO_JSON_LOG if(false) std::cout
|
||||
//#endif
|
||||
|
||||
//#define IO_JSON_USE_LARGE_NUMBER_PROXY
|
||||
|
||||
namespace tjp::core::io::json {
|
||||
|
||||
using Size = u32;
|
||||
|
||||
typedef std::vector<char> Serialization;
|
||||
|
||||
// detects whether there is an appropriate intermediate
|
||||
// io function
|
||||
|
||||
template <typename IO, typename T, typename = void>
|
||||
struct has_io_json : std::false_type {};
|
||||
|
||||
template <typename IO, typename T>
|
||||
struct has_io_json<IO, T,
|
||||
std::void_t<decltype(io_json(std::declval<IO&>(),
|
||||
std::declval<T&>())) >> : std::true_type {};
|
||||
|
||||
|
||||
template<typename IO> void io_json(IO &io, bool &t) { io.intrinsic(t); }
|
||||
template<typename IO> void io_json(IO &io, u8 &t) { io.intrinsic(t); }
|
||||
template<typename IO> void io_json(IO &io, u16 &t) { io.intrinsic(t); }
|
||||
template<typename IO> void io_json(IO &io, u32 &t) { io.intrinsic(t); }
|
||||
template<typename IO> void io_json(IO &io, u64 &t) { io.intrinsic(t); }
|
||||
template<typename IO> void io_json(IO &io, u128 &t) { io.intrinsic(t); }
|
||||
|
||||
|
||||
template<typename IO> void io_json(IO &io, s8 &t) { io.intrinsic(t); }
|
||||
template<typename IO> void io_json(IO &io, char &t) { io.intrinsic(t); }
|
||||
template<typename IO> void io_json(IO &io, s16 &t) { io.intrinsic(t); }
|
||||
template<typename IO> void io_json(IO &io, s32 &t) { io.intrinsic(t); }
|
||||
template<typename IO> void io_json(IO &io, s64 &t) { io.intrinsic(t); }
|
||||
template<typename IO> void io_json(IO &io, s128 &t) { io.intrinsic(t); }
|
||||
|
||||
template<typename IO> void io_json(IO &io, r32 &t) { io.intrinsic(t); }
|
||||
template<typename IO> void io_json(IO &io, r64 &t) { io.intrinsic(t); }
|
||||
template<typename IO> void io_json(IO &io, r128 &t) { io.intrinsic(t); }
|
||||
|
||||
template<typename IO> void io_json(IO &io, std::string &t) { io.intrinsic(t); }
|
||||
template<typename IO> void io_json(IO &io, std::string_view &t) { io.intrinsic(t); }
|
||||
|
||||
#if defined(SYS_MAC) || defined(SYS_IOS)
|
||||
template<typename IO> void io_json(IO &io, unsigned long &t) { io.intrinsic(t); }
|
||||
template<typename IO> void io_json(IO &io, signed long &t) { io.intrinsic(t); }
|
||||
#endif
|
||||
|
||||
|
||||
template<typename T>
|
||||
struct Optional
|
||||
{
|
||||
T &v;
|
||||
bool write;
|
||||
} ;
|
||||
|
||||
template<typename T>
|
||||
Optional<T> optional(T &t, bool write=true) { return Optional<T> { t, write }; }
|
||||
|
||||
template<typename IO, typename T>
|
||||
void io_json(IO &io, Optional<T> &t)
|
||||
{
|
||||
static_assert(always_false<T>::value, "this should not be dispatched to");
|
||||
}
|
||||
|
||||
template<typename P, typename T>
|
||||
struct Pointer
|
||||
{
|
||||
P *p;
|
||||
bool write;
|
||||
} ;
|
||||
|
||||
typedef const char *KeyType;
|
||||
|
||||
template<typename IO, typename T>
|
||||
void io_ptr_allocate(IO &io, T *&t)
|
||||
{
|
||||
io.allocate(t);
|
||||
}
|
||||
|
||||
template<typename IO, typename T>
|
||||
void io_ptr_allocate_default(IO &io, T *&t)
|
||||
{
|
||||
t = new T();
|
||||
}
|
||||
|
||||
template<typename IO, typename P, typename T>
|
||||
void io_json(IO &io, Pointer<P, T> &t)
|
||||
{
|
||||
io.object("ptr", t);
|
||||
}
|
||||
|
||||
template<typename IO, typename T>
|
||||
void io_json(IO &io, T *&t)
|
||||
{
|
||||
auto p = io.template pointer<T>(t);
|
||||
io.any(p);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct AsKeyType {
|
||||
std::string value;
|
||||
|
||||
AsKeyType (const T &t)
|
||||
{
|
||||
value = core::to_string(t);
|
||||
}
|
||||
|
||||
operator KeyType()
|
||||
{
|
||||
return value.c_str();
|
||||
}
|
||||
} ;
|
||||
|
||||
template<>
|
||||
struct AsKeyType<std::string> {
|
||||
const std::string &value;
|
||||
AsKeyType (const std::string &value_) :
|
||||
value(value_)
|
||||
{
|
||||
}
|
||||
|
||||
operator KeyType()
|
||||
{
|
||||
return value.c_str();
|
||||
}
|
||||
} ;
|
||||
|
||||
template<>
|
||||
struct AsKeyType<KeyType> {
|
||||
KeyType value;
|
||||
AsKeyType (KeyType value_) :
|
||||
value(value_)
|
||||
{
|
||||
}
|
||||
|
||||
operator KeyType()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
} ;
|
||||
|
||||
template<typename T>
|
||||
AsKeyType<T> asKeyType(const T &t)
|
||||
{
|
||||
return AsKeyType<T>(t);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct Custom
|
||||
{
|
||||
T *p;
|
||||
} ;
|
||||
|
||||
template<typename IO, typename T>
|
||||
void io_json(IO &io, Custom<T> &t)
|
||||
{
|
||||
io.template dispatch_custom<>(io, *t.p);
|
||||
}
|
||||
|
||||
|
||||
template<typename IO, typename ...T>
|
||||
void io_json(IO &io, std::tuple<T...> &t)
|
||||
{
|
||||
io.tuple(t);
|
||||
}
|
||||
|
||||
//#ifdef SYS_APPLE
|
||||
// std::vector<bool> fixes
|
||||
|
||||
template<typename IO>
|
||||
void io_json_w(IO &io, const std::vector<bool>::const_iterator::reference t)
|
||||
{
|
||||
bool b = t;
|
||||
io.intrinsic(b);
|
||||
}
|
||||
|
||||
template<typename IO>
|
||||
void io_json_r(IO &io, std::vector<bool>::iterator::reference &t)
|
||||
{
|
||||
bool b;
|
||||
io.intrinsic(b);
|
||||
t = b;
|
||||
}
|
||||
|
||||
//#endif
|
||||
|
||||
} // namespace
|
||||
415
tjp/core/io/json/_tests/IO_json.cpp
Normal file
415
tjp/core/io/json/_tests/IO_json.cpp
Normal file
@@ -0,0 +1,415 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#include <tjp/core/io/json/IO.h>
|
||||
#include <tjp/core/io/json/in_sajson.h>
|
||||
#include <tjp/core/io/json/out.h>
|
||||
|
||||
#include <tjp/core/io/IO_encoder.h>
|
||||
|
||||
#include <tjp/core/testing/catch.hpp>
|
||||
|
||||
namespace tjp::core::io_json_test {
|
||||
|
||||
struct VS {
|
||||
int x;
|
||||
} ;
|
||||
|
||||
bool operator ==(const VS &l, const VS &r)
|
||||
{
|
||||
return l.x == r.x;
|
||||
}
|
||||
|
||||
std::ostream &operator <<(std::ostream &o, const VS &v)
|
||||
{
|
||||
return o << v.x;
|
||||
}
|
||||
|
||||
std::istream &operator >>(std::istream &i, VS &v)
|
||||
{
|
||||
return i >> v.x;
|
||||
}
|
||||
|
||||
struct VV : std::vector<int>
|
||||
{
|
||||
} ;
|
||||
|
||||
template<typename IO>
|
||||
void io_json(IO &io, VV &v)
|
||||
{
|
||||
std::string b = "b";
|
||||
io.object(
|
||||
"a",b
|
||||
);
|
||||
};
|
||||
|
||||
struct A {
|
||||
bool w;
|
||||
int x;
|
||||
std::string s;
|
||||
u64 _u64;
|
||||
s64 _s64;
|
||||
u128 _u128;
|
||||
s128 _s128;
|
||||
|
||||
std::vector<int> y;
|
||||
std::vector<std::vector<int>> z;
|
||||
std::vector<int> v_b;
|
||||
VS v_s;
|
||||
|
||||
bool operator ==(const A &rhs) const
|
||||
{
|
||||
return
|
||||
w == rhs.w &&
|
||||
x == rhs.x &&
|
||||
y == rhs.y &&
|
||||
z == rhs.z &&
|
||||
s == rhs.s &&
|
||||
v_b == rhs.v_b &&
|
||||
v_s == rhs.v_s &&
|
||||
|
||||
_u64 == rhs._u64 &&
|
||||
_s64 == rhs._s64 &&
|
||||
_u128 == rhs._u128 &&
|
||||
_s128 == rhs._s128
|
||||
;
|
||||
}
|
||||
} ;
|
||||
|
||||
struct B {
|
||||
int i;
|
||||
} ;
|
||||
|
||||
struct C {
|
||||
s32 i32;
|
||||
u32 u32;
|
||||
s64 i64;
|
||||
u64 u64;
|
||||
s128 i128;
|
||||
u128 u128;
|
||||
|
||||
bool operator ==(const C &rhs) const
|
||||
{
|
||||
return
|
||||
i32 == rhs.i32 &&
|
||||
u32 == rhs.u32 &&
|
||||
i64 == rhs.i64 &&
|
||||
u64 == rhs.u64 &&
|
||||
i128 == rhs.i128 &&
|
||||
u128 == rhs.u128
|
||||
;
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
template<typename IO>
|
||||
void io_json(IO &io, C &a)
|
||||
{
|
||||
io.object(
|
||||
"i32", a.i32,
|
||||
"u32", a.u32,
|
||||
"i64", a.i64,
|
||||
"u64", a.u64,
|
||||
"i128", a.i128,
|
||||
"u128", a.u128
|
||||
);
|
||||
};
|
||||
|
||||
template<typename IO>
|
||||
void io_json(IO &io, A &a)
|
||||
{
|
||||
io.object(
|
||||
"w", a.w,
|
||||
"x", a.x,
|
||||
"y", a.y,
|
||||
"z", a.z,
|
||||
"s", a.s,
|
||||
"_u64", a._u64,
|
||||
"_s64", a._s64,
|
||||
"_u128", a._u128,
|
||||
"_s128", a._s128,
|
||||
"v_b", makeBinaryEncoder(a.v_b),
|
||||
"v_s", makeStringEncoder(a.v_s)
|
||||
);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T make_number()
|
||||
{
|
||||
T t;
|
||||
char *b = (char *)&t;
|
||||
auto *e = b + sizeof(T);
|
||||
|
||||
int i=1;
|
||||
for (char *v=b; v!=e; ++v)
|
||||
*v = i++;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
SCENARIO("io json serialization", "[core::io]" )
|
||||
{
|
||||
GIVEN("a custom array with serializer")
|
||||
{
|
||||
auto v = VV();
|
||||
auto vs = io::json::serialize(v);
|
||||
auto vss = String(vs.begin(), vs.end());
|
||||
REQUIRE(vss == "{\"a\":\"b\"}");
|
||||
auto vs0 = io::json::deserialize<VV>(vs);
|
||||
REQUIRE(vs0.empty());
|
||||
}
|
||||
|
||||
GIVEN("a simple structure")
|
||||
{
|
||||
auto check_each = [](C &c) {
|
||||
auto vi32_ = io::json::deserialize<decltype(c.i32)>(io::json::serialize(c.i32));
|
||||
auto vi64_ = io::json::deserialize<decltype(c.i64)>(io::json::serialize(c.i64));
|
||||
auto vi128_ = io::json::deserialize<decltype(c.i128)>(io::json::serialize(c.i128));
|
||||
auto vu32_ = io::json::deserialize<decltype(c.u32)>(io::json::serialize(c.u32));
|
||||
auto vu64_ = io::json::deserialize<decltype(c.u64)>(io::json::serialize(c.u64));
|
||||
auto vu128_ = io::json::deserialize<decltype(c.u128)>(io::json::serialize(c.u128));
|
||||
|
||||
REQUIRE(vi32_ == c.i32);
|
||||
REQUIRE(vi64_ == c.i64);
|
||||
REQUIRE(vi128_ == c.i128);
|
||||
REQUIRE(vu32_ == c.u32);
|
||||
REQUIRE(vu64_ == c.u64);
|
||||
REQUIRE(vu128_ == c.u128);
|
||||
} ;
|
||||
|
||||
WHEN("interesting numbers 1")
|
||||
{
|
||||
C c = {
|
||||
.i32 = 0,
|
||||
.u32 = 0,
|
||||
.i64 = 0,
|
||||
.u64 = 0,
|
||||
.i128 = 0,
|
||||
.u128 = 0
|
||||
};
|
||||
|
||||
auto sc = io::json::serialize(c);
|
||||
auto c_ = io::json::deserialize<C>(std::move(sc));
|
||||
bool same = c == c_;
|
||||
REQUIRE(same);
|
||||
|
||||
check_each(c);
|
||||
}
|
||||
|
||||
WHEN("interesting numbers max")
|
||||
{
|
||||
C c = {
|
||||
.i32 = std::numeric_limits<s32>::max(),
|
||||
.u32 = std::numeric_limits<u32>::max(),
|
||||
.i64 = std::numeric_limits<s64>::max(),
|
||||
.u64 = std::numeric_limits<u64>::max(),
|
||||
.i128 = std::numeric_limits<s128>::max(),
|
||||
.u128 = std::numeric_limits<u128>::max()
|
||||
};
|
||||
|
||||
auto sc = io::json::serialize(c);
|
||||
auto c_ = io::json::deserialize<C>(std::move(sc));
|
||||
bool same = c == c_;
|
||||
|
||||
REQUIRE(same);
|
||||
|
||||
check_each(c);
|
||||
}
|
||||
|
||||
WHEN("interesting numbers min")
|
||||
{
|
||||
C c = {
|
||||
.i32 = std::numeric_limits<s32>::min(),
|
||||
.u32 = std::numeric_limits<u32>::min(),
|
||||
.i64 = std::numeric_limits<s64>::min(),
|
||||
.u64 = std::numeric_limits<u64>::min(),
|
||||
.i128 = std::numeric_limits<s128>::min(),
|
||||
.u128 = std::numeric_limits<u128>::min()
|
||||
};
|
||||
|
||||
auto sc = io::json::serialize(c);
|
||||
auto sc_ = std::string(sc.begin(), sc.end());
|
||||
auto c_ = io::json::deserialize<C>(std::move(sc));
|
||||
bool same = c == c_;
|
||||
|
||||
REQUIRE(same);
|
||||
|
||||
check_each(c);
|
||||
}
|
||||
|
||||
WHEN("interesting numbers max-1")
|
||||
{
|
||||
C c = {
|
||||
.i32 = std::numeric_limits<s32>::max()-1,
|
||||
.u32 = std::numeric_limits<u32>::max()-1,
|
||||
.i64 = std::numeric_limits<s64>::max()-1,
|
||||
.u64 = std::numeric_limits<u64>::max()-1,
|
||||
.i128 = std::numeric_limits<s128>::max()-1,
|
||||
.u128 = std::numeric_limits<u128>::max()-1
|
||||
};
|
||||
|
||||
auto sc = io::json::serialize(c);
|
||||
auto c_ = io::json::deserialize<C>(std::move(sc));
|
||||
bool same = c == c_;
|
||||
|
||||
REQUIRE(same);
|
||||
|
||||
check_each(c);
|
||||
}
|
||||
|
||||
WHEN("interesting numbers min+1")
|
||||
{
|
||||
C c = {
|
||||
.i32 = std::numeric_limits<s32>::min()+1,
|
||||
.u32 = std::numeric_limits<u32>::min()+1,
|
||||
.i64 = std::numeric_limits<s64>::min()+1,
|
||||
.u64 = std::numeric_limits<u64>::min()+1,
|
||||
.i128 = std::numeric_limits<s128>::min()+1,
|
||||
.u128 = std::numeric_limits<u128>::min()+1
|
||||
};
|
||||
|
||||
auto sc = io::json::serialize(c);
|
||||
auto c_ = io::json::deserialize<C>(std::move(sc));
|
||||
bool same = c == c_;
|
||||
|
||||
REQUIRE(same);
|
||||
|
||||
check_each(c);
|
||||
}
|
||||
|
||||
WHEN("interesting numbers max+1")
|
||||
{
|
||||
C c = {
|
||||
.i32 = std::numeric_limits<s16>::max()+1,
|
||||
.u32 = std::numeric_limits<u16>::max()+1,
|
||||
.i64 = s64(std::numeric_limits<s32>::max())+1,
|
||||
.u64 = u64(std::numeric_limits<u32>::max())+1,
|
||||
.i128 = s128(std::numeric_limits<s64>::max())+1,
|
||||
.u128 = u128(std::numeric_limits<u64>::max())+1
|
||||
};
|
||||
|
||||
auto sc = io::json::serialize(c);
|
||||
auto c_ = io::json::deserialize<C>(std::move(sc));
|
||||
bool same = c == c_;
|
||||
|
||||
REQUIRE(same);
|
||||
|
||||
check_each(c);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
GIVEN("a structure")
|
||||
{
|
||||
A a = {
|
||||
true, 13,
|
||||
std::string("hi"),
|
||||
make_number<u64>(), make_number<s64>(),
|
||||
make_number<u128>(), make_number<s128>(),
|
||||
{ 2, 3, 4 },
|
||||
{ { 1, 2, 3 }, { 4, 5, 6 }},
|
||||
{ 1, 2, 3, 4, 5, 6, 7, 8 },
|
||||
{ 1 }
|
||||
};
|
||||
|
||||
WHEN("base64 works")
|
||||
{
|
||||
A b;
|
||||
|
||||
fromBase64(toBase64((char *)&a._s128, sizeof(a._s128)), (char *)&b._s128, sizeof(b._s128));
|
||||
fromBase64(toBase64((char *)&a._u128, sizeof(a._u128)), (char *)&b._u128, sizeof(b._u128));
|
||||
fromBase64(toBase64((char *)&a._s64, sizeof(a._s64)), (char *)&b._s64, sizeof(b._s64));
|
||||
fromBase64(toBase64((char *)&a._u64, sizeof(a._u64)), (char *)&b._u64, sizeof(b._u64));
|
||||
REQUIRE(a._s128 == b._s128);
|
||||
REQUIRE(a._u128 == b._u128);
|
||||
REQUIRE(a._s64 == b._s64);
|
||||
REQUIRE(a._u64 == b._u64);
|
||||
}
|
||||
|
||||
WHEN("serialize and deserialize, is same")
|
||||
{
|
||||
auto sa = io::json::serialize(a);
|
||||
auto saX = std::string(sa.data(), sa.size());
|
||||
|
||||
auto a_ = io::json::deserialize<A>(std::move(sa));
|
||||
bool same = a == a_;
|
||||
|
||||
REQUIRE(same);
|
||||
|
||||
auto sa2 = io::json::serialize(a_);
|
||||
auto sa2X = std::string(sa2.data(), sa2.size());
|
||||
|
||||
REQUIRE(saX == sa2X);
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("a structure empty")
|
||||
{
|
||||
A a = { true, 13 };
|
||||
|
||||
WHEN("serialize and deserialize, is same")
|
||||
{
|
||||
auto sa = io::json::serialize(a);
|
||||
auto a_ = io::json::deserialize<A>(std::move(sa));
|
||||
|
||||
bool same = a == a_;
|
||||
|
||||
REQUIRE(same);
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("a vector of bool")
|
||||
{
|
||||
typedef std::vector<bool> Bools;
|
||||
Bools a { true, false, true, true };
|
||||
|
||||
WHEN("serialize and deserialize")
|
||||
{
|
||||
auto sa = io::json::serialize(a);
|
||||
auto a_ = io::json::deserialize<Bools>(std::move(sa));
|
||||
|
||||
THEN("is same")
|
||||
{
|
||||
REQUIRE(a_ == a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("an empty vector")
|
||||
{
|
||||
typedef std::vector<int> V;
|
||||
V a;
|
||||
|
||||
WHEN("serialize and deserialize")
|
||||
{
|
||||
auto sa = io::json::serialize(a);
|
||||
auto a_ = io::json::deserialize<V>(std::move(sa));
|
||||
|
||||
THEN("is same")
|
||||
{
|
||||
REQUIRE(a_ == a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("an empty string")
|
||||
{
|
||||
typedef std::string V;
|
||||
V a;
|
||||
|
||||
WHEN("serialize and deserialize")
|
||||
{
|
||||
auto sa = io::json::serialize(a);
|
||||
auto a_ = io::json::deserialize<V>(std::move(sa));
|
||||
|
||||
THEN("is same")
|
||||
{
|
||||
REQUIRE(a_ == a);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
39
tjp/core/io/json/in_file.hpp
Normal file
39
tjp/core/io/json/in_file.hpp
Normal file
@@ -0,0 +1,39 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fstream>
|
||||
|
||||
namespace tjp::core::io::json {
|
||||
|
||||
template<typename T>
|
||||
bool fromFile (const std::string &fileName, T &t)
|
||||
{
|
||||
std::ifstream inFile;
|
||||
|
||||
inFile.open(fileName);
|
||||
if (!inFile)
|
||||
return false;
|
||||
|
||||
fromStream(inFile, t);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T fromFile (const std::string &fileName)
|
||||
{
|
||||
std::ifstream inFile;
|
||||
|
||||
inFile.open(fileName);
|
||||
if (!inFile)
|
||||
throw Exception { "File could not be opened" };
|
||||
|
||||
T t;
|
||||
fromStream(inFile, t);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
831
tjp/core/io/json/in_sajson.h
Normal file
831
tjp/core/io/json/in_sajson.h
Normal file
@@ -0,0 +1,831 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "IO.h"
|
||||
|
||||
#include <iostream>
|
||||
#include "../../json/sajson.h"
|
||||
#include <set>
|
||||
#include <tjp/core/string/from_string.h>
|
||||
#include <tjp/core/io/memstream.h>
|
||||
#include <tjp/core/ptr/Ptr.hpp>
|
||||
#include <tjp/core/debug/Debug.h>
|
||||
#include <tjp/core/rtti/RTTI.hpp>
|
||||
#include "../Exception.h"
|
||||
|
||||
#include <tjp/core/system/System.h>
|
||||
|
||||
#ifdef IO_JSON_USE_LARGE_NUMBER_PROXY
|
||||
#include <tjp/core/bases/Base64.h>
|
||||
#endif
|
||||
|
||||
//#ifdef DEBUG
|
||||
//#define IO_JSON_LOG std::cerr
|
||||
//#else
|
||||
#define IO_JSON_LOG if(false) std::cout
|
||||
//#endif
|
||||
|
||||
namespace tjp::core::io::json {
|
||||
|
||||
template<typename IO, typename T>
|
||||
void io_json_r(IO &io, T &t);
|
||||
|
||||
template<typename IO, typename T>
|
||||
void io_json_r_dispatch(IO &io, T &t);
|
||||
|
||||
typedef const char *KeyType;
|
||||
|
||||
struct json_in {
|
||||
Serialization underlying;
|
||||
typedef const sajson::document Document;
|
||||
Document document;
|
||||
|
||||
typedef const sajson::value Node;
|
||||
|
||||
struct Current {
|
||||
Node node;
|
||||
|
||||
Current(const Node &node_) :
|
||||
node(node_)
|
||||
{
|
||||
}
|
||||
|
||||
int keyIndex = 0;
|
||||
bool objectKeysFound = false;
|
||||
std::vector<std::string> objectKeys;
|
||||
|
||||
size_t array_size () {
|
||||
return node.get_length();
|
||||
}
|
||||
|
||||
const std::vector<std::string> &object_keys ()
|
||||
{
|
||||
if (!objectKeysFound)
|
||||
{
|
||||
auto length = node.get_length();
|
||||
for (auto i = 0; i<length; ++i)
|
||||
{
|
||||
auto key = node.get_object_key(i);
|
||||
objectKeys.push_back(key.as_string());
|
||||
}
|
||||
|
||||
objectKeysFound = true;
|
||||
}
|
||||
|
||||
return objectKeys;
|
||||
}
|
||||
|
||||
bool isValid()
|
||||
{
|
||||
return node.get_type() != sajson::TYPE_NULL;
|
||||
}
|
||||
|
||||
bool is_null ()
|
||||
{
|
||||
return node.get_type() == sajson::TYPE_NULL;
|
||||
}
|
||||
|
||||
Node operator[] (int i)
|
||||
{
|
||||
if (i >= node.get_length())
|
||||
throw Exception { "Index out of bounds" };
|
||||
|
||||
return node.get_array_element(i);
|
||||
}
|
||||
|
||||
Node operator[] (const std::string &i)
|
||||
{
|
||||
auto key = sajson::string(i.c_str(), i.length());
|
||||
auto element = node.find_object_key(key);
|
||||
if (element >= node.get_length())
|
||||
throw Exception { "Key does not exist" };
|
||||
|
||||
return node.get_object_value(element);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T get_int()
|
||||
{
|
||||
T t;
|
||||
if (!node.get_integer_value(t))
|
||||
{
|
||||
debug_assert(false);
|
||||
throw Exception { "Value not convertible" };
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
#ifdef IO_JSON_USE_LARGE_NUMBER_PROXY
|
||||
|
||||
template<typename T>
|
||||
T get_proxy()
|
||||
{
|
||||
T t;
|
||||
fromBase64(
|
||||
node.as_cstring(),
|
||||
node.get_string_length(),
|
||||
(char *)&t,
|
||||
sizeof(t)
|
||||
);
|
||||
|
||||
return t;
|
||||
}
|
||||
#endif
|
||||
|
||||
// s64 get_int64()
|
||||
// {
|
||||
// s64 v = -1;
|
||||
// if (!node.get_int53_value(&v))
|
||||
// {
|
||||
// debug_assert(false);
|
||||
// // throw something
|
||||
// }
|
||||
// return v;
|
||||
// }
|
||||
//
|
||||
// s64 get_int128()
|
||||
// {
|
||||
// s64 v = -1;
|
||||
// if (!node.get_int53_value(&v))
|
||||
// {
|
||||
// debug_assert(false);
|
||||
// // throw something
|
||||
// }
|
||||
// return v;
|
||||
// }
|
||||
|
||||
double get_double()
|
||||
{
|
||||
return node.get_number_value();
|
||||
}
|
||||
|
||||
std::string_view get_utf8()
|
||||
{
|
||||
return node.as_string();
|
||||
}
|
||||
|
||||
bool get_bool ()
|
||||
{
|
||||
return node.get_type() == sajson::TYPE_TRUE;
|
||||
}
|
||||
} ;
|
||||
|
||||
std::list<Current> current;
|
||||
|
||||
json_in(Serialization &&serialization) :
|
||||
underlying(std::move(serialization)),
|
||||
document(sajson::parse(
|
||||
sajson::dynamic_allocation(),
|
||||
sajson::mutable_string_view(underlying.size(), (char *)underlying.data())
|
||||
))
|
||||
{
|
||||
current.emplace_back(document.get_root());
|
||||
}
|
||||
|
||||
size_t array_size()
|
||||
{
|
||||
auto &doc = current.back();
|
||||
return doc.array_size();
|
||||
}
|
||||
|
||||
std::vector<std::string> object_keys()
|
||||
{
|
||||
auto &doc = current.back();
|
||||
return doc.object_keys();
|
||||
}
|
||||
|
||||
void key(const KeyType &key)
|
||||
{
|
||||
current.emplace_back(current.back()[key]);
|
||||
}
|
||||
|
||||
// bool hasKey(const KeyType &key_)
|
||||
// {
|
||||
// std::string key(key_);
|
||||
// auto &keys = current.back().object_keys();
|
||||
// return std::find(keys.begin(), keys.end(), key) != keys.end();
|
||||
// }
|
||||
|
||||
bool hasKeyValue(const KeyType &key_)
|
||||
{
|
||||
std::string key(key_);
|
||||
auto &keys = current.back().object_keys();
|
||||
auto i= std::find(keys.begin(), keys.end(), key);
|
||||
if (i == keys.end())
|
||||
return false;
|
||||
|
||||
return current.back()[*i].get_type() != sajson::TYPE_NULL;
|
||||
}
|
||||
|
||||
void next_array_key()
|
||||
{
|
||||
auto index = current.back().keyIndex++;
|
||||
current.emplace_back(current.back()[index]);
|
||||
}
|
||||
|
||||
void endKey()
|
||||
{
|
||||
current.pop_back();
|
||||
}
|
||||
|
||||
void begin_element ()
|
||||
{
|
||||
}
|
||||
|
||||
void begin_object()
|
||||
{
|
||||
begin_element();
|
||||
}
|
||||
|
||||
void end_element()
|
||||
{
|
||||
// current.pop_back();
|
||||
}
|
||||
|
||||
void end_object()
|
||||
{
|
||||
end_element();
|
||||
}
|
||||
|
||||
void begin_array()
|
||||
{
|
||||
begin_element();
|
||||
}
|
||||
|
||||
void end_array()
|
||||
{
|
||||
end_element();
|
||||
}
|
||||
|
||||
void clearKey ()
|
||||
{
|
||||
}
|
||||
|
||||
template<typename V>
|
||||
void v_int(V &v)
|
||||
{
|
||||
v = current.back().get_int<V>();
|
||||
}
|
||||
|
||||
#ifdef IO_JSON_USE_LARGE_NUMBER_PROXY
|
||||
|
||||
template<typename V>
|
||||
void v_proxy(V &v)
|
||||
{
|
||||
v = current.back().get_proxy<V>();
|
||||
}
|
||||
|
||||
template<>
|
||||
void v_int(s64 &v)
|
||||
{
|
||||
return v_proxy(v);
|
||||
}
|
||||
|
||||
template<>
|
||||
void v_int(u64 &v)
|
||||
{
|
||||
return v_proxy(v);
|
||||
}
|
||||
|
||||
template<>
|
||||
void v_int(s128 &v)
|
||||
{
|
||||
return v_proxy(v);
|
||||
}
|
||||
|
||||
template<>
|
||||
void v_int(u128 &v)
|
||||
{
|
||||
return v_proxy(v);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
template<typename V>
|
||||
void v_string(V &v)
|
||||
{
|
||||
v = current.back().get_utf8();
|
||||
}
|
||||
|
||||
template<typename V>
|
||||
void v_double(V &v)
|
||||
{
|
||||
v = current.back().get_double();
|
||||
}
|
||||
|
||||
template<typename V>
|
||||
void v_bool(V &v)
|
||||
{
|
||||
v = current.back().get_bool();
|
||||
}
|
||||
|
||||
void v(u8 &v) { v_int(v); }
|
||||
void v(u16 &v) { v_int(v); }
|
||||
void v(u32 &v) { v_int(v); }
|
||||
void v(u64 &v) { v_int(v); }
|
||||
void v(u128 &v) { v_int(v); }
|
||||
void v(s8 &v) { v_int(v); }
|
||||
void v(char &v) { v_int(v); }
|
||||
void v(s16 &v) { v_int(v); }
|
||||
void v(s32 &v) { v_int(v); }
|
||||
void v(s64 &v) { v_int(v); }
|
||||
void v(s128 &v) { v_int(v); }
|
||||
void v(r32 &v) { v_double(v); }
|
||||
void v(r64 &v) { v_double(v); }
|
||||
void v(r128 &v) { v_double(v); }
|
||||
void v(bool &v) { v_bool(v); }
|
||||
void v(std::string &v) { v_string(v); }
|
||||
|
||||
#if defined(SYS_MAC) || defined(SYS_IOS)
|
||||
void v(unsigned long &v) { v_int(v); }
|
||||
void v(signed long &v) { v_int(v); }
|
||||
#endif
|
||||
|
||||
bool is_null()
|
||||
{
|
||||
return current.back().is_null();
|
||||
}
|
||||
} ;
|
||||
|
||||
// ------------------------
|
||||
|
||||
struct ReaderEmptyBaseClass {
|
||||
template<typename IO, typename T>
|
||||
void dispatch_custom(IO &io, T &t) {}
|
||||
};
|
||||
|
||||
struct ReaderAllocatorDefault {
|
||||
template<typename IO, typename T>
|
||||
void allocate(IO &io, T &t)
|
||||
{
|
||||
io_ptr_allocate_default(io, t);
|
||||
}
|
||||
} ;
|
||||
|
||||
template<typename Stream_, typename Base=ReaderEmptyBaseClass, typename Allocator=ReaderAllocatorDefault>
|
||||
class Reader : public Base
|
||||
{
|
||||
public:
|
||||
typedef Stream_ Stream;
|
||||
|
||||
Stream f;
|
||||
u8 count = 0;
|
||||
|
||||
public:
|
||||
Allocator allocator;
|
||||
|
||||
template<typename T>
|
||||
void allocate(T &t)
|
||||
{
|
||||
allocator.allocate(*this, t);
|
||||
}
|
||||
|
||||
auto partial(const Serialization &s)
|
||||
{
|
||||
using ReaderT = Reader<json_in, Base>;
|
||||
return strong<ReaderT>(std::move(Serialization(s)));
|
||||
}
|
||||
|
||||
public:
|
||||
template<class... Args>
|
||||
Reader (Args&&... args) :
|
||||
f(std::forward<Args>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return f.good();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void intrinsic(T &t)
|
||||
{
|
||||
read(t);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void intrinsic_string(T &t)
|
||||
{
|
||||
std::string s;
|
||||
read(s);
|
||||
t = s;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void intrinsic_string(std::string &t)
|
||||
{
|
||||
read(t);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void any(T &t)
|
||||
{
|
||||
dispatch_any(t);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void any(T &&t)
|
||||
{
|
||||
dispatch_any(t);
|
||||
}
|
||||
|
||||
template<typename ... T>
|
||||
void object(T&&... t)
|
||||
{
|
||||
object_begin();
|
||||
object_(t...);
|
||||
object_end();
|
||||
}
|
||||
|
||||
void object_begin()
|
||||
{
|
||||
f.begin_object();
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
void object_kf(KeyType k, F &&q)
|
||||
{
|
||||
f.key(k);
|
||||
q();
|
||||
f.endKey();
|
||||
};
|
||||
|
||||
template<typename ... T>
|
||||
void object_kv(T&&... t)
|
||||
{
|
||||
object_(t...);
|
||||
}
|
||||
|
||||
void object_end()
|
||||
{
|
||||
f.end_object();
|
||||
}
|
||||
|
||||
template<typename ... T>
|
||||
void array(T&&... t)
|
||||
{
|
||||
f.begin_array();
|
||||
array_(t...);
|
||||
f.end_array();
|
||||
}
|
||||
|
||||
template<typename ...T>
|
||||
void tuple(std::tuple<T...> &t)
|
||||
{
|
||||
f.begin_array();
|
||||
std::apply([this](auto &...x){(av(x), ...);}, t);
|
||||
f.end_array();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void on_dispatch_any(T &t)
|
||||
{
|
||||
on_any(t);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void on_dispatch_custom(T &t)
|
||||
{
|
||||
Base::dispatch_custom(*this, t);
|
||||
}
|
||||
|
||||
protected:
|
||||
// ----- read
|
||||
|
||||
template<typename T>
|
||||
void read(T &t)
|
||||
{
|
||||
f.v(t);
|
||||
}
|
||||
|
||||
// ----- dispatch out and in
|
||||
|
||||
template<typename T>
|
||||
void dispatch_any(T &t)
|
||||
{
|
||||
IO_JSON_LOG << "dispatch_any " << type_id<T>().name() << std::endl;
|
||||
|
||||
io_json_r_dispatch(*this, t);
|
||||
}
|
||||
|
||||
// kv & av -----------
|
||||
|
||||
template<typename V>
|
||||
void kv(KeyType k, V &v)
|
||||
{
|
||||
f.key(k);
|
||||
any(v);
|
||||
f.endKey();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void kv(KeyType k, Optional<T> &t)
|
||||
{
|
||||
if (f.hasKeyValue(k))
|
||||
{
|
||||
kv(k, t.v);
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void kv(KeyType k, std::optional<T> &t)
|
||||
{
|
||||
if (f.hasKeyValue(k))
|
||||
{
|
||||
t.emplace();
|
||||
kv(k, *t);
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
template<typename B, typename T>
|
||||
void kv(KeyType k, Pointer<B, T> &t)
|
||||
{
|
||||
if (f.hasKeyValue(k))
|
||||
{
|
||||
allocator.allocate(*this, *t.p);
|
||||
kv(k, **t.p);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void av(T &v)
|
||||
{
|
||||
f.next_array_key();
|
||||
any(v);
|
||||
f.endKey();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void av_no_ref(T v)
|
||||
{
|
||||
f.next_array_key();
|
||||
any(v);
|
||||
f.endKey();
|
||||
}
|
||||
|
||||
// -- handling different containers --
|
||||
|
||||
public:
|
||||
size_t array_begin()
|
||||
{
|
||||
f.begin_array();
|
||||
return f.array_size();
|
||||
}
|
||||
|
||||
void array_end()
|
||||
{
|
||||
f.end_array();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void on_vector_begin (T &t)
|
||||
{
|
||||
auto count = array_begin();
|
||||
io_resize_vector(t, count);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void on_vector_end (T &t)
|
||||
{
|
||||
array_end();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void on_vector_value(T &t)
|
||||
{
|
||||
av(t);
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
void array_values(size_t size, F &&fun)
|
||||
{
|
||||
for (auto i=0;i<size;++i)
|
||||
fun();
|
||||
}
|
||||
|
||||
template<typename K, typename F>
|
||||
void object_values(F &&fun)
|
||||
{
|
||||
f.begin_object();
|
||||
|
||||
auto keys = f.object_keys();
|
||||
for (auto &k: keys)
|
||||
{
|
||||
typedef K Key;
|
||||
Key key = core::from_string<Key>(k);
|
||||
|
||||
f.key(k);
|
||||
fun(k);
|
||||
f.endKey();
|
||||
}
|
||||
|
||||
f.end_object();
|
||||
}
|
||||
|
||||
protected:
|
||||
template<typename T>
|
||||
void on_vector(T &t)
|
||||
{
|
||||
IO_JSON_LOG << "many " << type_id<T>().name() << std::endl;
|
||||
on_vector_begin(t);
|
||||
|
||||
for (auto &v : t)
|
||||
on_vector_value(v);
|
||||
|
||||
on_vector_end(t);
|
||||
}
|
||||
|
||||
void on_vector(std::vector<bool> &t)
|
||||
{
|
||||
typedef std::vector<bool> T;
|
||||
IO_JSON_LOG << "many " << type_id<T>().name() << std::endl;
|
||||
|
||||
on_vector_begin(t);
|
||||
|
||||
for (auto i = t.begin(); i!=t.end(); ++i)
|
||||
av_no_ref<typename T::iterator::reference>(*i);
|
||||
|
||||
on_vector_end(t);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void on_map(T &t)
|
||||
{
|
||||
IO_JSON_LOG << "map " << type_id<T>().name() << std::endl;
|
||||
|
||||
f.begin_object();
|
||||
|
||||
auto keys = f.object_keys();
|
||||
for (auto &k: keys)
|
||||
{
|
||||
typedef typename T::key_type Key;
|
||||
Key key = core::from_string<Key>(k);
|
||||
|
||||
typename T::mapped_type value;
|
||||
|
||||
kv(asKeyType(k), value);
|
||||
t.emplace(key, value);
|
||||
}
|
||||
|
||||
f.end_object();
|
||||
}
|
||||
|
||||
template<typename V, typename T>
|
||||
void on_set(T &t)
|
||||
{
|
||||
IO_JSON_LOG << "many " << type_id<T>().name() << std::endl;
|
||||
|
||||
f.begin_array();
|
||||
|
||||
auto count = f.array_size();
|
||||
|
||||
for (auto i=0; i<count; ++i)
|
||||
{
|
||||
V v;
|
||||
|
||||
av(v);
|
||||
t.insert(v);
|
||||
}
|
||||
|
||||
f.end_array();
|
||||
}
|
||||
|
||||
// void on_any(variant::Variant &t)
|
||||
// {
|
||||
// on_variant(t);
|
||||
// }
|
||||
//
|
||||
// -- dispatching different containers --
|
||||
|
||||
public:
|
||||
template<typename T, typename std::enable_if<!is_iterable<T>::value || is_stringish<T>::value, T>::type* = nullptr>
|
||||
void on_any(T &t)
|
||||
{
|
||||
io_dispatch_failed(*this, t);
|
||||
}
|
||||
|
||||
template<typename V, typename S>
|
||||
void on_any(std::set<V,S> &t)
|
||||
{
|
||||
on_set<V>(t);
|
||||
}
|
||||
|
||||
template<typename T, typename std::enable_if<is_iterable<T>::value && !is_mappish<T>::value && !is_stringish<T>::value, T>::type* = nullptr>
|
||||
void on_any(T &t)
|
||||
{
|
||||
on_vector(t);
|
||||
}
|
||||
|
||||
template<typename T, typename std::enable_if<is_iterable<T>::value && is_mappish<T>::value, T>::type* = nullptr>
|
||||
void on_any(T &t)
|
||||
{
|
||||
on_map(t);
|
||||
}
|
||||
|
||||
protected:
|
||||
// ---- handling object & array ----
|
||||
|
||||
void object_()
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void object__(KeyType key, T &t)
|
||||
{
|
||||
IO_JSON_LOG << "key " << key << std::endl;
|
||||
kv(key, t);
|
||||
}
|
||||
|
||||
template<typename ...Args>
|
||||
void object_(Args&& ...args)
|
||||
{
|
||||
call_expand_2(
|
||||
[&](auto && ...args) { object__(std::forward<decltype(args)>(args)...); },
|
||||
std::forward<Args>(args)...
|
||||
);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void array__(T &t)
|
||||
{
|
||||
av(t);
|
||||
}
|
||||
|
||||
template<typename ...Args>
|
||||
void array_(Args&& ...args)
|
||||
{
|
||||
call_expand_1(
|
||||
[&](auto && ...args) { array__(std::forward<decltype(args)>(args)...); },
|
||||
std::forward<Args>(args)...
|
||||
);
|
||||
}
|
||||
|
||||
template<typename ...T>
|
||||
void tuple_(std::tuple<T...> &t)
|
||||
{
|
||||
std::apply([this](auto &...x){(av(x), ...);}, t);
|
||||
}
|
||||
|
||||
// ----- miscellaneous -----
|
||||
|
||||
public:
|
||||
bool null ()
|
||||
{
|
||||
return f.is_null();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Optional<T> optional(T &t, bool write=true)
|
||||
{
|
||||
return io::json::optional(t, write);
|
||||
}
|
||||
|
||||
template<typename T, typename B>
|
||||
Pointer<B, T> pointer(B &b)
|
||||
{
|
||||
return io::json::Pointer<B, T> { &b };
|
||||
}
|
||||
|
||||
template<typename K>
|
||||
AsKeyType<K> asKeyType(const K &k)
|
||||
{
|
||||
return io::json::asKeyType(k);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Custom<T> custom(T &t)
|
||||
{
|
||||
return Custom<T> { &t };
|
||||
}
|
||||
} ;
|
||||
|
||||
template<typename T, typename Reader>
|
||||
void deserializeFrom (Reader &reader, T &t);
|
||||
|
||||
template<typename T, typename Reader>
|
||||
T deserializeFrom (Reader &reader);
|
||||
|
||||
template<typename R, typename T>
|
||||
T deserialize (Serialization &&data);
|
||||
|
||||
template<typename T>
|
||||
T deserialize (Serialization &&data);
|
||||
|
||||
template<typename T>
|
||||
T deserialize (const Serialization &data);
|
||||
|
||||
} // namespace
|
||||
|
||||
#include "in_sajson.inl"
|
||||
76
tjp/core/io/json/in_sajson.inl
Normal file
76
tjp/core/io/json/in_sajson.inl
Normal file
@@ -0,0 +1,76 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IO.h"
|
||||
|
||||
namespace tjp::core::io::json {
|
||||
|
||||
template<typename IO, typename T>
|
||||
void io_json_r(IO &io_, T &t)
|
||||
{
|
||||
if constexpr (has_io_json<IO, T>::value)
|
||||
{
|
||||
io_json(io_, t);
|
||||
}
|
||||
else
|
||||
{
|
||||
io_r(io_, t);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename IO, typename T>
|
||||
void io_json_r_dispatch(IO &io_, T &t)
|
||||
{
|
||||
io_json_r(io_, t);
|
||||
}
|
||||
|
||||
template<typename T, typename Reader>
|
||||
void deserializeFrom (Reader &reader, T &t)
|
||||
{
|
||||
reader.any(t);
|
||||
}
|
||||
|
||||
template<typename T, typename Reader>
|
||||
T deserializeFrom (Reader &reader)
|
||||
{
|
||||
T t;
|
||||
deserializeFrom(reader, t);
|
||||
return t;
|
||||
}
|
||||
|
||||
template<typename R, typename T>
|
||||
T deserialize (Serialization &&data)
|
||||
{
|
||||
R reader(std::move(data));
|
||||
return deserializeFrom<T>(reader);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T deserialize (Serialization &&data)
|
||||
{
|
||||
return deserialize<Reader<json_in>, T>(std::move(data));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T deserialize (const Serialization &data)
|
||||
{
|
||||
return deserialize<Reader<json_in>, T>(Serialization(data));
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<typename T>
|
||||
void fromStream (const std::istream &stream, T &t)
|
||||
{
|
||||
std::vector<char> v;
|
||||
omemstream s(v);
|
||||
s << stream.rdbuf(); //read the file
|
||||
|
||||
Reader<json_in> reader(std::move(v));
|
||||
reader.any(t);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
26
tjp/core/io/json/json.inl
Normal file
26
tjp/core/io/json/json.inl
Normal file
@@ -0,0 +1,26 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace nlohmann {
|
||||
|
||||
template<typename IO>
|
||||
void io_json_w(IO &io, const json &j)
|
||||
{
|
||||
auto s = to_string(j);
|
||||
io.any(s);
|
||||
}
|
||||
|
||||
template<typename IO>
|
||||
void io_json_r(IO &io, json &j)
|
||||
{
|
||||
std::string s;
|
||||
io.any(s);
|
||||
j = json(s);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
650
tjp/core/io/json/out.h
Normal file
650
tjp/core/io/json/out.h
Normal file
@@ -0,0 +1,650 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IO.h"
|
||||
|
||||
#include <tjp/core/io/memstream.h>
|
||||
#include <tjp/core/types/Types+IO.h>
|
||||
|
||||
#include <tjp/core/rtti/RTTI.hpp>
|
||||
|
||||
#ifdef IO_JSON_USE_LARGE_NUMBER_PROXY
|
||||
#include <tjp/core/containers/InPlaceArray.hpp>
|
||||
#include <tjp/core/bases/Base64.h>
|
||||
#endif
|
||||
|
||||
namespace tjp::core::io::json {
|
||||
|
||||
template<typename IO, typename T>
|
||||
void io_json_w(IO &io, const T &t);
|
||||
|
||||
template<typename IO, typename T>
|
||||
void io_json_w_dispatch(IO &io, const T &t);
|
||||
|
||||
struct json_out {
|
||||
std::ostream *out;
|
||||
|
||||
char Q = '"';
|
||||
char C = ':';
|
||||
char SEP = ',';
|
||||
bool firstValue=true;
|
||||
int level = 0;
|
||||
|
||||
json_out(std::ostream &out_) :
|
||||
out(&out_)
|
||||
{
|
||||
}
|
||||
|
||||
json_out()
|
||||
{
|
||||
}
|
||||
|
||||
~json_out()
|
||||
{
|
||||
}
|
||||
|
||||
void prettyPrintX ()
|
||||
{
|
||||
(*out) << std::endl;
|
||||
for (auto i=0; i<level; ++i)
|
||||
(*out) << " ";
|
||||
}
|
||||
|
||||
void prettyPrint ()
|
||||
{
|
||||
}
|
||||
|
||||
void valueSeparator()
|
||||
{
|
||||
if (!firstValue)
|
||||
{
|
||||
(*out) << SEP;
|
||||
}
|
||||
}
|
||||
|
||||
void endValue ()
|
||||
{
|
||||
firstValue = false;
|
||||
}
|
||||
|
||||
void beginValue ()
|
||||
{
|
||||
firstValue = true;
|
||||
}
|
||||
|
||||
void key(const KeyType &key)
|
||||
{
|
||||
valueSeparator();
|
||||
beginValue();
|
||||
|
||||
prettyPrint();
|
||||
(*out) << Q << key << Q << C;
|
||||
}
|
||||
|
||||
void begin_object()
|
||||
{
|
||||
valueSeparator();
|
||||
beginValue();
|
||||
(*out) << '{';
|
||||
level++;
|
||||
}
|
||||
|
||||
void end_object()
|
||||
{
|
||||
level--;
|
||||
prettyPrint();
|
||||
|
||||
(*out) << '}';
|
||||
endValue();
|
||||
}
|
||||
|
||||
void begin_array()
|
||||
{
|
||||
valueSeparator();
|
||||
beginValue();
|
||||
(*out) << '[';
|
||||
level++;
|
||||
}
|
||||
|
||||
void end_array()
|
||||
{
|
||||
level--;
|
||||
(*out) << ']';
|
||||
endValue();
|
||||
}
|
||||
|
||||
template<typename V>
|
||||
void v_value(const V &v_)
|
||||
{
|
||||
valueSeparator();
|
||||
|
||||
(*out) << v_;
|
||||
endValue();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void v_quoted(const T &v_)
|
||||
{
|
||||
valueSeparator();
|
||||
|
||||
(*out) << Q;
|
||||
|
||||
auto &oss = *out;
|
||||
for (const char c : v_) {
|
||||
switch (c) {
|
||||
case '\"': oss << "\\\""; break;
|
||||
case '\\': oss << "\\\\"; break;
|
||||
case '\b': oss << "\\b"; break;
|
||||
case '\f': oss << "\\f"; break;
|
||||
case '\n': oss << "\\n"; break;
|
||||
case '\r': oss << "\\r"; break;
|
||||
case '\t': oss << "\\t"; break;
|
||||
default:
|
||||
if (static_cast<unsigned char>(c) < 0x20)
|
||||
{
|
||||
char buffer[7];
|
||||
std::snprintf(buffer, sizeof(buffer), "\\u%04X", static_cast<unsigned char>(c));
|
||||
oss << buffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
oss << c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(*out) << Q;
|
||||
endValue();
|
||||
}
|
||||
|
||||
#ifdef IO_JSON_USE_LARGE_NUMBER_PROXY
|
||||
|
||||
template<typename V>
|
||||
void v_value_proxy(const V &v_)
|
||||
{
|
||||
constexpr size_t b64Size = sizeof(V) * 4;
|
||||
InPlaceArray<char, b64Size> b64;
|
||||
b64.resize(b64Size);
|
||||
|
||||
auto size = toBase64((char *)&v_, sizeof(V), b64.data(), b64.size());
|
||||
|
||||
b64.resize(size);
|
||||
v_quoted(b64);
|
||||
}
|
||||
#else
|
||||
|
||||
template<typename V>
|
||||
void v_value_proxy(const V &v_)
|
||||
{
|
||||
v_value(v_);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
template<typename V>
|
||||
void v(const V &v_)
|
||||
{
|
||||
v_value(v_);
|
||||
}
|
||||
|
||||
void v(const std::string &v_)
|
||||
{
|
||||
v_quoted(v_);
|
||||
}
|
||||
|
||||
void v(const std::string_view &v_)
|
||||
{
|
||||
v_quoted(v_);
|
||||
}
|
||||
|
||||
void v(const u8 &v_)
|
||||
{
|
||||
v_value((u32)v_);
|
||||
}
|
||||
|
||||
void v(const s8 &v_)
|
||||
{
|
||||
v_value((s32)v_);
|
||||
}
|
||||
|
||||
void v(const char &v_)
|
||||
{
|
||||
v_value((s32)v_);
|
||||
}
|
||||
|
||||
void v(const s64 &v_)
|
||||
{
|
||||
v_value_proxy(v_);
|
||||
}
|
||||
|
||||
void v(const u64 &v_)
|
||||
{
|
||||
v_value_proxy(v_);
|
||||
}
|
||||
|
||||
void v(const s128 &v_)
|
||||
{
|
||||
v_value_proxy(v_);
|
||||
}
|
||||
|
||||
void v(const u128 &v_)
|
||||
{
|
||||
v_value_proxy(v_);
|
||||
}
|
||||
|
||||
void v(const bool &v_)
|
||||
{
|
||||
v_value(v_ ? "true" : "false");
|
||||
}
|
||||
|
||||
void null()
|
||||
{
|
||||
v_value("null");
|
||||
}
|
||||
|
||||
} ;
|
||||
|
||||
struct json_out_mem : json_out
|
||||
{
|
||||
Serialization block;
|
||||
omemstream s;
|
||||
|
||||
json_out_mem() :
|
||||
s(block)
|
||||
{
|
||||
s.precision(17);
|
||||
out = &s;
|
||||
}
|
||||
} ;
|
||||
|
||||
inline
|
||||
auto serialization_of(json_out &m)
|
||||
{
|
||||
}
|
||||
|
||||
inline
|
||||
auto serialization_of(json_out_mem &m)
|
||||
{
|
||||
return m.block;
|
||||
}
|
||||
|
||||
// ------------------------
|
||||
|
||||
struct EmptyWriterBaseClass {
|
||||
template<typename IO, typename T>
|
||||
void dispatch_custom(IO &io, T &t) {}
|
||||
} ;
|
||||
|
||||
struct WriterAllocatorBase {
|
||||
template<typename T>
|
||||
void allocate(T &t)
|
||||
{
|
||||
io_ptr_allocate_default(*this, t);
|
||||
}
|
||||
} ;
|
||||
|
||||
template<typename S, typename Base=EmptyWriterBaseClass, typename Allocator=WriterAllocatorBase>
|
||||
class Writer :
|
||||
public Base,
|
||||
public Allocator
|
||||
{
|
||||
public:
|
||||
S f;
|
||||
u8 count=0;
|
||||
|
||||
public:
|
||||
auto partial()
|
||||
{
|
||||
return Writer<json_out_mem, Base>();
|
||||
}
|
||||
|
||||
auto serialization ()
|
||||
{
|
||||
return serialization_of(f);
|
||||
}
|
||||
|
||||
auto partial_serialization ()
|
||||
{
|
||||
return serialization();
|
||||
}
|
||||
|
||||
public:
|
||||
template<class... Args>
|
||||
Writer (Args&&... args) :
|
||||
f(args...)
|
||||
{
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return f.good();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void intrinsic(const T &t)
|
||||
{
|
||||
write(t);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void intrinsic_string(const T &t)
|
||||
{
|
||||
write(std::string_view(t));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void any(const T &t)
|
||||
{
|
||||
dispatch_any(t);
|
||||
}
|
||||
|
||||
template<typename ... T>
|
||||
void object(T&&... t)
|
||||
{
|
||||
object_begin();
|
||||
object_(t...);
|
||||
object_end();
|
||||
}
|
||||
|
||||
void object_begin()
|
||||
{
|
||||
f.begin_object();
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
void object_kf(KeyType k, F &&q)
|
||||
{
|
||||
f.key(k);
|
||||
q();
|
||||
};
|
||||
|
||||
template<typename ... T>
|
||||
void object_kv(T&&... t)
|
||||
{
|
||||
object_(t...);
|
||||
}
|
||||
|
||||
void object_end()
|
||||
{
|
||||
f.end_object();
|
||||
}
|
||||
|
||||
|
||||
template<typename ... T>
|
||||
void array(T&&... t)
|
||||
{
|
||||
f.begin_array();
|
||||
array_(t...);
|
||||
f.end_array();
|
||||
}
|
||||
|
||||
template<typename ...T>
|
||||
void tuple(std::tuple<T...> &t)
|
||||
{
|
||||
f.begin_array();
|
||||
std::apply([this](auto &...x){(av(x), ...);}, t);
|
||||
f.end_array();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void on_dispatch_any(const T &t)
|
||||
{
|
||||
on_any(t);
|
||||
}
|
||||
|
||||
void null()
|
||||
{
|
||||
f.null();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void on_dispatch_custom(T &t)
|
||||
{
|
||||
Base::dispatch_custom(*this, t);
|
||||
}
|
||||
|
||||
protected:
|
||||
template<typename T>
|
||||
void dispatch_any(const T &t)
|
||||
{
|
||||
IO_JSON_LOG << "any " << type_id<T>().name() << std::endl;
|
||||
|
||||
io_json_w_dispatch(*this, t);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void write(const T &t)
|
||||
{
|
||||
f.v(t);
|
||||
}
|
||||
|
||||
// kv & av -----------
|
||||
|
||||
template<typename V>
|
||||
void kv(KeyType k, const V &v)
|
||||
{
|
||||
f.key(k);
|
||||
any(v);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void kv(KeyType k, const Optional<T> &t)
|
||||
{
|
||||
if (t.write)
|
||||
kv(k, t.v);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void kv(KeyType k, const std::optional<T> &t)
|
||||
{
|
||||
if (t.has_value())
|
||||
kv(k, *t);
|
||||
}
|
||||
|
||||
template<typename B, typename T>
|
||||
void kv(KeyType k, const Pointer<B, T> &t)
|
||||
{
|
||||
if (t.write)
|
||||
kv(k, *(T *)*t.p);
|
||||
}
|
||||
|
||||
template<typename V>
|
||||
void av(const V &v)
|
||||
{
|
||||
any(v);
|
||||
}
|
||||
|
||||
// -- handling different containers --
|
||||
public:
|
||||
void array_begin (Size size)
|
||||
{
|
||||
f.begin_array();
|
||||
}
|
||||
|
||||
void array_end()
|
||||
{
|
||||
f.end_array();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void on_vector_begin (const T &t)
|
||||
{
|
||||
array_begin((Size)t.size());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void on_vector_end (const T &t)
|
||||
{
|
||||
array_end();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void on_vector_value(const T &t)
|
||||
{
|
||||
av(t);
|
||||
}
|
||||
|
||||
protected:
|
||||
template<typename T>
|
||||
void on_vector(const T &t)
|
||||
{
|
||||
IO_JSON_LOG << "many " << type_id<T>().name() << std::endl;
|
||||
on_vector_begin(t);
|
||||
|
||||
// this fails for std::vector<bool>, because vector bool
|
||||
// does some magic to compress to a bit field, and then the iterator
|
||||
// doesn't pass back a reference
|
||||
// for (const auto &v : t)
|
||||
// any(v);
|
||||
|
||||
for (auto i = t.begin(); i!=t.end(); ++i)
|
||||
on_vector_value(*i);
|
||||
|
||||
on_vector_end(t);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void on_map(const T &t)
|
||||
{
|
||||
IO_JSON_LOG << "map " << type_id<T>().name() << std::endl;
|
||||
|
||||
f.begin_object();
|
||||
|
||||
for (auto &v : t)
|
||||
kv(asKeyType(v.first), v.second);
|
||||
|
||||
f.end_object();
|
||||
}
|
||||
|
||||
// -- dispatching different containers --
|
||||
|
||||
template<typename T, typename std::enable_if<!is_iterable<T>::value || is_stringish<T>::value, T>::type* = nullptr>
|
||||
void on_any(const T &t)
|
||||
{
|
||||
io_dispatch_failed(*this, t);
|
||||
}
|
||||
|
||||
template<typename T, typename std::enable_if<is_iterable<T>::value && !is_mappish<T>::value && !is_stringish<T>::value, T>::type* = nullptr>
|
||||
void on_any(const T &t)
|
||||
{
|
||||
on_vector(t);
|
||||
}
|
||||
|
||||
template<typename T, typename std::enable_if<is_iterable<T>::value && is_mappish<T>::value, T>::type* = nullptr>
|
||||
void on_any(const T &t)
|
||||
{
|
||||
on_map(t);
|
||||
}
|
||||
|
||||
// void on_any(const variant::Variant &t)
|
||||
// {
|
||||
// on_variant(t);
|
||||
// }
|
||||
//
|
||||
// ---- handling object & array ----
|
||||
|
||||
void object_()
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void object__(KeyType key, const T &t)
|
||||
{
|
||||
kv(key, t);
|
||||
}
|
||||
|
||||
template<typename ...Args>
|
||||
void object_(Args&& ...args)
|
||||
{
|
||||
call_expand_2(
|
||||
[&](auto && ...args) { object__(std::forward<decltype(args)>(args)...); },
|
||||
std::forward<Args>(args)...
|
||||
);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void array__(const T &t)
|
||||
{
|
||||
any(t);
|
||||
}
|
||||
|
||||
template<typename ...Args>
|
||||
void array_(Args&& ...args)
|
||||
{
|
||||
call_expand_1(
|
||||
[&](auto && ...args) { array__(std::forward<decltype(args)>(args)...); },
|
||||
std::forward<Args>(args)...
|
||||
);
|
||||
}
|
||||
|
||||
template<typename ...T>
|
||||
void tuple_(std::tuple<T...> &t)
|
||||
{
|
||||
std::apply([this](auto &...x){(av(x), ...);}, t);
|
||||
}
|
||||
|
||||
public:
|
||||
// ----- miscellaneous -----
|
||||
|
||||
template<typename T>
|
||||
Optional<T> optional(T &t, bool write=true)
|
||||
{
|
||||
return io::json::optional(t, write);
|
||||
}
|
||||
|
||||
template<typename T, typename B, typename std::enable_if<!std::is_class<T>::value, T>::type* = nullptr>
|
||||
Pointer<B, T> pointer(B &b)
|
||||
{
|
||||
return io::json::Pointer<B, T> {
|
||||
&b, b != nullptr
|
||||
};
|
||||
}
|
||||
|
||||
template<typename T, typename B, typename std::enable_if<std::is_class<T>::value, T>::type* = nullptr>
|
||||
Pointer<B, T> pointer(B &b)
|
||||
{
|
||||
return io::json::Pointer<B, T> {
|
||||
&b, dynamic_cast_ptr<T>(b) != nullptr
|
||||
};
|
||||
}
|
||||
|
||||
template<typename K>
|
||||
AsKeyType<K> asKeyType(const K &k)
|
||||
{
|
||||
return io::json::asKeyType(k);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Custom<T> custom(T &t)
|
||||
{
|
||||
return Custom<T> { &t };
|
||||
}
|
||||
} ;
|
||||
|
||||
// -------------------
|
||||
|
||||
template<typename W, typename ... X>
|
||||
Serialization serialize (X&&... x);
|
||||
|
||||
template<typename ... X>
|
||||
Serialization serialize (X&&... x);
|
||||
|
||||
template<typename T>
|
||||
void serializeTo (Serialization &serialization, const T &t);
|
||||
|
||||
template<typename W, typename T>
|
||||
Serialization serializeTo(W &w, const T &t);
|
||||
|
||||
template<typename ... X>
|
||||
void toStream (std::ostream &file, X&& ... x);
|
||||
|
||||
template<typename ... X>
|
||||
bool toFile (const std::string &fileName, X&& ... x);
|
||||
|
||||
} // namespace
|
||||
|
||||
#include "out.inl"
|
||||
72
tjp/core/io/json/out.inl
Normal file
72
tjp/core/io/json/out.inl
Normal file
@@ -0,0 +1,72 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IO.h"
|
||||
|
||||
namespace tjp::core::io::json {
|
||||
|
||||
template<typename IO, typename T>
|
||||
void io_json_w(IO &io_, const T &t)
|
||||
{
|
||||
typedef typename std::remove_const<T>::type TC;
|
||||
|
||||
if constexpr (has_io_json<IO, T>::value)
|
||||
{
|
||||
auto &t_ = *const_cast<TC *>(&t);
|
||||
io_json(io_, t_);
|
||||
}
|
||||
else
|
||||
{
|
||||
io_w(io_, t);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename IO, typename T>
|
||||
void io_json_w_dispatch(IO &io_, const T &t)
|
||||
{
|
||||
io_json_w(io_, t);
|
||||
}
|
||||
|
||||
template<typename W, typename T>
|
||||
void serialize_many (W &writer, const T &t)
|
||||
{
|
||||
writer.any(t);
|
||||
}
|
||||
|
||||
template<typename W, typename T, typename ... X>
|
||||
void serialize_many (W &writer, const T &t, X&& ... x)
|
||||
{
|
||||
writer.any(t);
|
||||
serialize_many(writer, x...);
|
||||
}
|
||||
|
||||
template<typename W, typename ... X>
|
||||
Serialization serialize (X&&... x)
|
||||
{
|
||||
Serialization v;
|
||||
omemstream s(v);
|
||||
s.precision(17);
|
||||
|
||||
W writer(s);
|
||||
|
||||
serialize_many(writer, x...);
|
||||
return v;
|
||||
};
|
||||
|
||||
template<typename ... X>
|
||||
Serialization serialize (X&&... x)
|
||||
{
|
||||
return serialize<Writer<json_out>>(x...);
|
||||
}
|
||||
|
||||
template<typename ... X>
|
||||
void toStream (std::ostream &file, X&& ... x)
|
||||
{
|
||||
Writer<json_out> writer { file };
|
||||
serialize_many(writer, x...);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
21
tjp/core/io/json/out_file.hpp
Normal file
21
tjp/core/io/json/out_file.hpp
Normal file
@@ -0,0 +1,21 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fstream>
|
||||
|
||||
namespace tjp::core::io::json {
|
||||
|
||||
template<typename ... X>
|
||||
bool toFile (const std::string &fileName, X&& ... x)
|
||||
{
|
||||
std::ofstream stream(fileName);
|
||||
stream.precision(17);
|
||||
toStream(stream, x...);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
Reference in New Issue
Block a user