Program Listing for File Objects.h

Return to documentation for file (uconfig/Objects.h)

#pragma once

#include "detail/detail.h"

#include <memory>
#include <optional>
#include <stdexcept>
#include <typeindex>
#include <unordered_set>
#include <vector>

namespace uconfig {

struct Error: public std::runtime_error
{
    using std::runtime_error::runtime_error;
};

struct ParseError: public Error
{
    using Error::Error;
};

struct EmitError: public Error
{
    using Error::Error;
};

class Object
{
public:
    // template <typename F>
    // using iface_type;

    virtual ~Object() = default;

    virtual bool Initialized() const noexcept = 0;
    virtual bool Optional() const noexcept = 0;
    virtual void Validate() const {};
};

template <typename... FormatTs>
class Config: public Object
{
public:
    template <typename F>
    using iface_type = ConfigIface<F>;

    template <typename F>
    friend class ConfigIface;

    Config(bool optional = false);

    Config(const Config<FormatTs...>& other);
    Config<FormatTs...>& operator=(const Config<FormatTs...>& other);
    Config(Config<FormatTs...>&& other) noexcept;
    Config<FormatTs...>& operator=(Config<FormatTs...>&& other) noexcept;

    virtual ~Config() = default;

    template <typename F>
    bool Parse(const F& parser, const std::string& path, const typename F::source_type* source,
               bool throw_on_fail = true);

    template <typename F>
    void Emit(const F& emitter, const std::string& path, typename F::dest_type* destination, bool throw_on_fail = true);

    virtual bool Initialized() const noexcept override;

    virtual bool Optional() const noexcept override;

protected:
    virtual void Init(const std::string& config_path) = 0;

    template <typename F, typename T>
    void Register(const std::string& element_path, T* element) noexcept;

private:
    void Reset() noexcept;

    template <typename F>
    void SetFormat() noexcept;

    template <typename F>
    std::vector<std::unique_ptr<Interface<F>>>& Interfaces() noexcept;

private:
    bool optional_ = false;
    std::unordered_set<Object*> elements_;
    std::unordered_set<std::type_index> register_formats_;
    std::tuple<std::vector<std::unique_ptr<Interface<FormatTs>>>...> interfaces_;
};

template <typename T>
class Variable: public Object
{
public:
    template <typename F>
    using iface_type = VariableIface<T, F>;

    template <typename U, typename F>
    friend class VariableIface;

    Variable();
    Variable(T&& init_value);
    Variable(const T& init_value);

    Variable(const Variable<T>&) = default;
    Variable<T>& operator=(const Variable<T>&) = default;
    Variable(Variable<T>&& other) noexcept = default;
    Variable<T>& operator=(Variable<T>&& other) noexcept = default;
    Variable<T>& operator=(T&& other) noexcept;

    virtual ~Variable() = default;

    virtual bool Initialized() const noexcept override;

    virtual bool Optional() const noexcept override;

    T& Get();

    const T& Get() const;

    T& operator*();

    const T& operator*() const;

    T* operator->();

    const T* operator->() const;

    explicit operator T&();

    explicit operator const T&() const;

#ifndef DOXYGEN_SHOULD_SKIP_THIS
    /* enable left and right-handed comparisons */

    template <typename V, typename U>
    friend bool operator==(const Variable<V>& lhs, const Variable<U>& rhs);
    template <typename V, typename U, std::enable_if_t<!detail::is_base_of_template<U, Variable>::value, bool>>
    friend bool operator==(const U& lhs, const Variable<V>& rhs);
    template <typename V, typename U, std::enable_if_t<!detail::is_base_of_template<U, Variable>::value, bool>>
    friend bool operator==(const Variable<V>& lhs, const U& rhs);

    template <typename V, typename U>
    friend bool operator!=(const Variable<V>& lhs, const Variable<U>& rhs);
    template <typename V, typename U, std::enable_if_t<!detail::is_base_of_template<U, Variable>::value, bool>>
    friend bool operator!=(const U& lhs, const Variable<V>& rhs);
    template <typename V, typename U, std::enable_if_t<!detail::is_base_of_template<U, Variable>::value, bool>>
    friend bool operator!=(const Variable<V>& lhs, const U& rhs);

    template <typename V, typename U>
    friend bool operator>(const U& lhs, const Variable<V>& rhs);
    template <typename V, typename U>
    friend bool operator>(const Variable<V>& lhs, const U& rhs);

    template <typename V, typename U>
    friend bool operator<(const U& lhs, const Variable<V>& rhs);
    template <typename V, typename U>
    friend bool operator<(const Variable<V>& lhs, const U& rhs);

    template <typename V, typename U>
    friend bool operator>=(const U& lhs, const Variable<V>& rhs);
    template <typename V, typename U>
    friend bool operator>=(const Variable<V>& lhs, const U& rhs);

    template <typename V, typename U>
    friend bool operator<=(const U& lhs, const Variable<V>& rhs);
    template <typename V, typename U>
    friend bool operator<=(const Variable<V>& lhs, const U& rhs);

#endif /* DOXYGEN_SHOULD_SKIP_THIS */

protected:
    bool optional_ = false;
    std::optional<T> value_ = std::nullopt;
};

template <typename T>
class Vector: public Variable<std::vector<T>>
{
public:
    template <typename F>
    using iface_type = VectorIface<T, F>;

    template <typename U, typename F>
    friend class VectorIface;

    Vector(bool optional = false);
    Vector(std::vector<T>&& init_value);
    Vector(const std::vector<T>& init_value);

    Vector(const Vector<T>&) = default;
    Vector<T>& operator=(const Vector<T>&) = default;
    Vector(Vector<T>&& other) noexcept = default;
    Vector<T>& operator=(Vector<T>&& other) noexcept = default;
    Vector<T>& operator=(std::vector<T>&& vector) noexcept;

    virtual ~Vector() = default;

    T& operator[](std::size_t pos);

    const T& operator[](std::size_t pos) const;

#ifndef DOXYGEN_SHOULD_SKIP_THIS

    // operator== for Vector<Variable<V>> and std::vector<V>.
    template <typename V>
    friend bool operator==(const Vector<Variable<V>>& lhs, const std::vector<V>& rhs);

#endif /* DOXYGEN_SHOULD_SKIP_THIS */
};

} // namespace uconfig

#include "impl/Objects.ipp"