diff --git a/src/examples/Makefile b/src/examples/Makefile new file mode 100644 index 0000000..5250c12 --- /dev/null +++ b/src/examples/Makefile @@ -0,0 +1,16 @@ +CXX=g++-13 + +SOURCES += $(wildcard *.cpp) +TARGETS := $(patsubst %.cpp, %, $(SOURCES)) + +INCLUDE_FLAGS = -I../ -I../graphblas/platforms/sequential -I../interfaces/spec + +CXXFLAGS = -std=c++2b -O3 $(INCLUDE_FLAGS) + +all: $(TARGETS) + +%: %.cpp + $(CXX) $(CXXFLAGS) -o $@ $^ $(CXXFLAGS) + +clean: + rm -fv $(TARGETS) diff --git a/src/examples/ewise.cpp b/src/examples/ewise.cpp new file mode 100644 index 0000000..20f95db --- /dev/null +++ b/src/examples/ewise.cpp @@ -0,0 +1,35 @@ +#include +#include + +//**************************************************************************** +int main(int argc, char** argv) { + spec::matrix a({10, 10}); + spec::matrix b({10, 10}); + spec::matrix c({10, 10}); + spec::matrix d({10, 10}); + + a[{2, 3}] = 12; + b[{2, 3}] = 12; + + a[{1, 8}] = 7; + b[{1, 8}] = 4; + + a[{7, 3}] = 2; + b[{4, 3}] = 2; + + std::cout << "Matrix: a\n"; + a.printInfo(std::cout); + std::cout << "Matrix: b\n"; + b.printInfo(std::cout); + + spec::ewise_intersection(c, a, b, spec::times{}); + + std::cerr << "Intersection (times) result: \n"; + c.printInfo(std::cout); + + spec::ewise_union(d, a, b, spec::plus{}); + + std::cerr << "Union (plus) result: \n"; + d.printInfo(std::cout); + return 0; +} diff --git a/src/examples/test.cpp b/src/examples/test.cpp new file mode 100644 index 0000000..0c9f3c9 --- /dev/null +++ b/src/examples/test.cpp @@ -0,0 +1,25 @@ +#include + +int main(int argc, char** argv) { + spec::matrix m({100, 100}); + + // Write to missing element. + m[{4, 4}] = 12; + + // Access present element. + int v = m[{4, 4}]; + std::cout << v << std::endl; + + // Access missing element. + int g = m[{4, 3}]; + std::cout << g << std::endl; + + // Write to present element. + m[{4, 3}] = 12; + + g = m[{4, 3}]; + std::cout << g << std::endl; + + + return 0; +} diff --git a/src/interfaces/spec/grb/algorithms/algorithms.hpp b/src/interfaces/spec/grb/algorithms/algorithms.hpp new file mode 100644 index 0000000..77a0fcb --- /dev/null +++ b/src/interfaces/spec/grb/algorithms/algorithms.hpp @@ -0,0 +1,5 @@ +#pragma once + +// #include "multiply.hpp" +#include "ewise_intersection.hpp" +#include "ewise_union.hpp" diff --git a/src/interfaces/spec/grb/algorithms/ewise_intersection.hpp b/src/interfaces/spec/grb/algorithms/ewise_intersection.hpp new file mode 100644 index 0000000..95a2a17 --- /dev/null +++ b/src/interfaces/spec/grb/algorithms/ewise_intersection.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include "../detail/detail.hpp" +#include "../util/util.hpp" +#include "../functional/functional.hpp" +#include "../matrix.hpp" +#include "../views/views.hpp" + +namespace GRB_SPEC_NAMESPACE { + +// NOTE: concepts are missing because `GRB_SPEC_NAMESPACE::matrix` does not +// satisfy iteration yet. +template , + typename Accumulate = GRB_SPEC_NAMESPACE::take_right<> + > +void ewise_intersection(C&& c, A&& a, B&& b, + Combine&& combine, + M&& mask = M{}, + Accumulate&& acc = Accumulate{}, + bool merge = false) +{ + auto merge_enum = ((merge) ? + GBTL_NAMESPACE::OutputControlEnum::MERGE : + GBTL_NAMESPACE::OutputControlEnum::REPLACE); + if constexpr(std::is_same_v>) + { + GBTL_NAMESPACE::eWiseMult(c.backend_, + GBTL_NAMESPACE::NoMask(), acc, + combine, a.backend_, b.backend_, merge_enum); + } else + { + GBTL_NAMESPACE::eWiseMult(c.backend_, + mask.backend_, acc, + combine, a.backend_, b.backend_, merge_enum); + } +} + +} // GRB_SPEC_NAMESPACE diff --git a/src/interfaces/spec/grb/algorithms/ewise_union.hpp b/src/interfaces/spec/grb/algorithms/ewise_union.hpp new file mode 100644 index 0000000..24afd39 --- /dev/null +++ b/src/interfaces/spec/grb/algorithms/ewise_union.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include "../detail/detail.hpp" +#include "../util/util.hpp" +#include "../functional/functional.hpp" +#include "../matrix.hpp" +#include "../views/views.hpp" + +namespace GRB_SPEC_NAMESPACE { + +// NOTE: concepts are missing because `GRB_SPEC_NAMESPACE::matrix` does not +// satisfy iteration yet. +template , + typename Accumulate = GRB_SPEC_NAMESPACE::take_right<> + > +void ewise_union(C&& c, A&& a, B&& b, + Combine&& combine, + M&& mask = M{}, + Accumulate&& acc = Accumulate{}, + bool merge = false) +{ + auto merge_enum = ((merge) ? + GBTL_NAMESPACE::OutputControlEnum::MERGE : + GBTL_NAMESPACE::OutputControlEnum::REPLACE); + if constexpr(std::is_same_v>) + { + GBTL_NAMESPACE::eWiseAdd(c.backend_, + GBTL_NAMESPACE::NoMask(), acc, + combine, a.backend_, b.backend_, merge_enum); + } else + { + GBTL_NAMESPACE::eWiseAdd(c.backend_, + mask.backend_, acc, + combine, a.backend_, b.backend_, merge_enum); + } +} + +} // GRB_SPEC_NAMESPACE diff --git a/src/interfaces/spec/grb/algorithms/multiply.hpp b/src/interfaces/spec/grb/algorithms/multiply.hpp new file mode 100644 index 0000000..6550b60 --- /dev/null +++ b/src/interfaces/spec/grb/algorithms/multiply.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include "../detail/detail.hpp" +#include "../util/util.hpp" +#include "../matrix.hpp" + +namespace GRB_SPEC_NAMESPACE { + +template , GRB_SPEC_NAMESPACE::matrix_scalar_t> Combine = GRB_SPEC_NAMESPACE::multiplies<>, + BinaryOperator, + GRB_SPEC_NAMESPACE::elementwise_return_type_t, + GRB_SPEC_NAMESPACE::elementwise_return_type_t> Reduce = GRB_SPEC_NAMESPACE::plus<>, + MaskMatrixRange M = GRB_SPEC_NAMESPACE::full_matrix_mask<>> +auto multiply(A&& a, + B&& b, + Reduce&& reduce = Reduce{}, + Combine&& combine = Combine{}, + M&& mask = GRB_SPEC_NAMESPACE::full_matrix_mask()) +{ + using T = GRB_SPEC_NAMESPACE::elementwise_return_type_t; + matrix c(a.shape()[0], b.shape()[1]); + multiply(c, a, b, reduce, combine, mask); +} + +} // end GRB_SPEC_NAMESPACE \ No newline at end of file diff --git a/src/interfaces/spec/grb/detail/concepts.hpp b/src/interfaces/spec/grb/detail/concepts.hpp new file mode 100644 index 0000000..ce50e42 --- /dev/null +++ b/src/interfaces/spec/grb/detail/concepts.hpp @@ -0,0 +1,105 @@ + +#pragma once + +#include + +#include "matrix_traits.hpp" +#include "cpos.hpp" +#include "get.hpp" +#include "namespace_macros.hpp" + +namespace GRB_SPEC_NAMESPACE { + +template +concept TupleElementGettable = requires(T tuple) { + {GRB_SPEC_NAMESPACE::get(tuple)} -> std::convertible_to; + }; + +template +concept TupleLike = + requires { + typename std::tuple_size>::type; + requires std::same_as>)>, std::size_t>; + } && + sizeof...(Args) == std::tuple_size_v> && + [](std::index_sequence) { + return (TupleElementGettable && ...); + }(std::make_index_sequence>>()); + +template +concept MatrixEntry = TupleLike && + requires(Entry entry) { {GRB_SPEC_NAMESPACE::get<0>(entry)} -> TupleLike; } && + requires(Entry entry) { {GRB_SPEC_NAMESPACE::get<1>(entry)} -> std::convertible_to; }; + +template +concept MutableMatrixEntry = MatrixEntry && + std::is_assignable_v(std::declval())), U>; + +template +concept MatrixRange = std::ranges::sized_range && + requires(M matrix) { + typename container_traits>; + // typename GRB_SPEC_NAMESPACE::matrix_scalar_t; + // typename GRB_SPEC_NAMESPACE::matrix_index_t; + {std::declval>()} + -> MatrixEntry, + GRB_SPEC_NAMESPACE::matrix_index_t>; + {GRB_SPEC_NAMESPACE::shape(matrix)} -> TupleLike, + GRB_SPEC_NAMESPACE::matrix_index_t>; + {GRB_SPEC_NAMESPACE::find(matrix, {GRB_SPEC_NAMESPACE::matrix_index_t{}, GRB_SPEC_NAMESPACE::matrix_index_t{}})} -> std::convertible_to>; + }; + +template +concept MutableMatrixRange = GRB_SPEC_NAMESPACE::MatrixRange && + GRB_SPEC_NAMESPACE::MutableMatrixEntry, + GRB_SPEC_NAMESPACE::matrix_scalar_t, + GRB_SPEC_NAMESPACE::matrix_index_t, + T> && + requires(M matrix, T value) { + {GRB_SPEC_NAMESPACE::insert(matrix, {{GRB_SPEC_NAMESPACE::matrix_index_t{}, GRB_SPEC_NAMESPACE::matrix_index_t{}}, value})} + -> std::same_as, bool>>; + } && + std::is_constructible_v, T>; + +template +concept MaskMatrixRange = MatrixRange && + std::is_convertible_v, bool>; + +template +concept VectorEntry = TupleLike && + requires(Entry entry) { {GRB_SPEC_NAMESPACE::get<0>(entry)} -> std::integral; } && + requires(Entry entry) { {GRB_SPEC_NAMESPACE::get<1>(entry)} -> std::convertible_to; }; + +template +concept MutableVectorEntry = VectorEntry && + std::is_assignable_v(std::declval())), U>; + +template +concept VectorRange = std::ranges::sized_range && + requires(V vector) { + typename GRB_SPEC_NAMESPACE::vector_scalar_t; + typename GRB_SPEC_NAMESPACE::vector_index_t; + {std::declval>()} + -> VectorEntry, + GRB_SPEC_NAMESPACE::vector_index_t>; + {GRB_SPEC_NAMESPACE::shape(vector)} -> std::same_as>; + {GRB_SPEC_NAMESPACE::find(vector, GRB_SPEC_NAMESPACE::vector_index_t{})} -> std::convertible_to>; +}; + +template +concept MutableVectorRange = VectorRange && + MutableVectorEntry, + GRB_SPEC_NAMESPACE::vector_scalar_t, + GRB_SPEC_NAMESPACE::vector_index_t, + T> && + requires(V vector, T value) { + {GRB_SPEC_NAMESPACE::insert(vector, {GRB_SPEC_NAMESPACE::vector_index_t{}, GRB_SPEC_NAMESPACE::vector_scalar_t{}})} + -> std::same_as, bool>>; + } && + std::is_constructible_v, T>; + +template +concept MaskVectorRange = VectorRange && + std::is_convertible_v, bool>; + +} // end GRB_SPEC_NAMESPACE \ No newline at end of file diff --git a/src/interfaces/spec/grb/detail/cpos.hpp b/src/interfaces/spec/grb/detail/cpos.hpp new file mode 100644 index 0000000..e54a08e --- /dev/null +++ b/src/interfaces/spec/grb/detail/cpos.hpp @@ -0,0 +1,107 @@ +#pragma once + +#include +#include +#include +#include +#include +#include "tag_invoke.hpp" +#include "matrix_traits.hpp" + +namespace GRB_SPEC_NAMESPACE { + +// Helper concepts for CPOs. + +namespace { + +template +concept has_matrix_shape = requires(T t) { {t.shape()} -> std::same_as>::type>; }; + +template +concept has_vector_shape = requires(T t) { {t.shape()} -> std::same_as>; }; + +template +concept has_find_method = requires(T t) { {t.find(std::declval::key_type>())} -> std::same_as::iterator>; }; + +template +concept has_insert_method = requires(T t) { {t.insert({std::declval::key_type>(), std::declval>()})}; }; + +template +concept has_insert_or_assign_method = requires(T t, M obj) { {t.insert_or_assign(std::declval::key_type>(), std::forward(obj))}; }; + +} // end anonymous + +inline constexpr struct shape_fn_ { + template + auto operator()(T&& x) const + requires(GRB_SPEC_NAMESPACE::is_tag_invocable_v || + has_matrix_shape || + has_vector_shape) + { + if constexpr(GRB_SPEC_NAMESPACE::is_tag_invocable_v) { + return GRB_SPEC_NAMESPACE::tag_invoke(*this, std::forward(x)); + } else if constexpr(has_matrix_shape) { + return std::forward(x).shape(); + } else if constexpr(has_vector_shape) { + return std::forward(x).shape(); + } + } +} shape{}; + +inline constexpr struct size_fn_ { + template + auto operator()(T&& x) const + requires(GRB_SPEC_NAMESPACE::is_tag_invocable_v || + std::ranges::sized_range) + { + if constexpr(GRB_SPEC_NAMESPACE::is_tag_invocable_v) { + return GRB_SPEC_NAMESPACE::tag_invoke(*this, std::forward(x)); + } else if constexpr(std::ranges::sized_range) { + return std::ranges::size(std::forward(x)); + } + } +} size{}; + +inline constexpr struct find_fn_ { + template + auto operator()(T&& x, typename GRB_SPEC_NAMESPACE::container_traits::key_type key) const + requires(GRB_SPEC_NAMESPACE::is_tag_invocable_v::key_type> || + has_find_method) + { + if constexpr(GRB_SPEC_NAMESPACE::is_tag_invocable_v::key_type>) { + return GRB_SPEC_NAMESPACE::tag_invoke(*this, std::forward(x), key); + } else if constexpr(has_find_method) { + return std::forward(x).find(key); + } + } +} find{}; + +inline constexpr struct insert_fn_ { + template + auto operator()(T&& x, const container_value_t& entry) const + requires(GRB_SPEC_NAMESPACE::is_tag_invocable_v&> || + has_insert_method) + { + if constexpr(GRB_SPEC_NAMESPACE::is_tag_invocable_v&>) { + return GRB_SPEC_NAMESPACE::tag_invoke(*this, std::forward(x), entry); + } else if constexpr(has_insert_method) { + return std::forward(x).insert(entry); + } + } +} insert{}; + +inline constexpr struct insert_or_assign_fn_ { + template + auto operator()(T&& x, typename GRB_SPEC_NAMESPACE::container_traits::key_type key, M&& obj) const + requires(GRB_SPEC_NAMESPACE::is_tag_invocable_v::key_type, M> || + has_insert_or_assign_method) + { + if constexpr(GRB_SPEC_NAMESPACE::is_tag_invocable_v::key_type, M>) { + return GRB_SPEC_NAMESPACE::tag_invoke(*this, std::forward(x), key, std::forward(obj)); + } else if constexpr(has_insert_or_assign_method) { + return std::forward(x).insert_or_assign(key, std::forward(obj)); + } + } +} insert_or_assign{}; + +} // end GRB_SPEC_NAMESPACE \ No newline at end of file diff --git a/src/interfaces/spec/grb/detail/detail.hpp b/src/interfaces/spec/grb/detail/detail.hpp new file mode 100644 index 0000000..587cad5 --- /dev/null +++ b/src/interfaces/spec/grb/detail/detail.hpp @@ -0,0 +1,6 @@ + +#pragma once + +#include "namespace_macros.hpp" +#include "concepts.hpp" +#include "iterator_adaptor.hpp" \ No newline at end of file diff --git a/src/interfaces/spec/grb/detail/get.hpp b/src/interfaces/spec/grb/detail/get.hpp new file mode 100644 index 0000000..64027a7 --- /dev/null +++ b/src/interfaces/spec/grb/detail/get.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include +#include + +#include "namespace_macros.hpp" + +namespace GRB_SPEC_NAMESPACE { + +template +concept method_gettable = requires(T tuple) { + {tuple. template get()} -> std::convertible_to; +}; + +template +concept adl_gettable = requires(T tuple) { + {get(tuple)} -> std::convertible_to; +}; + +template +concept std_gettable = requires(T tuple) { + {std::get(tuple)} -> std::convertible_to; +}; + +template +inline constexpr decltype(auto) get(T&& tuple) +requires(method_gettable) +{ + return std::forward(tuple). template get(); +} + +template +inline constexpr decltype(auto) get(T&& tuple) +requires(!method_gettable && std_gettable) +{ + return std::get(std::forward(tuple)); +} + +} // end GRB_SPEC_NAMESPACE \ No newline at end of file diff --git a/src/interfaces/spec/grb/detail/iterator_adaptor.hpp b/src/interfaces/spec/grb/detail/iterator_adaptor.hpp new file mode 100644 index 0000000..4a3a50b --- /dev/null +++ b/src/interfaces/spec/grb/detail/iterator_adaptor.hpp @@ -0,0 +1,183 @@ +#pragma once + +#include +#include + +namespace GRB_SPEC_NAMESPACE { + +namespace detail { + +template class iterator_adaptor { +public: + using accessor_type = Accessor; + using const_accessor_type = typename Accessor::const_iterator_accessor; + using nonconst_accessor_type = typename Accessor::nonconst_iterator_accessor; + + using difference_type = typename Accessor::difference_type; + using value_type = typename Accessor::value_type; + using iterator = iterator_adaptor; + using const_iterator = iterator_adaptor; + using reference = typename Accessor::reference; + using iterator_category = typename Accessor::iterator_category; + + using nonconst_iterator = iterator_adaptor; + + static_assert(std::is_same_v>); + + iterator_adaptor() = default; + ~iterator_adaptor() = default; + iterator_adaptor(const iterator_adaptor &) = default; + iterator_adaptor &operator=(const iterator_adaptor &) = default; + + template + requires( + sizeof...(Args) >= 1 && + !((sizeof...(Args) == 1 && + (std::is_same_v> || ...)) || + (std::is_same_v> || ...) || + (std::is_same_v> || ...) || + (std::is_same_v> || ...)) && + std::is_constructible_v) + iterator_adaptor(Args &&...args) : accessor_(std::forward(args)...) {} + + iterator_adaptor(const accessor_type &accessor) : accessor_(accessor) {} + iterator_adaptor(const const_accessor_type &accessor) + requires(!std::is_same_v) + : accessor_(accessor) {} + + operator const_iterator() const + requires(!std::is_same_v) + { + return const_iterator(accessor_); + } + + bool operator==(const_iterator other) const { + return accessor_ == other.accessor_; + } + + bool operator!=(const_iterator other) const { return !(*this == other); } + + bool operator<(const_iterator other) const + requires(std::is_same_v) + { + return accessor_ < other.accessor_; + } + + bool operator<=(const_iterator other) const + requires(std::is_same_v) + { + return *this < other || *this == other; + } + + bool operator>(const_iterator other) const + requires(std::is_same_v) + { + return !(*this <= other); + } + + bool operator>=(const_iterator other) const + requires(std::is_same_v) + { + return !(*this < other); + } + + reference operator*() const { return *accessor_; } + + reference operator[](difference_type offset) const + requires(std::is_same_v) + { + return *(*this + offset); + } + + iterator &operator+=(difference_type offset) noexcept + requires(std::is_same_v) + { + accessor_ += offset; + return *this; + } + + iterator &operator-=(difference_type offset) noexcept + requires(std::is_same_v) + { + accessor_ += -offset; + return *this; + } + + iterator operator+(difference_type offset) const + requires(std::is_same_v) + { + iterator other = *this; + other += offset; + return other; + } + + iterator operator-(difference_type offset) const + requires(std::is_same_v) + { + iterator other = *this; + other += -offset; + return other; + } + + difference_type operator-(const_iterator other) const + requires(std::is_same_v) + { + return accessor_ - other.accessor_; + } + + iterator &operator++() noexcept + requires(std::is_same_v) + { + *this += 1; + return *this; + } + + iterator &operator++() noexcept + requires( + !std::is_same_v) + { + ++accessor_; + return *this; + } + + iterator operator++(int) noexcept { + iterator other = *this; + ++(*this); + return other; + } + + iterator &operator--() noexcept + requires( + std::is_same_v || + std::is_same_v) + { + *this += -1; + return *this; + } + + iterator operator--(int) noexcept + requires( + std::is_same_v || + std::is_same_v) + { + iterator other = *this; + --(*this); + return other; + } + + friend iterator operator+(difference_type n, iterator iter) + requires(std::is_same_v) + { + return iter + n; + } + +private: + friend const_iterator; + friend nonconst_iterator; + + accessor_type accessor_; +}; + +} // end detail + +} // end GRB_SPEC_NAMESPACE \ No newline at end of file diff --git a/src/interfaces/spec/grb/detail/matrix_traits.hpp b/src/interfaces/spec/grb/detail/matrix_traits.hpp new file mode 100644 index 0000000..547d62d --- /dev/null +++ b/src/interfaces/spec/grb/detail/matrix_traits.hpp @@ -0,0 +1,106 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "tag_invoke.hpp" +#include "namespace_macros.hpp" + +namespace GRB_SPEC_NAMESPACE { + +using any = std::any; + +namespace { + +template +struct get_index_type { +private: + using value_type = std::ranges::range_value_t; + using key_type = typename std::tuple_element<0, value_type>::type; +public: + using type = key_type; +}; + +template +requires requires { typename std::tuple_element<0, typename std::tuple_element<0, std::ranges::range_value_t>::type>::type; } +struct get_index_type +{ +private: + using value_type = std::ranges::range_value_t; + using key_type = typename std::tuple_element<0, value_type>::type; +public: + using type = typename std::tuple_element<0, key_type>::type; +}; +} + +template +struct container_traits { + using size_type = std::ranges::range_size_t; + using difference_type = std::ranges::range_difference_t; + using iterator = std::ranges::iterator_t; + using value_type = std::ranges::range_value_t; + using key_type = std::remove_cvref_t::type>; + using map_type = std::remove_cvref_t::type>; + using scalar_type = std::remove_cvref_t::type>; + using index_type = std::remove_cvref_t::type>; + using reference = std::ranges::range_reference_t; +}; + +template +struct matrix_traits { + using size_type = std::ranges::range_size_t; + using difference_type = std::ranges::range_difference_t; + using iterator = std::ranges::iterator_t; + using value_type = std::ranges::range_value_t; + using key_type = std::remove_cvref_t::type>; + using map_type = std::remove_cvref_t::type>; + using scalar_type = std::remove_cvref_t::type>; + using index_type = std::remove_cvref_t::type>; + using reference = std::ranges::range_reference_t; +}; + +template +struct vector_traits { + using size_type = std::ranges::range_size_t; + using difference_type = std::ranges::range_difference_t; + using iterator = std::ranges::iterator_t; + using value_type = std::ranges::range_value_t; + using key_type = std::remove_cvref_t::type>; + using map_type = std::remove_cvref_t::type>; + using scalar_type = std::remove_cvref_t::type>; + using index_type = key_type; + using reference = std::ranges::range_reference_t; +}; + +template +using container_scalar_t = typename container_traits::scalar_type; + +template +using container_index_t = typename container_traits::index_type; + +template +using container_value_t = typename container_traits::value_type; + +template +using container_key_t = typename container_traits::key_type; + +template +using matrix_scalar_t = typename container_traits>::scalar_type; + +template +using matrix_index_t = typename container_traits>::index_type; + +template +using vector_scalar_t = typename container_traits>::scalar_type; + +template +using vector_index_t = typename container_traits>::index_type; + +template +using elementwise_return_type_t = std::invoke_result_t, container_scalar_t>; + +} // end GRB_SPEC_NAMESPACE \ No newline at end of file diff --git a/src/interfaces/spec/grb/detail/namespace_macros.hpp b/src/interfaces/spec/grb/detail/namespace_macros.hpp new file mode 100644 index 0000000..71053a9 --- /dev/null +++ b/src/interfaces/spec/grb/detail/namespace_macros.hpp @@ -0,0 +1,5 @@ + +#pragma once + +#define GRB_SPEC_NAMESPACE spec +#define GBTL_NAMESPACE grb diff --git a/src/interfaces/spec/grb/detail/tag_invoke.hpp b/src/interfaces/spec/grb/detail/tag_invoke.hpp new file mode 100644 index 0000000..57fdadb --- /dev/null +++ b/src/interfaces/spec/grb/detail/tag_invoke.hpp @@ -0,0 +1,86 @@ +#pragma once + +#include +#include + +#include "namespace_macros.hpp" + +namespace GRB_SPEC_NAMESPACE { + +namespace _tag_invoke { + void tag_invoke(); + + struct _fn { + template + constexpr auto operator()(CPO cpo, Args&&... args) const + noexcept(noexcept(tag_invoke((CPO &&) cpo, (Args &&) args...))) + -> decltype(tag_invoke((CPO &&) cpo, (Args &&) args...)) { + return tag_invoke((CPO &&) cpo, (Args &&) args...); + } + }; + + template + using tag_invoke_result_t = decltype( + tag_invoke(std::declval(), std::declval()...)); + + using yes_type = char; + using no_type = char(&)[2]; + + template + auto try_tag_invoke(int) // + noexcept(noexcept(tag_invoke( + std::declval(), std::declval()...))) + -> decltype(static_cast(tag_invoke( + std::declval(), std::declval()...)), yes_type{}); + + template + no_type try_tag_invoke(...) noexcept(false); + + template