Commit 8b2e02e1 by Maarten L. Hekkelman

documented row

parent 1addd2be
......@@ -30,6 +30,51 @@
#include <array>
/**
* @file row.hpp
*
* The class cif::row should be an opaque type. It is used to store the
* internal data per row in a category. You should use cif::row_handle
* to get access to the contents in a row.
*
* One could think of rows as vectors of cif::item. But internally
* that's not the case.
*
* You can access the values of stored items by name or index.
* The return value of operator[] is an cif::item_handle object.
*
* @code {.cpp}
* cif::category &atom_site = my_db["atom_site"];
* cif::row_handle rh = atom_site.front();
*
* // by name:
* std::string name = rh["label_atom_id"].as<std::string>();
*
* // by index:
* uint16_t ix = atom_site.get_column_ix("label_atom_id");
* assert(rh[ix].as<std::string() == name);
* @endcode
*
* There some template magic here to allow easy extracting of data
* from rows. This can be done using cif::tie e.g.:
*
* @code {.cpp}
* std::string name;
* float x, y, z;
*
* cif::tie(name, x, y, z) = rh.get("label_atom_id", "cartn_x", "cartn_y", "cartn_z");
* @endcode
*
* However, a more modern way uses structured binding:
*
* @code {.cpp}
* const auto &[name, x, y, z] = rh.get<std::string,float,float,float>("label_atom_id", "cartn_x", "cartn_y", "cartn_z");
* @endcode
*
*
*
*/
namespace cif
{
......@@ -96,6 +141,8 @@ namespace detail
} // namespace detail
/// \brief similar to std::tie, assign values to each element in @a v from the
/// result of a get on a row_handle.
template <typename... Ts>
auto tie(Ts &...v)
{
......@@ -150,6 +197,7 @@ class row : public std::vector<item_value>
class row_handle
{
public:
/** @cond */
friend struct item_handle;
friend class category;
friend class category_index;
......@@ -163,79 +211,119 @@ class row_handle
row_handle &operator=(const row_handle &) = default;
row_handle &operator=(row_handle &&) = default;
/** @endcond */
/// \brief constructor taking a category @a cat and a row @a r
row_handle(const category &cat, const row &r)
: m_category(const_cast<category *>(&cat))
, m_row(const_cast<row *>(&r))
{
}
/// \brief return the category this row belongs to
const category &get_category() const
{
return *m_category;
}
/// \brief Return true if the row is empty or uninitialised
bool empty() const
{
return m_category == nullptr or m_row == nullptr;
}
/// \brief convenience method to test for empty()
explicit operator bool() const
{
return not empty();
}
/// \brief return a cif::item_handle to the item in column @a column_ix
item_handle operator[](uint16_t column_ix)
{
return empty() ? item_handle::s_null_item : item_handle(column_ix, *this);
}
/// \brief return a const cif::item_handle to the item in column @a column_ix
const item_handle operator[](uint16_t column_ix) const
{
return empty() ? item_handle::s_null_item : item_handle(column_ix, const_cast<row_handle &>(*this));
}
/// \brief return a cif::item_handle to the item in the column named @a column_name
item_handle operator[](std::string_view column_name)
{
return empty() ? item_handle::s_null_item : item_handle(add_column(column_name), *this);
}
/// \brief return a const cif::item_handle to the item in the column named @a column_name
const item_handle operator[](std::string_view column_name) const
{
return empty() ? item_handle::s_null_item : item_handle(get_column_ix(column_name), const_cast<row_handle &>(*this));
}
/// \brief Return an object that can be used in combination with cif::tie
/// to assign the values for the columns @a columns
template <typename... C>
auto get(C... columns) const
{
return detail::get_row_result<C...>(*this, { get_column_ix(columns)... });
}
/// \brief Return a tuple of values of types @a Ts for the columns @a columns
template <typename... Ts, typename... C, std::enable_if_t<sizeof...(Ts) == sizeof...(C) and sizeof...(C) != 1, int> = 0>
std::tuple<Ts...> get(C... columns) const
{
return detail::get_row_result<Ts...>(*this, { get_column_ix(columns)... });
}
/// \brief Get the value of column @a column cast to type @a T
template <typename T>
T get(const char *column) const
{
return operator[](get_column_ix(column)).template as<T>();
}
/// \brief assign each of the columns named in @a values to their respective value
void assign(const std::vector<item> &values)
{
for (auto &value : values)
assign(value, true);
}
/** \brief assign the value @a value to the column named @a name
*
* If updateLinked it true, linked records are updated as well.
* That means that if column @name is part of the link definition
* and the link results in a linked record in another category
* this record in the linked category is updated as well.
*
* If validate is true, which is default, the assigned value is
* checked to see if it conforms to the rules defined in the dictionary
*/
void assign(std::string_view name, std::string_view value, bool updateLinked, bool validate = true)
{
assign(add_column(name), value, updateLinked, validate);
}
/** \brief assign the value @a value to column at index @a column
*
* If updateLinked it true, linked records are updated as well.
* That means that if column @a column is part of the link definition
* and the link results in a linked record in another category
* this record in the linked category is updated as well.
*
* If validate is true, which is default, the assigned value is
* checked to see if it conforms to the rules defined in the dictionary
*/
void assign(uint16_t column, std::string_view value, bool updateLinked, bool validate = true);
/// \brief compare two rows
bool operator==(const row_handle &rhs) const { return m_category == rhs.m_category and m_row == rhs.m_row; }
/// \brief compare two rows
bool operator!=(const row_handle &rhs) const { return m_category != rhs.m_category or m_row != rhs.m_row; }
private:
......@@ -267,9 +355,17 @@ class row_handle
// --------------------------------------------------------------------
/**
* @brief The class row_initializer is a list of cif::item's.
*
* This class is used to construct new rows, it allows to
* group a list of item name and value pairs and pass it
* in one go to the constructing function.
*/
class row_initializer : public std::vector<item>
{
public:
/** @cond */
friend class category;
row_initializer() = default;
......@@ -278,26 +374,38 @@ class row_initializer : public std::vector<item>
row_initializer &operator=(const row_initializer &) = default;
row_initializer &operator=(row_initializer &&) = default;
/** @endcond */
/// \brief constructor taking a std::initializer_list of items
row_initializer(std::initializer_list<item> items)
: std::vector<item>(items)
{
}
/// \brief constructor taking a range of items
template <typename ItemIter, std::enable_if_t<std::is_same_v<typename ItemIter::value_type, item>, int> = 0>
row_initializer(ItemIter b, ItemIter e)
: std::vector<item>(b, e)
{
}
/// \brief constructor taking the values of an existing row
row_initializer(row_handle rh);
/// \brief set the value for item name @a name to @a value
void set_value(std::string_view name, std::string_view value);
/// \brief set the value for item based on @a i
void set_value(const item &i)
{
set_value(i.name(), i.value());
}
/// \brief set the value for item name @a name to @a value, but only if the item did not have a value already
void set_value_if_empty(std::string_view name, std::string_view value);
/// \brief set the value for item @a i, but only if the item did not have a value already
void set_value_if_empty(const item &i)
{
set_value_if_empty(i.name(), i.value());
......
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