Commit 877a64ad by Maarten L. Hekkelman

documenting more

parent 0fcf9ed5
......@@ -3,7 +3,7 @@ FILE_PATTERNS = *.hpp
STRIP_FROM_PATH = @DOXYGEN_INPUT_DIR@
RECURSIVE = YES
GENERATE_XML = YES
PREDEFINED += and=&& or=|| not=!
PREDEFINED += and=&& or=|| not=! CIFPP_EXPORT=""
GENERATE_HTML = NO
GENERATE_TODOLIST = NO
INPUT = @DOXYGEN_INPUT_DIR@
......@@ -26,6 +26,12 @@
#pragma once
/// \file category.hpp cif++/category.hpp
/// Documentation for the cif::category class
///
/// The category class should meet the requirements of Container and
/// SequenceContainer.
#include "cif++/forward_decl.hpp"
#include "cif++/condition.hpp"
......@@ -44,7 +50,12 @@ namespace cif
{
// --------------------------------------------------------------------
// special exception
// special exceptions
/// @brief A duplicate_key_error is thrown when an attempt is made
/// to insert a row with values that would introduce a duplicate key
/// in the index. Of course, this can only happen if a @ref category_validator
/// has been defined for this category.
class duplicate_key_error : public std::runtime_error
{
public:
......@@ -54,6 +65,8 @@ class duplicate_key_error : public std::runtime_error
}
};
/// @brief A multiple_results_error is throw when you request a single
/// row using a query but the query contains more than exactly one row.
class multiple_results_error : public std::runtime_error
{
public:
......@@ -66,18 +79,31 @@ class multiple_results_error : public std::runtime_error
// --------------------------------------------------------------------
// These should be moved elsewhere, one day.
template<typename _Tp> inline constexpr bool is_optional_v = false;
template<typename _Tp> inline constexpr bool is_optional_v<std::optional<_Tp>> = true;
/// \cond
template <typename _Tp>
inline constexpr bool is_optional_v = false;
template <typename _Tp>
inline constexpr bool is_optional_v<std::optional<_Tp>> = true;
/// \endcond
// --------------------------------------------------------------------
/// The class category is a sequence container for rows of data values.
/// You could think of it as a std::vector<cif::row_handle> like class.
///
/// A @ref category_validator can be assigned to an object of category
/// after which this class can validate contained data and use an
/// index to keep key values unique.
class category
{
public:
/// \cond
friend class row_handle;
template <typename, typename...>
friend class iterator_impl;
/// \endcond
using value_type = row_handle;
using reference = value_type;
......@@ -85,38 +111,66 @@ class category
using iterator = iterator_impl<category>;
using const_iterator = iterator_impl<const category>;
category() = default;
category(std::string_view name);
category(const category &rhs);
category(category &&rhs);
category &operator=(const category &rhs);
category &operator=(category &&rhs);
category() = default; ///< Default constructor
category(std::string_view name); ///< Constructor taking a \a name
category(const category &rhs); ///< Copy constructor
category(category &&rhs); ///< Move constructor
category &operator=(const category &rhs); ///< Copy assignement operator
category &operator=(category &&rhs); ///< Move assignement operator
/// @brief Destructor
/// @note Please note that the destructor is not virtual. It is assumed that
/// you will not derive from this class.
~category();
// --------------------------------------------------------------------
const std::string &name() const { return m_name; }
iset key_fields() const;
std::set<uint16_t> key_field_indices() const;
const std::string &name() const { return m_name; } ///< Returns the name of the category
iset key_fields() const; ///< Returns the @ref iset of key field names. Retrieved from the @ref category_validator for this category
std::set<uint16_t> key_field_indices() const; ///< Returns a set of indices for the key fields.
/// @brief Set the validator for this category to @a v
/// @param v The category_validator to assign. A nullptr value is allowed.
/// @param db The enclosing @ref datablock
void set_validator(const validator *v, datablock &db);
/// @brief Update the links in this category
/// @param db The enclosing @ref datablock
void update_links(datablock &db);
/// @brief Return the global @ref validator for the data
/// @return The @ref validator or nullptr if not assigned
const validator *get_validator() const { return m_validator; }
/// @brief Return the category validator for this category
/// @return The @ref category_validator or nullptr if not assigned
const category_validator *get_cat_validator() const { return m_cat_validator; }
/// @brief Validate the data stored using the assigned @ref category_validator
/// @return Returns true is all validations pass
bool is_valid() const;
/// @brief Validate links, that means, values in this category should have an
/// accompanying value in parent categories.
///
/// @note
/// The code makes one exception when validating missing links and that's between
/// *atom_site* and a parent *pdbx_poly_seq_scheme* or *entity_poly_seq*.
/// This particular case should be skipped because it is wrong:
/// there are atoms that are not part of a polymer, and thus will have no
/// parent in those categories.
///
/// @return Returns true is all validations pass
bool validate_links() const;
/// @brief Equality operator, returns true if @a rhs is equal to this
/// @param rhs The object to compare with
/// @return True if the data contained is equal
bool operator==(const category &rhs) const;
/// @brief Unequality operator, returns true if @a rhs is not equal to this
/// @param rhs The object to compare with
/// @return True if the data contained is not equal
bool operator!=(const category &rhs) const
{
return not operator==(rhs);
......@@ -124,21 +178,33 @@ class category
// --------------------------------------------------------------------
/// @brief Return a reference to the first row in this category.
/// @return Reference to the first row in this category. The result is undefined if
/// the category is empty.
reference front()
{
return { *this, *m_head };
}
/// @brief Return a const reference to the first row in this category.
/// @return const reference to the first row in this category. The result is undefined if
/// the category is empty.
const_reference front() const
{
return { const_cast<category &>(*this), const_cast<row &>(*m_head) };
}
/// @brief Return a reference to the last row in this category.
/// @return Reference to the last row in this category. The result is undefined if
/// the category is empty.
reference back()
{
return { *this, *m_tail };
}
/// @brief Return a const reference to the last row in this category.
/// @return const reference to the last row in this category. The result is undefined if
/// the category is empty.
const_reference back() const
{
return { const_cast<category &>(*this), const_cast<row &>(*m_tail) };
......@@ -174,11 +240,19 @@ class category
return { *this, nullptr };
}
/// Return a count of the rows in this container
size_t size() const
{
return std::distance(cbegin(), cend());
}
/// Return the theoretical maximum number or rows that can be stored
size_t max_size() const
{
return std::numeric_limits<size_t>::max(); // this is a bit optimistic, I guess
}
/// Return true if the category is empty
bool empty() const
{
return m_head == nullptr;
......@@ -195,6 +269,9 @@ class category
/// @return The row found in the index, or an undefined row_handle
row_handle operator[](const key_type &key);
/// @brief Return a const row_handle for the row specified by \a key
/// @param key The value for the key, fields specified in the dictionary should have a value
/// @return The row found in the index, or an undefined row_handle
const row_handle operator[](const key_type &key) const
{
return const_cast<category *>(this)->operator[](key);
......@@ -202,6 +279,17 @@ class category
// --------------------------------------------------------------------
/// @brief Return a special const iterator for all rows in this category.
/// This iterator can be used in a structured binding context. E.g.:
///
/// @code{.cpp}
/// for (const auto &[name, value] : cat.rows<std::string,int>("item_name", "item_value"))
/// std::cout << name << ": " << value << std::endl;
/// @endcode
///
/// @tparam Ts The types for the columns requested
/// @param names The names for the columns requested
template <typename... Ts, typename... Ns>
iterator_proxy<const category, Ts...> rows(Ns... names) const
{
......@@ -209,6 +297,22 @@ class category
return iterator_proxy<const category, Ts...>(*this, begin(), { names... });
}
/// @brief Return a special iterator for all rows in this category.
/// This iterator can be used in a structured binding context. E.g.:
///
/// @code{.cpp}
/// for (const auto &[name, value] : cat.rows<std::string,int>("item_name", "item_value"))
/// std::cout << name << ": " << value << std::endl;
///
/// // or in case we only need one column:
///
/// for (int id : cat.rows<int>("id"))
/// std::cout << id << std::endl;
/// @endcode
///
/// @tparam Ts The types for the columns requested
/// @param names The names for the columns requested
template <typename... Ts, typename... Ns>
iterator_proxy<category, Ts...> rows(Ns... names)
{
......@@ -218,26 +322,72 @@ class category
// --------------------------------------------------------------------
/// @brief Return a special iterator to loop over all rows that conform to @a cond
///
/// @code{.cpp}
/// for (row_handle rh : cat.find(cif::key("first_name") == "John" and cif::key("last_name") == "Doe"))
/// .. // do something with rh
/// @endcode
///
/// @param cond The @ref condition for the query
/// @return A special iterator that loops over all elements that match. The iterator can be dereferenced
/// to a @ref row_handle
conditional_iterator_proxy<category> find(condition &&cond)
{
return find(begin(), std::move(cond));
}
/// @brief Return a special iterator to loop over all rows that conform to @a cond
/// starting at @a pos
///
/// @param pos Where to start searching
/// @param cond The @ref condition for the query
/// @return A special iterator that loops over all elements that match. The iterator can be dereferenced
/// to a @ref row_handle
conditional_iterator_proxy<category> find(iterator pos, condition &&cond)
{
return { *this, pos, std::move(cond) };
}
/// @brief Return a special const iterator to loop over all rows that conform to @a cond
///
/// @param cond The @ref condition for the query
/// @return A special iterator that loops over all elements that match. The iterator can be dereferenced
/// to a @ref const row_handle
conditional_iterator_proxy<const category> find(condition &&cond) const
{
return find(cbegin(), std::move(cond));
}
/// @brief Return a special const iterator to loop over all rows that conform to @a cond
/// starting at @a pos
///
/// @param pos Where to start searching
/// @param cond The @ref condition for the query
/// @return A special iterator that loops over all elements that match. The iterator can be dereferenced
/// to a @ref const row_handle
conditional_iterator_proxy<const category> find(const_iterator pos, condition &&cond) const
{
return conditional_iterator_proxy<const category>{ *this, pos, std::move(cond) };
}
/// @brief Return a special iterator to loop over all rows that conform to @a cond. The resulting
/// iterator can be used in a structured binding context.
///
/// @code{.cpp}
/// for (const auto &[name, value] : cat.find<std::string,int>(cif::key("item_value") > 10, "item_name", "item_value"))
/// std::cout << name << ": " << value << std::endl;
/// @endcode
///
/// @param cond The @ref condition for the query
/// @tparam Ts The types for the columns requested
/// @param names The names for the columns requested
/// @return A special iterator that loops over all elements that match.
template <typename... Ts, typename... Ns>
conditional_iterator_proxy<category, Ts...> find(condition &&cond, Ns... names)
{
......@@ -245,6 +395,14 @@ class category
return find<Ts...>(cbegin(), std::move(cond), std::forward<Ns>(names)...);
}
/// @brief Return a special const iterator to loop over all rows that conform to @a cond. The resulting
/// iterator can be used in a structured binding context.
///
/// @param cond The @ref condition for the query
/// @tparam Ts The types for the columns requested
/// @param names The names for the columns requested
/// @return A special iterator that loops over all elements that match.
template <typename... Ts, typename... Ns>
conditional_iterator_proxy<const category, Ts...> find(condition &&cond, Ns... names) const
{
......@@ -252,6 +410,15 @@ class category
return find<Ts...>(cbegin(), std::move(cond), std::forward<Ns>(names)...);
}
/// @brief Return a special iterator to loop over all rows that conform to @a cond starting at @a pos.
/// The resulting iterator can be used in a structured binding context.
///
/// @param pos Iterator pointing to the location where to start
/// @param cond The @ref condition for the query
/// @tparam Ts The types for the columns requested
/// @param names The names for the columns requested
/// @return A special iterator that loops over all elements that match.
template <typename... Ts, typename... Ns>
conditional_iterator_proxy<category, Ts...> find(const_iterator pos, condition &&cond, Ns... names)
{
......@@ -259,6 +426,15 @@ class category
return { *this, pos, std::move(cond), std::forward<Ns>(names)... };
}
/// @brief Return a special const iterator to loop over all rows that conform to @a cond starting at @a pos.
/// The resulting iterator can be used in a structured binding context.
///
/// @param pos Iterator pointing to the location where to start
/// @param cond The @ref condition for the query
/// @tparam Ts The types for the columns requested
/// @param names The names for the columns requested
/// @return A special iterator that loops over all elements that match.
template <typename... Ts, typename... Ns>
conditional_iterator_proxy<const category, Ts...> find(const_iterator pos, condition &&cond, Ns... names) const
{
......@@ -269,11 +445,20 @@ class category
// --------------------------------------------------------------------
// if you only expect a single row
/// @brief Return the row handle for the row that matches @a cond Throws @a multiple_results_error if
/// there are is not exactly one row matching @a cond
/// @param cond The condition to search for
/// @return Row handle to the row found
row_handle find1(condition &&cond)
{
return find1(begin(), std::move(cond));
}
/// @brief Return the row handle for the row that matches @a cond starting at @a pos
/// Throws @a multiple_results_error if there are is not exactly one row matching @a cond
/// @param pos The position to start the search
/// @param cond The condition to search for
/// @return Row handle to the row found
row_handle find1(iterator pos, condition &&cond)
{
auto h = find(pos, std::move(cond));
......@@ -284,11 +469,20 @@ class category
return *h.begin();
}
/// @brief Return the const row handle for the row that matches @a cond Throws @a multiple_results_error if
/// there are is not exactly one row matching @a cond
/// @param cond The condition to search for
/// @return Row handle to the row found
const row_handle find1(condition &&cond) const
{
return find1(cbegin(), std::move(cond));
}
/// @brief Return const the row handle for the row that matches @a cond starting at @a pos
/// Throws @a multiple_results_error if there are is not exactly one row matching @a cond
/// @param pos The position to start the search
/// @param cond The condition to search for
/// @return Row handle to the row found
const row_handle find1(const_iterator pos, condition &&cond) const
{
auto h = find(pos, std::move(cond));
......@@ -299,12 +493,26 @@ class category
return *h.begin();
}
/// @brief Return value for the column named @a column for the single row that
/// matches @a cond. Throws @a multiple_results_error if there are is not exactly one row
/// @tparam The type to use for the result
/// @param cond The condition to search for
/// @param column The name of the column to return the value for
/// @return The value found
template <typename T>
T find1(condition &&cond, const char *column) const
{
return find1<T>(cbegin(), std::move(cond), column);
}
/// @brief Return value for the column named @a column for the single row that
/// matches @a cond when starting to search at @a pos.
/// Throws @a multiple_results_error if there are is not exactly one row
/// @tparam The type to use for the result
/// @param pos The location to start the search
/// @param cond The condition to search for
/// @param column The name of the column to return the value for
/// @return The value found
template <typename T, std::enable_if_t<not is_optional_v<T>, int> = 0>
T find1(const_iterator pos, condition &&cond, const char *column) const
{
......@@ -316,6 +524,14 @@ class category
return *h.begin();
}
/// @brief Return a value of type std::optional<T> for the column named @a column for the single row that
/// matches @a cond when starting to search at @a pos.
/// If the row was not found, an empty value is returned.
/// @tparam The type to use for the result
/// @param pos The location to start the search
/// @param cond The condition to search for
/// @param column The name of the column to return the value for
/// @return The value found, can be empty if no row matches the condition
template <typename T, std::enable_if_t<is_optional_v<T>, int> = 0>
T find1(const_iterator pos, condition &&cond, const char *column) const
{
......@@ -330,6 +546,13 @@ class category
return *h.begin();
}
/// @brief Return a std::tuple for the values for the columns named in @a columns
/// for the single row that matches @a cond
/// Throws @a multiple_results_error if there are is not exactly one row
/// @tparam The types to use for the resulting tuple
/// @param cond The condition to search for
/// @param columns The names of the columns to return the value for
/// @return The values found as a single tuple of type std::tuple<Ts...>
template <typename... Ts, typename... Cs, typename U = std::enable_if_t<sizeof...(Ts) != 1>>
std::tuple<Ts...> find1(condition &&cond, Cs... columns) const
{
......@@ -338,6 +561,14 @@ class category
return find1<Ts...>(cbegin(), std::move(cond), std::forward<Cs>(columns)...);
}
/// @brief Return a std::tuple for the values for the columns named in @a columns
/// for the single row that matches @a cond when starting to search at @a pos
/// Throws @a multiple_results_error if there are is not exactly one row
/// @tparam The types to use for the resulting tuple
/// @param pos The location to start the search
/// @param cond The condition to search for
/// @param columns The names of the columns to return the value for
/// @return The values found as a single tuple of type std::tuple<Ts...>
template <typename... Ts, typename... Cs, typename U = std::enable_if_t<sizeof...(Ts) != 1>>
std::tuple<Ts...> find1(const_iterator pos, condition &&cond, Cs... columns) const
{
......@@ -353,11 +584,18 @@ class category
// --------------------------------------------------------------------
// if you want only a first hit
/// @brief Return a row handle to the first row that matches @a cond
/// @param cond The condition to search for
/// @return The handle to the row that matches or an empty row_handle
row_handle find_first(condition &&cond)
{
return find_first(begin(), std::move(cond));
}
/// @brief Return a row handle to the first row that matches @a cond starting at @a pos
/// @param pos The location to start searching
/// @param cond The condition to search for
/// @return The handle to the row that matches or an empty row_handle
row_handle find_first(iterator pos, condition &&cond)
{
auto h = find(pos, std::move(cond));
......@@ -365,11 +603,18 @@ class category
return h.empty() ? row_handle{} : *h.begin();
}
/// @brief Return a const row handle to the first row that matches @a cond
/// @param cond The condition to search for
/// @return The const handle to the row that matches or an empty row_handle
const row_handle find_first(condition &&cond) const
{
return find_first(cbegin(), std::move(cond));
}
/// @brief Return a const row handle to the first row that matches @a cond starting at @a pos
/// @param pos The location to start searching
/// @param cond The condition to search for
/// @return The const handle to the row that matches or an empty row_handle
const row_handle find_first(const_iterator pos, condition &&cond) const
{
auto h = find(pos, std::move(cond));
......@@ -377,12 +622,22 @@ class category
return h.empty() ? row_handle{} : *h.begin();
}
/// @brief Return the value for column @a column for the first row that matches condition @a cond
/// @tparam The type of the value to return
/// @param cond The condition to search for
/// @return The value found or a default constructed value if not found
template <typename T>
T find_first(condition &&cond, const char *column) const
{
return find_first<T>(cbegin(), std::move(cond), column);
}
/// @brief Return the value for column @a column for the first row that matches condition @a cond
/// when starting the search at @a pos
/// @tparam The type of the value to return
/// @param pos The location to start searching
/// @param cond The condition to search for
/// @return The value found or a default constructed value if not found
template <typename T>
T find_first(const_iterator pos, condition &&cond, const char *column) const
{
......@@ -391,6 +646,10 @@ class category
return h.empty() ? T{} : *h.begin();
}
/// @brief Return a tuple containing the values for the columns @a columns for the first row that matches condition @a cond
/// @tparam The types of the values to return
/// @param cond The condition to search for
/// @return The values found or default constructed values if not found
template <typename... Ts, typename... Cs, typename U = std::enable_if_t<sizeof...(Ts) != 1>>
std::tuple<Ts...> find_first(condition &&cond, Cs... columns) const
{
......@@ -399,6 +658,12 @@ class category
return find_first<Ts...>(cbegin(), std::move(cond), std::forward<Cs>(columns)...);
}
/// @brief Return a tuple containing the values for the columns @a columns for the first row that matches condition @a cond
/// when starting the search at @a pos
/// @tparam The types of the values to return
/// @param pos The location to start searching
/// @param cond The condition to search for
/// @return The values found or default constructed values if not found
template <typename... Ts, typename... Cs, typename U = std::enable_if_t<sizeof...(Ts) != 1>>
std::tuple<Ts...> find_first(const_iterator pos, condition &&cond, Cs... columns) const
{
......@@ -410,6 +675,11 @@ class category
// --------------------------------------------------------------------
/// @brief Return the maximum value for column @a column for all rows that match condition @a cond
/// @tparam The type of the value to return
/// @param column The column to use for the value
/// @param cond The condition to search for
/// @return The value found or the minimal value for the type
template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
T find_max(const char *column, condition &&cond) const
{
......@@ -424,12 +694,21 @@ class category
return result;
}
/// @brief Return the maximum value for column @a column for all rows
/// @tparam The type of the value to return
/// @param column The column to use for the value
/// @return The value found or the minimal value for the type
template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
T find_max(const char *column) const
{
return find_max<T>(column, all());
}
/// @brief Return the minimum value for column @a column for all rows that match condition @a cond
/// @tparam The type of the value to return
/// @param column The column to use for the value
/// @param cond The condition to search for
/// @return The value found or the maximum value for the type
template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
T find_min(const char *column, condition &&cond) const
{
......@@ -444,12 +723,20 @@ class category
return result;
}
/// @brief Return the maximum value for column @a column for all rows
/// @tparam The type of the value to return
/// @param column The column to use for the value
/// @param cond The condition to search for
/// @return The value found or the maximum value for the type
template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
T find_min(const char *column) const
{
return find_min<T>(column, all());
}
/// @brief Return whether a row exists that matches condition @a cond
/// @param cond The condition to match
/// @return True if a row exists
bool exists(condition &&cond) const
{
bool result = false;
......@@ -478,6 +765,9 @@ class category
return result;
}
/// @brief Return the total number of rows that match condition @a cond
/// @param cond The condition to match
/// @return The count
size_t count(condition &&cond) const
{
size_t result = 0;
......
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