# Program Listing for File row.hpp

↰ Return to documentation for file (`cif++/row.hpp`)

    /*-
     * 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 "cif++/item.hpp"

    #include <array>
    #include <cstddef>
    #include <cstdint>
    #include <initializer_list>
    #include <string>
    #include <string_view>
    #include <tuple>
    #include <type_traits>
    #include <utility>
    #include <vector>

    namespace cif
    {

    class category;

    namespace cql
    {
        struct connection_impl;
    }

    namespace detail
    {
        template <typename... C>
        struct get_row_result;
    }

    // --------------------------------------------------------------------

    class row : public std::vector<item_value>
    {
      public:
        row() = default;

      private:
        item_value *get(uint16_t ix)
        {
            if (ix >= size())
                resize(ix + 1);
            return &data()[ix];
        }

        [[nodiscard]] const item_value *get(uint16_t ix) const
        {
            return ix < size() ? &data()[ix] : nullptr;
        }

        void set(uint16_t ix, item_value v)
        {
            if (ix >= size())
                resize(ix + 1);
            operator[](ix) = std::move(v);
        }

        friend class category;
        friend class category_index;

        template <bool, typename...>
        friend class iterator_impl_base;

        row *m_next = nullptr;
    };

    // --------------------------------------------------------------------

    class row_handle
    {
      public:
        template <bool>
        friend struct item_handle_base;
        friend class category;
        friend class category_index;
        friend class row_initializer;
        friend class const_row_handle;

        template <bool, typename...>
        friend class iterator_impl_base;

        row_handle() = default;
        virtual ~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 &cat, row &r)
            : m_category(&cat)
            , m_row(&r)
        {
        }

        [[nodiscard]] category &get_category() const
        {
            return *m_category;
        }

        [[nodiscard]] int64_t row_id() const
        {
            return reinterpret_cast<int64_t>(m_row);
        }

        [[nodiscard]] bool empty() const
        {
            return m_category == nullptr or m_row == nullptr;
        }

        explicit operator bool() const
        {
            return not empty();
        }

        [[nodiscard]] size_t size() const { return m_row->size(); }

        item_handle operator[](uint16_t item_ix)
        {
            return { *m_category, *m_row, item_ix };
        }

        const item_handle operator[](uint16_t item_ix) const
        {
            return { *m_category, *m_row, item_ix };
        }

        item_handle operator[](std::string_view item_name)
        {
            return { *m_category, *m_row, add_item(item_name) };
        }

        const item_handle operator[](std::string_view item_name) const
        {
            return { *m_category, *m_row, get_item_ix(item_name) };
        }

        void assign(const std::vector<item> &values, bool updateLinked = true)
        {
            for (auto &value : values)
                assign(value, updateLinked);
        }

        void assign(std::string_view name, item_value value, bool updateLinked, bool validate = true)
        {
            assign(add_item(name), std::move(value), updateLinked, validate);
        }

        void assign(uint16_t item, item_value value, bool updateLinked, bool validate = true);

        template <typename... C>
        [[nodiscard]] auto get(C... items) const
        {
            return detail::get_row_result<C...>(*this, { get_item_ix(items)... });
        }

        template <typename... Ts, typename... C>
        std::tuple<Ts...> get(C... items) const
            requires(sizeof...(Ts) == sizeof...(C) and sizeof...(C) != 1)
        {
            return detail::get_row_result<Ts...>(*this, { get_item_ix(items)... });
        }

        template <typename T>
        [[nodiscard]] T get(std::string_view item) const
        {
            return operator[](get_item_ix(item)).template get<T>();
        }

        bool operator==(const row_handle &rhs) const { return m_category == rhs.m_category and m_row == rhs.m_row; }

        bool operator!=(const row_handle &rhs) const { return m_category != rhs.m_category or m_row != rhs.m_row; }

      protected:
        [[nodiscard]] uint16_t get_item_ix(std::string_view name) const;

        [[nodiscard]] std::string_view get_item_name(uint16_t ix) const;

        friend cql::connection_impl;

        [[nodiscard]] auto get_row() const
        {
            return m_row;
        }

        category *m_category = nullptr;
        row *m_row = nullptr;

      private:
        uint16_t add_item(std::string_view name);

        void assign(const item &i, bool updateLinked)
        {
            assign(i.name(), i.value(), updateLinked);
        }

    };

    class const_row_handle
    {
      public:
        template <bool>
        friend struct item_handle_base;
        friend class category;
        friend class category_index;
        friend class row_initializer;

        template <bool, typename...>
        friend class iterator_impl_base;

        const_row_handle() = default;
        virtual ~const_row_handle() = default;

        const_row_handle(const const_row_handle &) = default;
        const_row_handle(const_row_handle &&) = default;
        const_row_handle &operator=(const const_row_handle &) = default;
        const_row_handle &operator=(const_row_handle &&) = default;

        const_row_handle(row_handle rh)
            : m_category(rh.m_category)
            , m_row(rh.m_row)
        {
        }

        const_row_handle(const category &cat, const row &r)
            : m_category(&cat)
            , m_row(&r)
        {
        }

        [[nodiscard]] const category &get_category() const
        {
            return *m_category;
        }

        [[nodiscard]] int64_t row_id() const
        {
            return reinterpret_cast<int64_t>(m_row);
        }

        [[nodiscard]] bool empty() const
        {
            return m_category == nullptr or m_row == nullptr;
        }

        explicit operator bool() const
        {
            return not empty();
        }

        [[nodiscard]] size_t size() const { return m_row->size(); }

        const item_handle operator[](uint16_t item_ix) const
        {
            return { *m_category, *m_row, item_ix };
        }

        const item_handle operator[](std::string_view item_name) const
        {
            return operator[](get_item_ix(item_name));
        }

        template <typename... C>
        [[nodiscard]] auto get(C... items) const
        {
            return detail::get_row_result<C...>(*this, { get_item_ix(items)... });
        }

        template <typename... Ts, typename... C>
        std::tuple<Ts...> get(C... items) const
            requires(sizeof...(Ts) == sizeof...(C) and sizeof...(C) != 1)
        {
            return detail::get_row_result<Ts...>(*this, { get_item_ix(items)... });
        }

        template <typename T>
        [[nodiscard]] T get(std::string_view item) const
        {
            return operator[](get_item_ix(item)).template get<T>();
        }

        // bool operator==(const const_row_handle &rhs) const { return m_category == rhs.m_category and m_row == rhs.m_row; }
        friend bool operator==(const_row_handle a, const_row_handle b)
        {
            return a.m_category == b.m_category and a.m_row == b.m_row;
        }

        bool operator!=(const const_row_handle &rhs) const { return m_category != rhs.m_category or m_row != rhs.m_row; }

      protected:
        [[nodiscard]] uint16_t get_item_ix(std::string_view name) const;

        [[nodiscard]] std::string_view get_item_name(uint16_t ix) const;

        friend cql::connection_impl;

        [[nodiscard]] auto get_row() const
        {
            return m_row;
        }

        const category *m_category = nullptr;
        const row *m_row = nullptr;
    };

    namespace detail
    {

        template <typename... C>
        struct get_row_result
        {
            static constexpr std::size_t N = sizeof...(C);

            get_row_result(const_row_handle r, std::array<uint16_t, N> &&items)
                : m_row(std::move(r))
                , m_items(std::move(items))
            {
            }

            const item_handle operator[](uint16_t ix) const
            {
                return m_row[m_items[ix]];
            }

            template <typename... Ts>
            operator std::tuple<Ts...>() const
                requires(N == sizeof...(Ts))
            {
                return get<Ts...>(std::index_sequence_for<Ts...>{});
            }

            template <typename... Ts, std::size_t... Is>
            [[nodiscard]] std::tuple<Ts...> get(std::index_sequence<Is...>) const
            {
                return std::tuple<Ts...>{ m_row[m_items[Is]].template get<Ts>()... };
            }

            const_row_handle m_row;
            std::array<uint16_t, N> m_items;
        };

        // we want to be able to tie some variables to a get_row_result, for this we use tiewraps
        template <typename... Ts>
        struct tie_wrap
        {
            tie_wrap(Ts... args)
                : m_value(args...)
            {
            }

            template <typename RR>
            void operator=(const RR &&rr)
            {
                // get_row_result will do the conversion, but only if the types
                // are compatible. That means the number of parameters to the get()
                // of the row should be equal to the number of items in the tuple
                // you are trying to tie.

                using RType = std::tuple<std::remove_reference_t<Ts>...>;

                m_value = static_cast<RType>(rr);
            }

            std::tuple<Ts...> m_value;
        };

    } // namespace detail

    template <typename... Ts>
    auto tie(Ts &...v)
    {
        return detail::tie_wrap<Ts &...>(std::forward<Ts &>(v)...);
    }

    // --------------------------------------------------------------------

    class row_initializer : public std::vector<item>
    {
      public:
        friend class category;

        row_initializer() = default;
        row_initializer(const row_initializer &) = default;
        row_initializer(row_initializer &&) = default;
        row_initializer &operator=(const row_initializer &) = default;
        row_initializer &operator=(row_initializer &&) = default;

        row_initializer(std::initializer_list<item> items)
            : std::vector<item>(items)
        {
        }

        template <typename ItemIter>
        row_initializer(ItemIter b, ItemIter e)
            requires(std::is_constructible_v<item, typename ItemIter::value_type>)
            : std::vector<item>(b, e)
        {
        }

        row_initializer(row_handle rh)
            : cif::row_initializer(const_row_handle{ rh })
        {
        }

        row_initializer(const_row_handle rh);

        void set_value(std::string name, item_value value);

        void set_value(const item &i)
        {
            set_value(i.name(), i.value());
        }

        void set_value_if_empty(std::string name, item_value value);

        void set_value_if_empty(const item &i)
        {
            set_value_if_empty(i.name(), i.value());
        }

        auto emplace_back(std::string name, item_value value)
        {
            return std::vector<item>::emplace_back(item(std::forward<std::string>(name), std::forward<item_value>(value)));
        }
    };

    } // namespace cif