Skip to content

Commit

Permalink
Query objects
Browse files Browse the repository at this point in the history
  • Loading branch information
vector-of-bool committed Sep 3, 2024
1 parent 3a6c8d9 commit 04d4704
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 12 deletions.
36 changes: 24 additions & 12 deletions src/neo/memory.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "./concepts.hpp"
#include "./declval.hpp"
#include "./fwd.hpp"
#include "./query.hpp"
#include "./type_traits.hpp"

#include <memory>
Expand Down Expand Up @@ -52,23 +53,34 @@ namespace _memory_detail {
template <typename T>
constexpr bool has_value_type = requires { typename T::value_type; };

template <typename T>
constexpr bool has_get_allocator = requires(T & t) { t.get_allocator(); };

} // namespace _memory_detail

/**
* @brief Query object type that obtains the allocator from an object
*/
struct get_allocator_q : query_interface<get_allocator_q> {
static constexpr auto exec(const auto& obj) noexcept
requires requires { obj.get_allocator(); }
{
return obj.get_allocator();
}
};

/**
* @brief Query object that obtains the allocator associated with an object
*/
inline constexpr get_allocator_q get_allocator;

template <typename T>
constexpr auto allocator_of(T&& t, auto alloc = std::allocator<void>{}) noexcept {
if constexpr (_memory_detail::has_get_allocator<T>) {
return t.get_allocator();
} else if constexpr (_memory_detail::has_value_type<T>) {
return rebind_alloc<typename T::value_type>(alloc);
} else {
return alloc;
}
return query_or(get_allocator, t, alloc);
}

template <typename T>
using get_allocator_t = decltype(allocator_of(NEO_DECLVAL(T)));
/**
* @brief Match a type that is an allocator for the given object, according to
* std::uses_allocator_v
*/
template <typename Allocator, typename T>
concept allocator_for = std::uses_allocator_v<T, Allocator>;

} // namespace neo
107 changes: 107 additions & 0 deletions src/neo/query.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#pragma once

#include "./concepts.hpp"
#include "./declval.hpp"
#include "./type_traits.hpp"

namespace neo {

/**
* @brief Match a query object
*
* @tparam Q A type that can be used as a query
*
* The semantics of the given object require that all instances are equivalent
* and that there exist an `operator()` that returns an appropriate query result.
*/
template <typename Q>
concept query_object = neo::copyable<Q>;

/**
* @brief Match a query type object that can be executed on the given object
*
* @tparam Q A query type to be tested
* @tparam T The primary argument object to be queried
* @tparam Args Supplemental arguments to the query call
*/
template <typename Q, typename T, typename... Args>
concept valid_query_for = query_object<Q> and requires(const Q q, const T obj, Args&&... args) {
{ q(obj, static_cast<Args&&>(args)...) } noexcept -> detail::can_reference;
};

/**
* @brief Obtain the result of executing the given query on an object
*/
template <query_object Q, typename T, typename... Args>
requires valid_query_for<Q, T, Args...>
using query_t = decltype(NEO_DECLVAL(const Q&)(NEO_DECLVAL(const T&), NEO_DECLVAL(Args&&)...));

/**
* @brief Implement a query interface that supports a direct query and an indirect query
*
* @tparam Derived The derived type, or `void` to deduce the instance type
*
* When invoked, attempts to call a `exec()` static method on the derived class that
* should be constrained appropriately to execute the direct query. If the
* direct query via `exec()` is not supported, attempts to invoke `obj.query(*this, ...)`
* on the queried object.
*/
template <typename Derived = void>
class query_interface {
private:
constexpr const Derived& _self() const noexcept { return static_cast<const Derived&>(*this); }

public:
template <typename Object, typename... Args>
constexpr decltype(auto) operator()(const Object& obj, Args&&... args) const noexcept
requires requires { Derived::exec(obj, NEO_FWD(args)...); }
{
return Derived::exec(obj, NEO_FWD(args)...);
}

template <typename Object, typename... Args>
constexpr decltype(auto) operator()(const Object& obj, Args&&... args) const noexcept
requires requires(const Derived& q) {
obj.query(q, NEO_FWD(args)...);
requires not requires { Derived::exec(obj, NEO_FWD(args)...); };
}
{
return obj.query(static_cast<const Derived&>(*this), NEO_FWD(args)...);
}
};

#ifdef __cpp_explicit_this_parameter
template <>
class query_interface<void> {
public:
template <typename Self, typename Object, typename... Args>
constexpr decltype(auto)
operator()(this Self const&, const Object& obj, Args&&... args) noexcept
requires requires { Self::exec(obj, NEO_FWD(args)...); }
{
return Self::exec(obj, NEO_FWD(args)...);
}

template <typename Self, typename Object, typename... Args>
constexpr decltype(auto)
operator()(this Self const& self, const Object& obj, Args&&... args) noexcept
requires requires {
obj.query(self, NEO_FWD(args)...);
requires not requires { Self::exec(obj, NEO_FWD(args)...); };
}
{
return obj.query(self, NEO_FWD(args)...);
}
};
#endif

template <query_object Q, typename T, typename Default>
constexpr decltype(auto) query_or(Q q, const T& obj, Default&& dflt) {
if constexpr (valid_query_for<Q, T>) {
return q(obj);
} else {
return static_cast<Default>(dflt);
}
}

} // namespace neo
13 changes: 13 additions & 0 deletions src/neo/query.test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#include "./query.hpp"
#include <vector>

struct get_allocator : neo::query_interface<get_allocator> {
static constexpr auto exec(auto const& obj)
requires requires { obj.get_allocator(); }
{
return obj.get_allocator();
}
};

static_assert(neo::valid_query_for<get_allocator, std::vector<int>>);
static_assert(not neo::valid_query_for<get_allocator, std::array<int, 4>>);

0 comments on commit 04d4704

Please sign in to comment.