Commit 39b91e74 by Maarten L. Hekkelman

- new item storage

- formatting of numbers using to_chars
parent 6175b7e3
......@@ -209,7 +209,7 @@ inline auto coloured(std::basic_string<CharT, Traits, Alloc> &s, StringColour fo
// --------------------------------------------------------------------
/// std::from_chars for floating point types.
template <typename FloatType>
template <typename FloatType, std::enable_if_t<std::is_floating_point_v<FloatType>, int> = 0>
std::from_chars_result from_chars(const char *first, const char *last, FloatType &value)
{
std::from_chars_result result{first, {}};
......@@ -329,6 +329,92 @@ std::from_chars_result from_chars(const char *first, const char *last, FloatType
return result;
}
enum class chars_format
{
scientific = 1,
fixed = 2,
// hex,
general = fixed | scientific
};
template <typename FloatType, std::enable_if_t<std::is_floating_point_v<FloatType>, int> = 0>
std::to_chars_result to_chars(char *first, char *last, FloatType &value, chars_format fmt)
{
int size = last - first;
int r;
switch (fmt)
{
case chars_format::scientific:
if constexpr (std::is_same_v<FloatType, long double>)
r = snprintf(first, last - first, "%le", value);
else
r = snprintf(first, last - first, "%e", value);
break;
case chars_format::fixed:
if constexpr (std::is_same_v<FloatType, long double>)
r = snprintf(first, last - first, "%lf", value);
else
r = snprintf(first, last - first, "%f", value);
break;
case chars_format::general:
if constexpr (std::is_same_v<FloatType, long double>)
r = snprintf(first, last - first, "%lg", value);
else
r = snprintf(first, last - first, "%g", value);
break;
}
std::to_chars_result result;
if (r < 0 or r >= size)
result = { first, std::errc::value_too_large };
else
result = { first + r, std::errc() };
return result;
}
template <typename FloatType, std::enable_if_t<std::is_floating_point_v<FloatType>, int> = 0>
std::to_chars_result to_chars(char *first, char *last, FloatType &value, chars_format fmt, int precision)
{
int size = last - first;
int r;
switch (fmt)
{
case chars_format::scientific:
if constexpr (std::is_same_v<FloatType, long double>)
r = snprintf(first, last - first, "%.*le", precision, value);
else
r = snprintf(first, last - first, "%.*e", precision, value);
break;
case chars_format::fixed:
if constexpr (std::is_same_v<FloatType, long double>)
r = snprintf(first, last - first, "%.*lf", precision, value);
else
r = snprintf(first, last - first, "%.*f", precision, value);
break;
case chars_format::general:
if constexpr (std::is_same_v<FloatType, long double>)
r = snprintf(first, last - first, "%.*lg", precision, value);
else
r = snprintf(first, last - first, "%.*g", precision, value);
break;
}
std::to_chars_result result;
if (r < 0 or r >= size)
result = { first, std::errc::value_too_large };
else
result = { first + r, std::errc() };
return result;
}
// --------------------------------------------------------------------
// A progress bar
......
......@@ -147,9 +147,19 @@ class category_t
{
}
category_t(const category_t &) = default;
category_t(const category_t &rhs)
: m_allocator(std::allocator_traits<allocator_type>::select_on_container_copy_construction(rhs.get_allocator()))
, m_name(rhs.m_name)
, m_columns(rhs.m_columns)
{
// for (value_type r : rhs)
// this->emplace(r);
}
category_t(category_t &&)
{
category_t(category_t &&) = default;
}
template <typename Alloc2>
category_t(const category_t &c, const Alloc2 &a)
......@@ -165,8 +175,15 @@ class category_t
{
}
category_t &operator=(const category_t &) = default;
category_t &operator=(category_t &&) = default;
category_t &operator=(const category_t &)
{
}
category_t &operator=(category_t &&)
{
}
~category_t()
{
......@@ -241,6 +258,16 @@ class category_t
return {*this, nullptr};
}
size_t size() const
{
return std::distance(cbegin(), cend());
}
bool empty() const
{
return m_head == nullptr;
}
// --------------------------------------------------------------------
template <typename... Ts, typename... Ns>
......@@ -264,6 +291,11 @@ class category_t
return this->emplace(items.begin(), items.end());
}
iterator emplace(value_type row)
{
// return
}
template <typename ItemIter>
iterator emplace(ItemIter b, ItemIter e)
{
......
......@@ -26,25 +26,26 @@
#pragma once
#include <cstring>
#include <charconv>
#include <cstring>
#include <iomanip>
#include <limits>
#include <memory>
#include <optional>
#include <limits>
#include <cif++/CifUtils.hpp>
namespace cif
{
extern int VERBOSE;
extern int VERBOSE;
}
namespace cif::v2
{
// --------------------------------------------------------------------
/// \brief item is a transient class that is used to pass data into rows
/// but it also takes care of formatting data
class item
{
public:
......@@ -52,28 +53,51 @@ class item
item(std::string_view name, char value)
: m_name(name)
, m_value({ value })
, m_value(m_buffer, 1)
{
m_buffer[0] = value;
m_buffer[1] = 0;
}
template <typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0>
item(std::string_view name, const T &value, int precision)
: m_name(name)
#if defined(__cpp_lib_format)
, m_value(std::format(".{}f", value, precision))
#endif
{
#if not defined(__cpp_lib_format)
// TODO: implement
m_value = std::to_string(value);
#endif
auto r = cif::to_chars(m_buffer, m_buffer + sizeof(m_buffer) - 1, value, cif::chars_format::fixed, precision);
if (r.ec != std::errc())
throw std::runtime_error("Could not format number");
assert(r.ptr >= m_buffer and r.ptr < m_buffer + sizeof(m_buffer));
*r.ptr = 0;
m_value = std::string_view(m_buffer, r.ptr - m_buffer);
}
template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
template <typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0>
item(const std::string_view name, const T &value)
: m_name(name)
, m_value(std::to_string(value))
{
auto r = cif::to_chars(m_buffer, m_buffer + sizeof(m_buffer) - 1, value, cif::chars_format::general);
if (r.ec != std::errc())
throw std::runtime_error("Could not format number");
assert(r.ptr >= m_buffer and r.ptr < m_buffer + sizeof(m_buffer));
*r.ptr = 0;
m_value = std::string_view(m_buffer, r.ptr - m_buffer);
}
template <typename T, std::enable_if_t<std::is_integral_v<T>, int> = 0>
item(const std::string_view name, const T &value)
: m_name(name)
, m_value(std::to_string(value))
{
auto r = std::to_chars(m_buffer, m_buffer + sizeof(m_buffer) - 1, value);
if (r.ec != std::errc())
throw std::runtime_error("Could not format number");
assert(r.ptr >= m_buffer and r.ptr < m_buffer + sizeof(m_buffer));
*r.ptr = 0;
m_value = std::string_view(m_buffer, r.ptr - m_buffer);
}
item(const std::string_view name, const std::string_view value)
......@@ -90,8 +114,8 @@ class item
item &operator=(item &&rhs) noexcept = default;
const std::string &name() const { return m_name; }
const std::string &value() const { return m_value; }
std::string_view name() const { return m_name; }
std::string_view value() const { return m_value; }
void value(const std::string &v) { m_value = v; }
......@@ -105,17 +129,18 @@ class item
bool is_unknown() const { return m_value == "?"; }
size_t length() const { return m_value.length(); }
const char *c_str() const { return m_value.c_str(); }
// const char *c_str() const { return m_value.c_str(); }
private:
std::string m_name;
std::string m_value;
std::string_view m_name;
std::string_view m_value;
char m_buffer[64]; // TODO: optimize this magic number, might be too large
};
// --------------------------------------------------------------------
// Transient object to access stored data
template<typename Row>
template <typename Row>
struct item_handle
{
using row_type = Row;
......@@ -232,7 +257,6 @@ struct item_handle
}
private:
uint16_t m_column;
row_type &m_row;
// bool mConst = false;
......@@ -276,7 +300,7 @@ struct item_handle<Row>::item_value_as<T, std::enable_if_t<std::is_arithmetic_v<
return result;
}
static int compare(const item_handle &ref, const T& value, bool icase)
static int compare(const item_handle &ref, const T &value, bool icase)
{
int result = 0;
......@@ -358,7 +382,7 @@ struct item_handle<Row>::item_value_as<T, std::enable_if_t<std::is_same_v<T, boo
{
bool rv = convert(ref);
return value && rv ? 0
: (rv < value ? -1 : 1);
: (rv < value ? -1 : 1);
}
};
......
......@@ -121,28 +121,42 @@ class row_handle
using category_type = Category;
using row_type = std::conditional_t<std::is_const_v<category_type>, const typename category_type::row, typename category_type::row>;
row_handle() = default;
row_handle(const row_handle &) = default;
row_handle(row_handle &&) = default;
row_handle &operator=(const row_handle &) = default;
row_handle &operator=(row_handle &&) = default;
row_handle(category_type &cat, row_type &row)
: m_cat(cat)
, m_row(row) {}
: m_cat(&cat)
, m_row(&row) {}
explicit operator bool() const
{
return m_cat != nullptr and m_row != nullptr;
}
item_handle<row_type> operator[](uint32_t column_ix)
{
return item_handle<row_type>(column_ix, m_row);
return item_handle<row_type>(column_ix, *m_row);
}
const item_handle<const row_type> operator[](uint32_t column_ix) const
{
return item_handle<const row_type>(column_ix, m_row);
return item_handle<const row_type>(column_ix, *m_row);
}
item_handle<row_type> operator[](std::string_view column_name)
{
return item_handle<row_type>(get_column_ix(column_name), m_row);
return item_handle<row_type>(get_column_ix(column_name), *m_row);
}
const item_handle<const row_type> operator[](std::string_view column_name) const
{
return item_handle<const row_type>(get_column_ix(column_name), m_row);
return item_handle<const row_type>(get_column_ix(column_name), *m_row);
}
template <typename... Ts, size_t N>
......@@ -166,11 +180,11 @@ class row_handle
uint32_t get_column_ix(std::string_view name) const
{
return m_cat.get_column_ix(name);
return m_cat->get_column_ix(name);
}
category_type &m_cat;
row_type &m_row;
category_type *m_cat = nullptr;
row_type *m_row = nullptr;
};
......
......@@ -78,24 +78,24 @@ bool init_unit_test()
BOOST_AUTO_TEST_CASE(cc_1)
{
std::tuple<std::string_view,float,char> tests[] = {
{ "1.0", 1.0, 0 },
{ "1.0e10", 1.0e10, 0 },
{ "-1.1e10", -1.1e10, 0 },
{ "-.2e11", -.2e11, 0 },
{ "1.3e-10", 1.3e-10, 0 },
{ "1.0 ", 1.0, ' ' },
{ "1.0e10 ", 1.0e10, ' ' },
{ "-1.1e10 ", -1.1e10, ' ' },
{ "-.2e11 ", -.2e11, ' ' },
{ "1.3e-10 ", 1.3e-10, ' ' },
{ "3.0", 3.0, 0 },
{ "3.0 ", 3.0, ' ' },
{ "3.000000", 3.0, 0 },
{ "3.000000 ", 3.0, ' ' },
std::tuple<std::string_view, float, char> tests[] = {
{"1.0", 1.0, 0},
{"1.0e10", 1.0e10, 0},
{"-1.1e10", -1.1e10, 0},
{"-.2e11", -.2e11, 0},
{"1.3e-10", 1.3e-10, 0},
{"1.0 ", 1.0, ' '},
{"1.0e10 ", 1.0e10, ' '},
{"-1.1e10 ", -1.1e10, ' '},
{"-.2e11 ", -.2e11, ' '},
{"1.3e-10 ", 1.3e-10, ' '},
{"3.0", 3.0, 0},
{"3.0 ", 3.0, ' '},
{"3.000000", 3.0, 0},
{"3.000000 ", 3.0, ' '},
};
for (const auto &[txt, val, ch] : tests)
......@@ -108,24 +108,42 @@ BOOST_AUTO_TEST_CASE(cc_1)
if (ch != 0)
BOOST_CHECK_EQUAL(*ptr, ch);
}
}
BOOST_AUTO_TEST_CASE(cc_2)
{
std::tuple<float, int, std::string_view> tests[] = {
{ 1.1, 1, "1.1" }
};
for (const auto &[val, prec, test] : tests)
{
char buffer[64];
const auto &[ptr, ec] = cif::to_chars(buffer, buffer + sizeof(buffer), val, cif::chars_format::fixed, prec);
BOOST_CHECK(ec == std::errc());
BOOST_CHECK_EQUAL(buffer, test);
}
}
// --------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(r_1)
{
cif::v2::category c("foo");
c.emplace({
{ "f-1", 1 },
{ "f-2", "two" },
{ "f-3", 3.0 },
{"f-1", 1},
{"f-2", "two"},
{"f-3", 3.0, 3},
});
auto row = c.front();
BOOST_CHECK_EQUAL(row["f-1"].compare(1), 0);
BOOST_CHECK_EQUAL(row["f-2"].compare("two"), 0);
BOOST_CHECK_EQUAL(row["f-3"].compare(3.0), 0); // This fails when running in valgrind... sigh
BOOST_CHECK_EQUAL(row["f-3"].compare(3.0), 0); // This fails when running in valgrind... sigh
}
BOOST_AUTO_TEST_CASE(r_2)
......@@ -134,25 +152,22 @@ BOOST_AUTO_TEST_CASE(r_2)
for (size_t i = 1; i < 256; ++i)
{
c.emplace({
{ "id", i },
{ "txt", std::string(i, 'x') }
});
c.emplace({{"id", i},
{"txt", std::string(i, 'x')}});
}
}
BOOST_AUTO_TEST_CASE(c_1)
{
cif::v2::category c("foo");
c.emplace({ { "id", 1 }, { "s", "aap" } });
c.emplace({ { "id", 2 }, { "s", "noot" } });
c.emplace({ { "id", 3 }, { "s", "mies" } });
c.emplace({{"id", 1}, {"s", "aap"}});
c.emplace({{"id", 2}, {"s", "noot"}});
c.emplace({{"id", 3}, {"s", "mies"}});
int n = 1;
const char *ts[] = { "aap", "noot", "mies" };
const char *ts[] = {"aap", "noot", "mies"};
for (auto r : c)
{
......@@ -177,7 +192,7 @@ BOOST_AUTO_TEST_CASE(c_1)
n = 1;
for (const auto &[i, s] : c.rows<int,std::string>("id", "s"))
for (const auto &[i, s] : c.rows<int, std::string>("id", "s"))
{
BOOST_CHECK_EQUAL(i, n);
BOOST_CHECK_EQUAL(s.compare(ts[n - 1]), 0);
......@@ -185,16 +200,29 @@ BOOST_AUTO_TEST_CASE(c_1)
}
}
BOOST_AUTO_TEST_CASE(c_2)
{
std::tuple<int,const char*> D[] = {
{1, "aap"},
{2, "noot"},
{3, "mies"}
};
cif::v2::category c("foo");
c.emplace({ { "id", 1 }, { "s", "aap" } });
c.emplace({ { "id", 2 }, { "s", "noot" } });
c.emplace({ { "id", 3 }, { "s", "mies" } });
for (const auto &[id, s] : D)
c.emplace({ {"id", id}, { "s", s} });
BOOST_CHECK(not c.empty());
BOOST_CHECK_EQUAL(c.size(), 3);
cif::v2::category c2(c);
for (auto r : c)
c2.emplace(r);
BOOST_CHECK(not c2.empty());
BOOST_CHECK_EQUAL(c2.size(), 3);
}
......@@ -306,7 +334,6 @@ BOOST_AUTO_TEST_CASE(c_2)
// auto &test = db["test"];
// BOOST_CHECK(test.size() == 5);
// BOOST_CHECK(test.exists("value"_key == cif::null));
// BOOST_CHECK(test.find("value"_key == cif::null).size() == 2);
// }
......@@ -2045,4 +2072,3 @@ BOOST_AUTO_TEST_CASE(c_2)
// BOOST_CHECK_EQUAL(text, kS[i++].s);
// }
// }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment