Commit 0eaeb165 by Maarten L. Hekkelman

split out item

parent f4a6533f
...@@ -35,50 +35,117 @@ ...@@ -35,50 +35,117 @@
#include "cif++/CifUtils.hpp" #include "cif++/CifUtils.hpp"
namespace cif_v2 #include "cif++/v2/item.hpp"
namespace cif::v2
{ {
template <typename Alloc = std::allocator<void>> // template <typename Alloc = std::allocator<void>>
class item_t // class item
// {
// public:
// item() = default;
// item(std::string_view name, char value)
// : m_name(name)
// , m_value(value)
// {
// }
// #if defined(__cpp_lib_format)
// 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)
// , m_value(std::format(".{}f", value, precision))
// {
// }
// #endif
// template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
// item(const std::string_view name, const T &value)
// : m_name(name)
// , m_value(std::to_string(value))
// {
// }
// item(const std::string_view name, const std::string_view value)
// : m_name(name)
// , m_value(value)
// {
// }
// item(const item &rhs) = default;
// item(item &&rhs) noexcept = default;
// item &operator=(const item &rhs) = default;
// item &operator=(item &&rhs) noexcept = default;
// const std::string &name() const { return m_name; }
// const std::string &value() const { return m_value; }
// void value(const std::string &v) { m_value = v; }
// /// \brief empty means either null or unknown
// bool empty() const { return m_value.empty(); }
// /// \brief returns true if the field contains '.'
// bool is_null() const { return m_value == "."; }
// /// \brief returns true if the field contains '?'
// 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(); }
// private:
// std::string m_name;
// std::string m_value;
// };
// using item = item<>;
class item
{ {
public: public:
item_t() = default; item() = default;
item_t(std::string_view name, char value) item(std::string_view name, char value)
: m_name(name) : m_name(name)
, m_value(value) , m_value({ value })
{ {
} }
#if defined(__cpp_lib_format)
template <typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0> template <typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0>
item_t(std::string_view name, const T &value, int precision) item(std::string_view name, const T &value, int precision)
: m_name(name) : m_name(name)
#if defined(__cpp_lib_format)
, m_value(std::format(".{}f", value, precision)) , m_value(std::format(".{}f", value, precision))
#endif
{ {
} }
#endif
template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0> template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
item_t(const std::string_view name, const T &value) item(const std::string_view name, const T &value)
: m_name(name) : m_name(name)
, m_value(std::to_string(value)) , m_value(std::to_string(value))
{ {
} }
item_t(const std::string_view name, const std::string_view value) item(const std::string_view name, const std::string_view value)
: m_name(name) : m_name(name)
, m_value(value) , m_value(value)
{ {
} }
item_t(const item_t &rhs) = default; item(const item &rhs) = default;
item_t(item_t &&rhs) noexcept = default; item(item &&rhs) noexcept = default;
item_t &operator=(const item_t &rhs) = default; item &operator=(const item &rhs) = default;
item_t &operator=(item_t &&rhs) noexcept = default; item &operator=(item &&rhs) noexcept = default;
const std::string &name() const { return m_name; } const std::string &name() const { return m_name; }
const std::string &value() const { return m_value; } const std::string &value() const { return m_value; }
...@@ -102,99 +169,113 @@ class item_t ...@@ -102,99 +169,113 @@ class item_t
std::string m_value; std::string m_value;
}; };
using item = item_t<>;
// -------------------------------------------------------------------- // --------------------------------------------------------------------
// template < template <typename Alloc = std::allocator<std::byte>>
// typename Item = item, class row_t
// typename Alloc = std::allocator<Item>> {
// class row_t : public std::list<Item, Alloc> using byte_allocator_type = typename std::allocator_traits<Alloc>::template rebind_alloc<std::byte>;
// {
// public:
// using value_type = Item;
// using base_type = std::list<Item, Alloc>;
// using allocator_type = Alloc;
// row_t() = default; struct AllocTraitsImpl : std::allocator_traits<byte_allocator_type>
{
using base_type = std::allocator_traits<byte_allocator_type>;
// row_t(const std::string &name, const allocator_type &alloc = allocator_type()) static constexpr typename base_type::pointer
// : base_type(alloc) allocate(byte_allocator_type &a, typename base_type::size_type n)
// , m_name(name) {} {
return base_type::allocate(a, n);
}
};
// row_t(const row_t &) = default;
// row_t(row_t &&) = default; using AllocTraits = AllocTraitsImpl;
// template<typename Alloc2> public:
// row_t(const row_t &r, const Alloc2 &a)
// : base_type(r, a)
// , m_name(r.m_name)
// {
// }
// template<typename Alloc2> using allocator_type = byte_allocator_type;
// row_t(row_t &&r, const Alloc2 &a)
// : base_type(std::move(r), a)
// , m_name(r.m_name)
// {
// }
// row_t &operator=(const row_t &) = default; row_t(const allocator_type &alloc = allocator_type())
// row_t &operator=(row_t &&) = default; : m_allocator(alloc) {}
// row_t(std::initializer_list<item_type> items, const allocator_type &alloc = allocator_type()) row_t(const row_t &) = default;
// {
// } row_t(row_t &&) = default;
// // -------------------------------------------------------------------- // template<typename Alloc2>
// row_t(const row_t &r, const Alloc2 &a)
// : m_allocator(a)
// {
// }
// const std::string &name() const { return m_name; } // template<typename Alloc2>
// const std::string &value() const { return m_value; } // row_t(row_t &&r, const Alloc2 &a)
// : m_allocator(a)
// {
// }
// private: row_t &operator=(const row_t &) = default;
// std::string m_name; row_t &operator=(row_t &&) = default;
// std::string m_value;
// };
// using row = row_t<>; row_t(std::initializer_list<item> items, const allocator_type &alloc = allocator_type())
: m_allocator(alloc)
{
// -------------------------------------------------------------------- }
namespace detail item_handle<row_t> operator[](uint32_t column_ix)
{ {
return item_handle<row_t>(column_ix, *this);
}
struct item_value const item_handle<const row_t> operator[](uint32_t column_ix) const
{ {
item_value *m_next; return item_handle<const row_t>(column_ix, *this);
uint32_t m_column_ix; }
char m_text[4];
};
item_handle<row_t> operator[](std::string_view column_name)
{
return item_handle<row_t>(column_name, get_column_ix(column_name), *this);
}
} const item_handle<const row_t> operator[](std::string_view column_name) const
{
return item_handle<const row_t>(column_name, get_column_ix(column_name), *this);
}
// --------------------------------------------------------------------
// --------------------------------------------------------------------
private:
uint32_t get_column_ix(std::string_view name) const
{
return 0;
}
template<typename R> friend class item_handle;
allocator_type m_allocator;
item_value *m_head = nullptr, *m_tail = nullptr;
size_t m_size = 0;
};
template <typename Item = item, typename Alloc = std::allocator<Item>> using row = row_t<>;
using row = std::forward_list<Item, Alloc>;
// -------------------------------------------------------------------- // --------------------------------------------------------------------
template < template <
typename Row = row,
typename Alloc = std::allocator<std::byte>> typename Alloc = std::allocator<std::byte>>
class category_t class category_t : public std::forward_list<Row, std::scoped_allocator_adaptor<std::allocator<Row>>>
{ {
public: public:
using item_type = Item;
using value_type = Row; using value_type = Row;
using base_type = std::list<Row, Alloc>; using base_type = std::forward_list<Row, std::scoped_allocator_adaptor<std::allocator<Row>>>;
using allocator_type = Alloc; using allocator_type = Alloc;
category_t() = default; category_t() = default;
...@@ -232,9 +313,9 @@ class category_t ...@@ -232,9 +313,9 @@ class category_t
void emplace(value_type &&row); void emplace(value_type &&row);
void emplace(std::initializer_list<item_type> items) void emplace(std::initializer_list<item> items)
{ {
this->emplace_back(value_type{items}); this->emplace_back(value_type{items, this->get_allocator()});
} }
// void write(std::ostream &os, const std::vector<size_t> &order, bool includeEmptyColumns) // void write(std::ostream &os, const std::vector<size_t> &order, bool includeEmptyColumns)
...@@ -424,7 +505,7 @@ class category_t ...@@ -424,7 +505,7 @@ class category_t
}; };
std::string m_name; std::string m_name;
std::vector<item_column> m_columns; std::vector<item_column, typename std::allocator_traits<allocator_type>::template rebind_alloc<item_column>> m_columns;
}; };
using category = category_t<>; using category = category_t<>;
......
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022 NKI/AVL, Netherlands Cancer Institute
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <cstring>
#include <memory>
#include <optional>
namespace cif::v2
{
// --------------------------------------------------------------------
// Internal storage, strictly forward linked list with minimal space
// requirements.
struct item_value
{
item_value(uint32_t column_ix, const char *value)
: m_column_ix(column_ix)
{
strcpy(m_text, value);
}
~item_value()
{
// remove recursively (and be paranoid)
while (m_next != nullptr and m_next != this)
{
auto n = m_next;
m_next = n->m_next;
n->m_next = nullptr;
delete n;
}
}
item_value *m_next;
uint32_t m_column_ix;
char m_text[4];
};
// --------------------------------------------------------------------
// Transient object to access stored data
template<typename Row>
struct item_handle
{
using row_type = Row;
public:
// conversion helper class
template <typename T, typename = void>
struct item_value_as;
template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
item_handle &operator=(const T &value)
{
this->operator=(std::to_string(value));
return *this;
}
template <typename T>
item_handle &operator=(const std::optional<T> &value)
{
if (value)
this->operator=(*value);
else
this->operator=("?");
return *this;
}
item_handle &operator=(const std::string &value)
{
m_row.assign(m_name, value, false);
return *this;
}
template <typename... Ts>
void os(const Ts &...v)
{
std::ostringstream ss;
((ss << v), ...);
this->operator=(ss.str());
}
void swap(item_handle &b);
template <typename T = std::string>
auto as() const
{
using value_type = std::remove_cv_t<std::remove_reference_t<T>>;
return item_value_as<value_type>::convert(*this);
}
template <typename T>
auto value_or(const T &dv)
{
return empty() ? dv : this->as<T>();
}
template <typename T>
int compare(const T &value, bool icase) const
{
return item_value_as<T>::compare(*this, value, icase);
}
// empty means either null or unknown
bool empty() const
{
const char *s = c_str();
return *s == 0 or (s[1] == 0 and (*s == '.' or *s == '?'));
}
explicit operator bool() const { return not empty(); }
// is_null means the field contains '.'
bool is_null() const
{
const char *s = c_str();
return *s == '.' and s[1] == 0;
}
// is_unknown means the field contains '?'
bool is_unknown() const
{
const char *s = c_str();
return *s == '?' and s[1] == 0;
}
const char *c_str() const
{
for (auto iv = m_row.m_head; iv != nullptr; iv = iv->m_next)
{
if (iv->m_column_ix == m_column)
return iv->m_text;
}
return s_empty_result;
}
// bool operator!=(const std::string &s) const { return s != c_str(); }
// bool operator==(const std::string &s) const { return s == c_str(); }
item_handle(std::string_view name, size_t column, row_type &row)
: m_name(name)
, m_column(column)
, m_row(row)
{
}
item_handle(std::string_view name, size_t column, const row_type &row)
: m_name(name)
, m_column(column)
, m_row(const_cast<row_type &>(row))
// , mConst(true)
{
}
private:
std::string_view m_name;
size_t m_column;
row_type &m_row;
// bool mConst = false;
static constexpr const char *s_empty_result = "";
};
template <typename Row>
template <typename T>
struct item_handle<Row>::item_value_as<T, std::enable_if_t<std::is_floating_point_v<T>>>
{
using value_type = std::remove_reference_t<std::remove_cv_t<T>>;
static value_type convert(const item_handle &ref)
{
value_type result = {};
if (not ref.empty())
result = static_cast<value_type>(std::stod(ref.c_str()));
return result;
}
static int compare(const item_handle &ref, double value, bool icase)
{
int result = 0;
const char *s = ref.c_str();
if (s == nullptr or *s == 0)
result = 1;
else
{
try
{
auto v = std::strtod(s, nullptr);
if (v < value)
result = -1;
else if (v > value)
result = 1;
}
catch (...)
{
// if (VERBOSE)
// std::cerr << "conversion error in compare for '" << ref.c_str() << '\'' << std::endl;
result = 1;
}
}
return result;
}
};
template <typename Row>
template <typename T>
struct item_handle<Row>::item_value_as<T, std::enable_if_t<std::is_integral_v<T> and std::is_unsigned_v<T> and not std::is_same_v<T, bool>>>
{
static T convert(const item_handle &ref)
{
T result = {};
if (not ref.empty())
result = static_cast<T>(std::stoul(ref.c_str()));
return result;
}
static int compare(const item_handle &ref, unsigned long value, bool icase)
{
int result = 0;
const char *s = ref.c_str();
if (s == nullptr or *s == 0)
result = 1;
else
{
try
{
auto v = std::strtoul(s, nullptr, 10);
if (v < value)
result = -1;
else if (v > value)
result = 1;
}
catch (...)
{
// if (VERBOSE)
// std::cerr << "conversion error in compare for '" << ref.c_str() << '\'' << std::endl;
result = 1;
}
}
return result;
}
};
template <typename Row>
template <typename T>
struct item_handle<Row>::item_value_as<T, std::enable_if_t<std::is_integral_v<T> and std::is_signed_v<T> and not std::is_same_v<T, bool>>>
{
static T convert(const item_handle &ref)
{
T result = {};
if (not ref.empty())
result = static_cast<T>(std::stol(ref.c_str()));
return result;
}
static int compare(const item_handle &ref, long value, bool icase)
{
int result = 0;
const char *s = ref.c_str();
if (s == nullptr or *s == 0)
result = 1;
else
{
try
{
auto v = std::strtol(s, nullptr, 10);
if (v < value)
result = -1;
else if (v > value)
result = 1;
}
catch (...)
{
// if (VERBOSE)
// std::cerr << "conversion error in compare for '" << ref.c_str() << '\'' << std::endl;
result = 1;
}
}
return result;
}
};
template <typename Row>
template <typename T>
struct item_handle<Row>::item_value_as<std::optional<T>>
{
static std::optional<T> convert(const item_handle &ref)
{
std::optional<T> result;
if (ref)
result = ref.as<T>();
return result;
}
static int compare(const item_handle &ref, std::optional<T> value, bool icase)
{
if (ref.empty() and not value)
return 0;
if (ref.empty())
return -1;
else if (not value)
return 1;
else
return ref.compare(*value, icase);
}
};
template <typename Row>
template <typename T>
struct item_handle<Row>::item_value_as<T, std::enable_if_t<std::is_same_v<T, bool>>>
{
static bool convert(const item_handle &ref)
{
bool result = false;
if (not ref.empty())
result = iequals(ref.c_str(), "y");
return result;
}
static int compare(const item_handle &ref, bool value, bool icase)
{
bool rv = convert(ref);
return value && rv ? 0
: (rv < value ? -1 : 1);
}
};
template <typename Row>
template <size_t N>
struct item_handle<Row>::item_value_as<char[N]>
{
static const char *convert(const item_handle &ref)
{
return ref.c_str();
}
static int compare(const item_handle &ref, const char (&value)[N], bool icase)
{
return icase ? cif::icompare(ref.c_str(), value) : std::strcmp(ref.c_str(), value);
}
};
template <typename Row>
template <typename T>
struct item_handle<Row>::item_value_as<T, std::enable_if_t<std::is_same_v<T, const char *>>>
{
static const char *convert(const item_handle &ref)
{
return ref.c_str();
}
static int compare(const item_handle &ref, const char *value, bool icase)
{
return icase ? cif::icompare(ref.c_str(), value) : std::strcmp(ref.c_str(), value);
}
};
template <typename Row>
template <typename T>
struct item_handle<Row>::item_value_as<T, std::enable_if_t<std::is_same_v<T, std::string>>>
{
static std::string convert(const item_handle &ref)
{
return ref.c_str();
}
static int compare(const item_handle &ref, const std::string &value, bool icase)
{
return icase ? cif::icompare(ref.c_str(), value) : std::strcmp(ref.c_str(), value.c_str());
}
};
} // namespace cif::v2
...@@ -41,7 +41,7 @@ std::filesystem::path gTestDir = std::filesystem::current_path(); // filled in f ...@@ -41,7 +41,7 @@ std::filesystem::path gTestDir = std::filesystem::current_path(); // filled in f
// -------------------------------------------------------------------- // --------------------------------------------------------------------
cif_v2::file operator""_cf(const char *text, size_t length) cif::v2::file operator""_cf(const char *text, size_t length)
{ {
struct membuf : public std::streambuf struct membuf : public std::streambuf
{ {
...@@ -52,7 +52,7 @@ cif_v2::file operator""_cf(const char *text, size_t length) ...@@ -52,7 +52,7 @@ cif_v2::file operator""_cf(const char *text, size_t length)
} buffer(const_cast<char *>(text), length); } buffer(const_cast<char *>(text), length);
std::istream is(&buffer); std::istream is(&buffer);
return cif_v2::file(is); return cif::v2::file(is);
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
...@@ -78,18 +78,24 @@ bool init_unit_test() ...@@ -78,18 +78,24 @@ bool init_unit_test()
BOOST_AUTO_TEST_CASE(r_1) BOOST_AUTO_TEST_CASE(r_1)
{ {
cif_v2::category c("foo"); cif::v2::row r;
c.emplace({
{ "f-1", 1 }, std::cout << r["f1"].value_or("<leeg>") << std::endl;
{ "f-2", "two" },
{ "f-3", 3.0 }, // cif::v2::category c("foo");
// { "f-4", 3.0, 3 } // c.emplace({
}); // { "f-1", 1 },
// { "f-2", "two" },
cif_v2::datablock db("test"); // { "f-3", 3.0 },
db.emplace_back(std::move(c)); // // { "f-4", 3.0, 3 }
// });
std::cout << db << std::endl;
// cif::v2::datablock db("test");
// db.emplace_back(std::move(c));
// std::cout << db << std::endl;
} }
......
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