added insert_and_visit and similar operations to concurrent containers (#283)

This commit is contained in:
joaquintides
2024-09-21 10:58:30 +02:00
committed by GitHub
parent 57546ed7e3
commit 834580b539
17 changed files with 2473 additions and 75 deletions
@@ -517,6 +517,80 @@ namespace boost {
this->insert_or_cvisit(ilist.begin(), ilist.end(), f);
}
template <class Ty, class F1, class F2>
BOOST_FORCEINLINE auto insert_and_visit(Ty&& value, F1 f1, F2 f2)
-> decltype(table_.insert_and_visit(std::forward<Ty>(value), f1, f2))
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F2)
return table_.insert_and_visit(std::forward<Ty>(value), f1, f2);
}
template <class F1, class F2>
BOOST_FORCEINLINE bool insert_and_visit(init_type&& obj, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F2)
return table_.insert_and_visit(std::move(obj), f1, f2);
}
template <class InputIterator, class F1, class F2>
void insert_and_visit(
InputIterator first, InputIterator last, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F2)
for (; first != last; ++first) {
table_.emplace_and_visit(*first, f1, f2);
}
}
template <class F1, class F2>
void insert_and_visit(
std::initializer_list<value_type> ilist, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F2)
this->insert_and_visit(ilist.begin(), ilist.end(), f1, f2);
}
template <class Ty, class F1, class F2>
BOOST_FORCEINLINE auto insert_and_cvisit(Ty&& value, F1 f1, F2 f2)
-> decltype(table_.insert_and_cvisit(std::forward<Ty>(value), f1, f2))
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return table_.insert_and_cvisit(std::forward<Ty>(value), f1, f2);
}
template <class F1, class F2>
BOOST_FORCEINLINE bool insert_and_cvisit(init_type&& obj, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return table_.insert_and_cvisit(std::move(obj), f1, f2);
}
template <class InputIterator, class F1, class F2>
void insert_and_cvisit(
InputIterator first, InputIterator last, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
for (; first != last; ++first) {
table_.emplace_and_cvisit(*first, f1, f2);
}
}
template <class F1, class F2>
void insert_and_cvisit(
std::initializer_list<value_type> ilist, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
this->insert_and_cvisit(ilist.begin(), ilist.end(), f1, f2);
}
template <class... Args> BOOST_FORCEINLINE bool emplace(Args&&... args)
{
return table_.emplace(std::forward<Args>(args)...);
@@ -538,6 +612,30 @@ namespace boost {
std::forward<Arg>(arg), std::forward<Args>(args)...);
}
template <class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool emplace_and_visit(
Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg2, Args...)
return table_.emplace_and_visit(
std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
template <class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool emplace_and_cvisit(
Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
return table_.emplace_and_cvisit(
std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
template <class... Args>
BOOST_FORCEINLINE bool try_emplace(key_type const& k, Args&&... args)
{
@@ -613,6 +711,78 @@ namespace boost {
std::forward<Arg>(arg), std::forward<Args>(args)...);
}
template <class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool try_emplace_and_visit(
key_type const& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg2, Args...)
return table_.try_emplace_and_visit(
k, std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
template <class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool try_emplace_and_cvisit(
key_type const& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
return table_.try_emplace_and_cvisit(
k, std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
template <class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool try_emplace_and_visit(
key_type&& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg2, Args...)
return table_.try_emplace_and_visit(
std::move(k), std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
template <class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool try_emplace_and_cvisit(
key_type&& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
return table_.try_emplace_and_cvisit(
std::move(k), std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
template <class K, class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool try_emplace_and_visit(
K&& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg2, Args...)
return table_.try_emplace_and_visit(std::forward<K>(k),
std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
template <class K, class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool try_emplace_and_cvisit(
K&& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
return table_.try_emplace_and_cvisit(std::forward<K>(k),
std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
BOOST_FORCEINLINE size_type erase(key_type const& k)
{
return table_.erase(k);
@@ -517,6 +517,101 @@ namespace boost {
this->insert_or_cvisit(ilist.begin(), ilist.end(), f);
}
template <class F1, class F2>
BOOST_FORCEINLINE bool insert_and_visit(
value_type const& obj, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return table_.insert_and_visit(obj, f1, f2);
}
template <class F1, class F2>
BOOST_FORCEINLINE bool insert_and_visit(value_type&& obj, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return table_.insert_and_visit(std::move(obj), f1, f2);
}
template <class K, class F1, class F2>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value,
bool >::type
insert_and_visit(K&& k, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return table_.try_emplace_and_visit(std::forward<K>(k), f1, f2);
}
template <class InputIterator, class F1, class F2>
void insert_and_visit(
InputIterator first, InputIterator last, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
for (; first != last; ++first) {
table_.emplace_and_visit(*first, f1, f2);
}
}
template <class F1, class F2>
void insert_and_visit(std::initializer_list<value_type> ilist, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
this->insert_and_visit(ilist.begin(), ilist.end(), f1, f2);
}
template <class F1, class F2>
BOOST_FORCEINLINE bool insert_and_cvisit(
value_type const& obj, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return table_.insert_and_cvisit(obj, f1, f2);
}
template <class F1, class F2>
BOOST_FORCEINLINE bool insert_and_cvisit(value_type&& obj, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return table_.insert_and_cvisit(std::move(obj), f1, f2);
}
template <class K, class F1, class F2>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value,
bool >::type
insert_and_cvisit(K&& k, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return table_.try_emplace_and_cvisit(std::forward<K>(k), f1, f2);
}
template <class InputIterator, class F1, class F2>
void insert_and_cvisit(
InputIterator first, InputIterator last, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
for (; first != last; ++first) {
table_.emplace_and_cvisit(*first, f1, f2);
}
}
template <class F1, class F2>
void insert_and_cvisit(
std::initializer_list<value_type> ilist, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
this->insert_and_cvisit(ilist.begin(), ilist.end(), f1, f2);
}
template <class... Args> BOOST_FORCEINLINE bool emplace(Args&&... args)
{
return table_.emplace(std::forward<Args>(args)...);
@@ -538,6 +633,30 @@ namespace boost {
std::forward<Arg>(arg), std::forward<Args>(args)...);
}
template <class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool emplace_and_visit(
Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_CONST_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
return table_.emplace_and_visit(
std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
template <class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool emplace_and_cvisit(
Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_CONST_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
return table_.emplace_and_cvisit(
std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
BOOST_FORCEINLINE size_type erase(key_type const& k)
{
return table_.erase(k);
@@ -585,6 +585,124 @@ namespace boost {
}
}
template <class Ty, class F1, class F2>
BOOST_FORCEINLINE auto insert_and_visit(Ty&& value, F1 f1, F2 f2)
-> decltype(table_.insert_and_visit(std::forward<Ty>(value), f1, f2))
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F2)
return table_.insert_and_visit(std::forward<Ty>(value), f1, f2);
}
template <class F1, class F2>
BOOST_FORCEINLINE bool insert_and_visit(init_type&& obj, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F2)
return table_.insert_and_visit(std::move(obj), f1, f2);
}
template <class InputIterator, class F1, class F2>
void insert_and_visit(
InputIterator first, InputIterator last, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F2)
for (; first != last; ++first) {
table_.emplace_and_visit(*first, f1, f2);
}
}
template <class F1, class F2>
void insert_and_visit(
std::initializer_list<value_type> ilist, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F2)
this->insert_and_visit(ilist.begin(), ilist.end(), f1, f2);
}
template <class F1, class F2>
insert_return_type insert_and_visit(node_type&& nh, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F2)
using access = detail::foa::node_handle_access;
if (nh.empty()) {
return {false, node_type{}};
}
// Caveat: get_allocator() incurs synchronization (not cheap)
BOOST_ASSERT(get_allocator() == nh.get_allocator());
if (table_.insert_and_visit(std::move(access::element(nh)), f1, f2)) {
access::reset(nh);
return {true, node_type{}};
} else {
return {false, std::move(nh)};
}
}
template <class Ty, class F1, class F2>
BOOST_FORCEINLINE auto insert_and_cvisit(Ty&& value, F1 f1, F2 f2)
-> decltype(table_.insert_and_cvisit(std::forward<Ty>(value), f1, f2))
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return table_.insert_and_cvisit(std::forward<Ty>(value), f1, f2);
}
template <class F1, class F2>
BOOST_FORCEINLINE bool insert_and_cvisit(init_type&& obj, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return table_.insert_and_cvisit(std::move(obj), f1, f2);
}
template <class InputIterator, class F1, class F2>
void insert_and_cvisit(
InputIterator first, InputIterator last, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
for (; first != last; ++first) {
table_.emplace_and_cvisit(*first, f1, f2);
}
}
template <class F1, class F2>
void insert_and_cvisit(
std::initializer_list<value_type> ilist, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
this->insert_and_cvisit(ilist.begin(), ilist.end(), f1, f2);
}
template <class F1, class F2>
insert_return_type insert_and_cvisit(node_type&& nh, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
using access = detail::foa::node_handle_access;
if (nh.empty()) {
return {false, node_type{}};
}
// Caveat: get_allocator() incurs synchronization (not cheap)
BOOST_ASSERT(get_allocator() == nh.get_allocator());
if (table_.insert_and_cvisit(std::move(access::element(nh)), f1, f2)) {
access::reset(nh);
return {true, node_type{}};
} else {
return {false, std::move(nh)};
}
}
template <class... Args> BOOST_FORCEINLINE bool emplace(Args&&... args)
{
return table_.emplace(std::forward<Args>(args)...);
@@ -606,6 +724,30 @@ namespace boost {
std::forward<Arg>(arg), std::forward<Args>(args)...);
}
template <class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool emplace_and_visit(
Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg2, Args...)
return table_.emplace_and_visit(
std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
template <class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool emplace_and_cvisit(
Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
return table_.emplace_and_cvisit(
std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
template <class... Args>
BOOST_FORCEINLINE bool try_emplace(key_type const& k, Args&&... args)
{
@@ -681,6 +823,78 @@ namespace boost {
std::forward<Arg>(arg), std::forward<Args>(args)...);
}
template <class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool try_emplace_and_visit(
key_type const& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg2, Args...)
return table_.try_emplace_and_visit(
k, std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
template <class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool try_emplace_and_cvisit(
key_type const& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
return table_.try_emplace_and_cvisit(
k, std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
template <class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool try_emplace_and_visit(
key_type&& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg2, Args...)
return table_.try_emplace_and_visit(
std::move(k), std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
template <class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool try_emplace_and_cvisit(
key_type&& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
return table_.try_emplace_and_cvisit(
std::move(k), std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
template <class K, class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool try_emplace_and_visit(
K&& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg2, Args...)
return table_.try_emplace_and_visit(std::forward<K>(k),
std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
template <class K, class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool try_emplace_and_cvisit(
K&& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
return table_.try_emplace_and_cvisit(std::forward<K>(k),
std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
BOOST_FORCEINLINE size_type erase(key_type const& k)
{
return table_.erase(k);
@@ -585,6 +585,145 @@ namespace boost {
}
}
template <class F1, class F2>
BOOST_FORCEINLINE bool insert_and_visit(
value_type const& obj, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return table_.insert_and_visit(obj, f1, f2);
}
template <class F1, class F2>
BOOST_FORCEINLINE bool insert_and_visit(value_type&& obj, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return table_.insert_and_visit(std::move(obj), f1, f2);
}
template <class K, class F1, class F2>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value,
bool >::type
insert_and_visit(K&& k, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return table_.try_emplace_and_visit(std::forward<K>(k), f1, f2);
}
template <class InputIterator, class F1, class F2>
void insert_and_visit(
InputIterator first, InputIterator last, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
for (; first != last; ++first) {
table_.emplace_and_visit(*first, f1, f2);
}
}
template <class F1, class F2>
void insert_and_visit(std::initializer_list<value_type> ilist, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
this->insert_and_visit(ilist.begin(), ilist.end(), f1, f2);
}
template <class F1, class F2>
insert_return_type insert_and_visit(node_type&& nh, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
using access = detail::foa::node_handle_access;
if (nh.empty()) {
return {false, node_type{}};
}
// Caveat: get_allocator() incurs synchronization (not cheap)
BOOST_ASSERT(get_allocator() == nh.get_allocator());
if (table_.insert_and_visit(std::move(access::element(nh)), f1, f2)) {
access::reset(nh);
return {true, node_type{}};
} else {
return {false, std::move(nh)};
}
}
template <class F1, class F2>
BOOST_FORCEINLINE bool insert_and_cvisit(
value_type const& obj, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return table_.insert_and_cvisit(obj, f1, f2);
}
template <class F1, class F2>
BOOST_FORCEINLINE bool insert_and_cvisit(value_type&& obj, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return table_.insert_and_cvisit(std::move(obj), f1, f2);
}
template <class K, class F1, class F2>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value,
bool >::type
insert_and_cvisit(K&& k, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return table_.try_emplace_and_cvisit(std::forward<K>(k), f1, f2);
}
template <class InputIterator, class F1, class F2>
void insert_and_cvisit(
InputIterator first, InputIterator last, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
for (; first != last; ++first) {
table_.emplace_and_cvisit(*first, f1, f2);
}
}
template <class F1, class F2>
void insert_and_cvisit(
std::initializer_list<value_type> ilist, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
this->insert_and_cvisit(ilist.begin(), ilist.end(), f1, f2);
}
template <class F1, class F2>
insert_return_type insert_and_cvisit(node_type&& nh, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
using access = detail::foa::node_handle_access;
if (nh.empty()) {
return {false, node_type{}};
}
// Caveat: get_allocator() incurs synchronization (not cheap)
BOOST_ASSERT(get_allocator() == nh.get_allocator());
if (table_.insert_and_cvisit(std::move(access::element(nh)), f1, f2)) {
access::reset(nh);
return {true, node_type{}};
} else {
return {false, std::move(nh)};
}
}
template <class... Args> BOOST_FORCEINLINE bool emplace(Args&&... args)
{
return table_.emplace(std::forward<Args>(args)...);
@@ -606,6 +745,30 @@ namespace boost {
std::forward<Arg>(arg), std::forward<Args>(args)...);
}
template <class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool emplace_and_visit(
Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_CONST_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
return table_.emplace_and_visit(
std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
template <class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool emplace_and_cvisit(
Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_CONST_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
return table_.emplace_and_cvisit(
std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
BOOST_FORCEINLINE size_type erase(key_type const& k)
{
return table_.erase(k);
@@ -1,5 +1,5 @@
/* Copyright 2023 Christian Mazakas.
* Copyright 2023 Joaquin M Lopez Munoz.
* Copyright 2023-2024 Joaquin M Lopez Munoz.
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
@@ -58,6 +58,24 @@
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE( \
BOOST_UNORDERED_DETAIL_LAST_ARG(Arg, Args))
#define BOOST_UNORDERED_DETAIL_PENULTIMATE_ARG(Arg1, Arg2, Args) \
mp11::mp_at_c<mp11::mp_list< \
Arg1 BOOST_UNORDERED_DETAIL_COMMA Arg2 BOOST_UNORDERED_DETAIL_COMMA Args \
>, \
mp11::mp_size<mp11::mp_list< \
Arg1 BOOST_UNORDERED_DETAIL_COMMA Arg2 BOOST_UNORDERED_DETAIL_COMMA Args \
>>::value - 2>
#define BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE( \
Arg1, Arg2, Args) \
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE( \
BOOST_UNORDERED_DETAIL_PENULTIMATE_ARG(Arg1, Arg2, Args))
#define BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_CONST_INVOCABLE( \
Arg1, Arg2, Args) \
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE( \
BOOST_UNORDERED_DETAIL_PENULTIMATE_ARG(Arg1, Arg2, Args))
namespace boost {
namespace unordered {
namespace detail {
@@ -215,7 +215,7 @@ struct atomic_integral
/* Group-level concurrency protection. It provides a rw mutex plus an
* atomic insertion counter for optimistic insertion (see
* unprotected_norehash_emplace_or_visit).
* unprotected_norehash_emplace_and_visit).
*/
struct group_access
@@ -755,6 +755,22 @@ public:
try_emplace_args_t{},std::forward<Key>(x),std::forward<Args>(args)...);
}
template<typename Key,typename... Args>
BOOST_FORCEINLINE bool try_emplace_and_visit(Key&& x,Args&&... args)
{
return emplace_and_visit_flast(
group_exclusive{},
try_emplace_args_t{},std::forward<Key>(x),std::forward<Args>(args)...);
}
template<typename Key,typename... Args>
BOOST_FORCEINLINE bool try_emplace_and_cvisit(Key&& x,Args&&... args)
{
return emplace_and_visit_flast(
group_shared{},
try_emplace_args_t{},std::forward<Key>(x),std::forward<Args>(args)...);
}
template<typename... Args>
BOOST_FORCEINLINE bool emplace_or_visit(Args&&... args)
{
@@ -769,86 +785,121 @@ public:
group_shared{},std::forward<Args>(args)...);
}
template<typename F>
BOOST_FORCEINLINE bool insert_or_visit(const init_type& x,F&& f)
template<typename... Args>
BOOST_FORCEINLINE bool emplace_and_visit(Args&&... args)
{
return emplace_or_visit_impl(group_exclusive{},std::forward<F>(f),x);
return construct_and_emplace_and_visit_flast(
group_exclusive{},std::forward<Args>(args)...);
}
template<typename F>
BOOST_FORCEINLINE bool insert_or_cvisit(const init_type& x,F&& f)
template<typename... Args>
BOOST_FORCEINLINE bool emplace_and_cvisit(Args&&... args)
{
return emplace_or_visit_impl(group_shared{},std::forward<F>(f),x);
return construct_and_emplace_and_visit_flast(
group_shared{},std::forward<Args>(args)...);
}
template<typename F>
BOOST_FORCEINLINE bool insert_or_visit(init_type&& x,F&& f)
template<typename Value,typename F>
BOOST_FORCEINLINE bool insert_or_visit(Value&& x,F&& f)
{
return emplace_or_visit_impl(
group_exclusive{},std::forward<F>(f),std::move(x));
return insert_and_visit(
std::forward<Value>(x),[](const value_type&){},std::forward<F>(f));
}
template<typename F>
BOOST_FORCEINLINE bool insert_or_cvisit(init_type&& x,F&& f)
template<typename Value,typename F>
BOOST_FORCEINLINE bool insert_or_cvisit(Value&& x,F&& f)
{
return emplace_or_visit_impl(
group_shared{},std::forward<F>(f),std::move(x));
return insert_and_cvisit(
std::forward<Value>(x),[](const value_type&){},std::forward<F>(f));
}
template<typename F1,typename F2>
BOOST_FORCEINLINE bool insert_and_visit(const init_type& x,F1&& f1,F2&& f2)
{
return emplace_and_visit_impl(
group_exclusive{},std::forward<F1>(f1),std::forward<F2>(f2),x);
}
template<typename F1,typename F2>
BOOST_FORCEINLINE bool insert_and_cvisit(const init_type& x,F1&& f1,F2&& f2)
{
return emplace_and_visit_impl(
group_shared{},std::forward<F1>(f1),std::forward<F2>(f2),x);
}
template<typename F1,typename F2>
BOOST_FORCEINLINE bool insert_and_visit(init_type&& x,F1&& f1,F2&& f2)
{
return emplace_and_visit_impl(
group_exclusive{},std::forward<F1>(f1),std::forward<F2>(f2),
std::move(x));
}
template<typename F1,typename F2>
BOOST_FORCEINLINE bool insert_and_cvisit(init_type&& x,F1&& f1,F2&& f2)
{
return emplace_and_visit_impl(
group_shared{},std::forward<F1>(f1),std::forward<F2>(f2),std::move(x));
}
/* SFINAE tilts call ambiguities in favor of init_type */
template<typename Value,typename F>
BOOST_FORCEINLINE auto insert_or_visit(const Value& x,F&& f)
template<typename Value,typename F1,typename F2>
BOOST_FORCEINLINE auto insert_and_visit(const Value& x,F1&& f1,F2&& f2)
->enable_if_is_value_type<Value,bool>
{
return emplace_or_visit_impl(group_exclusive{},std::forward<F>(f),x);
return emplace_and_visit_impl(
group_exclusive{},std::forward<F1>(f1),std::forward<F2>(f2),x);
}
template<typename Value,typename F>
BOOST_FORCEINLINE auto insert_or_cvisit(const Value& x,F&& f)
template<typename Value,typename F1,typename F2>
BOOST_FORCEINLINE auto insert_and_cvisit(const Value& x,F1&& f1,F2&& f2)
->enable_if_is_value_type<Value,bool>
{
return emplace_or_visit_impl(group_shared{},std::forward<F>(f),x);
return emplace_and_visit_impl(
group_shared{},std::forward<F1>(f1),std::forward<F2>(f2),x);
}
template<typename Value,typename F>
BOOST_FORCEINLINE auto insert_or_visit(Value&& x,F&& f)
template<typename Value,typename F1,typename F2>
BOOST_FORCEINLINE auto insert_and_visit(Value&& x,F1&& f1,F2&& f2)
->enable_if_is_value_type<Value,bool>
{
return emplace_or_visit_impl(
group_exclusive{},std::forward<F>(f),std::move(x));
return emplace_and_visit_impl(
group_exclusive{},std::forward<F1>(f1),std::forward<F2>(f2),
std::move(x));
}
template<typename Value,typename F>
BOOST_FORCEINLINE auto insert_or_cvisit(Value&& x,F&& f)
template<typename Value,typename F1,typename F2>
BOOST_FORCEINLINE auto insert_and_cvisit(Value&& x,F1&& f1,F2&& f2)
->enable_if_is_value_type<Value,bool>
{
return emplace_or_visit_impl(
group_shared{},std::forward<F>(f),std::move(x));
return emplace_and_visit_impl(
group_shared{},std::forward<F1>(f1),std::forward<F2>(f2),std::move(x));
}
template<typename F,typename T=element_type>
template<typename F1,typename F2,typename T=element_type>
BOOST_FORCEINLINE
typename std::enable_if<
!std::is_same<T,value_type>::value,
bool
>::type
insert_or_visit(element_type&& x, F&& f)
insert_and_visit(element_type&& x,F1&& f1,F2&& f2)
{
return emplace_or_visit_impl(
group_exclusive{},std::forward<F>(f),std::move(x));
return emplace_and_visit_impl(
group_exclusive{},std::forward<F1>(f1),std::forward<F2>(f2),
std::move(x));
}
template<typename F,typename T=element_type>
template<typename F1,typename F2,typename T=element_type>
BOOST_FORCEINLINE
typename std::enable_if<
!std::is_same<T,value_type>::value,
bool
>::type
insert_or_cvisit(element_type&& x, F&& f)
insert_and_cvisit(element_type&& x,F1&& f1,F2&& f2)
{
return emplace_or_visit_impl(
group_shared{},std::forward<F>(f),std::move(x));
return emplace_and_visit_impl(
group_shared{},std::forward<F1>(f1),std::forward<F2>(f2),std::move(x));
}
template<typename Key>
@@ -1408,23 +1459,59 @@ private:
);
}
struct call_construct_and_emplace_and_visit
{
template<typename... Args>
BOOST_FORCEINLINE bool operator()(
concurrent_table* this_,Args&&... args)const
{
return this_->construct_and_emplace_and_visit(
std::forward<Args>(args)...);
}
};
template<typename GroupAccessMode,typename... Args>
BOOST_FORCEINLINE bool construct_and_emplace_and_visit_flast(
GroupAccessMode access_mode,Args&&... args)
{
return mp11::tuple_apply(
call_construct_and_emplace_and_visit{},
std::tuple_cat(
std::make_tuple(this,access_mode),
tuple_rotate_right<2>(
std::forward_as_tuple(std::forward<Args>(args)...))
)
);
}
template<typename GroupAccessMode,typename F,typename... Args>
BOOST_FORCEINLINE bool construct_and_emplace_or_visit(
GroupAccessMode access_mode,F&& f,Args&&... args)
{
return construct_and_emplace_and_visit(
access_mode,[](const value_type&){},std::forward<F>(f),
std::forward<Args>(args)...);
}
template<typename GroupAccessMode,typename F1,typename F2,typename... Args>
BOOST_FORCEINLINE bool construct_and_emplace_and_visit(
GroupAccessMode access_mode,F1&& f1,F2&& f2,Args&&... args)
{
auto lck=shared_access();
alloc_cted_insert_type<type_policy,Allocator,Args...> x(
this->al(),std::forward<Args>(args)...);
int res=unprotected_norehash_emplace_or_visit(
access_mode,std::forward<F>(f),type_policy::move(x.value()));
int res=unprotected_norehash_emplace_and_visit(
access_mode,std::forward<F1>(f1),std::forward<F2>(f2),
type_policy::move(x.value()));
if(BOOST_LIKELY(res>=0))return res!=0;
lck.unlock();
rehash_if_full();
return noinline_emplace_or_visit(
access_mode,std::forward<F>(f),type_policy::move(x.value()));
return noinline_emplace_and_visit(
access_mode,std::forward<F1>(f1),std::forward<F2>(f2),
type_policy::move(x.value()));
}
template<typename... Args>
@@ -1442,6 +1529,15 @@ private:
access_mode,std::forward<F>(f),std::forward<Args>(args)...);
}
template<typename GroupAccessMode,typename F1,typename F2,typename... Args>
BOOST_NOINLINE bool noinline_emplace_and_visit(
GroupAccessMode access_mode,F1&& f1,F2&& f2,Args&&... args)
{
return emplace_and_visit_impl(
access_mode,std::forward<F1>(f1),std::forward<F2>(f2),
std::forward<Args>(args)...);
}
struct call_emplace_or_visit_impl
{
template<typename... Args>
@@ -1465,15 +1561,49 @@ private:
);
}
struct call_emplace_and_visit_impl
{
template<typename... Args>
BOOST_FORCEINLINE bool operator()(
concurrent_table* this_,Args&&... args)const
{
return this_->emplace_and_visit_impl(std::forward<Args>(args)...);
}
};
template<typename GroupAccessMode,typename... Args>
BOOST_FORCEINLINE bool emplace_and_visit_flast(
GroupAccessMode access_mode,Args&&... args)
{
return mp11::tuple_apply(
call_emplace_and_visit_impl{},
std::tuple_cat(
std::make_tuple(this,access_mode),
tuple_rotate_right<2>(
std::forward_as_tuple(std::forward<Args>(args)...))
)
);
}
template<typename GroupAccessMode,typename F,typename... Args>
BOOST_FORCEINLINE bool emplace_or_visit_impl(
GroupAccessMode access_mode,F&& f,Args&&... args)
{
return emplace_and_visit_impl(
access_mode,[](const value_type&){},std::forward<F>(f),
std::forward<Args>(args)...);
}
template<typename GroupAccessMode,typename F1,typename F2,typename... Args>
BOOST_FORCEINLINE bool emplace_and_visit_impl(
GroupAccessMode access_mode,F1&& f1,F2&& f2,Args&&... args)
{
for(;;){
{
auto lck=shared_access();
int res=unprotected_norehash_emplace_or_visit(
access_mode,std::forward<F>(f),std::forward<Args>(args)...);
int res=unprotected_norehash_emplace_and_visit(
access_mode,std::forward<F1>(f1),std::forward<F2>(f2),
std::forward<Args>(args)...);
if(BOOST_LIKELY(res>=0))return res!=0;
}
rehash_if_full();
@@ -1498,6 +1628,16 @@ private:
return true;
}
template<typename GroupAccessMode,typename F,typename... Args>
BOOST_FORCEINLINE int
unprotected_norehash_emplace_or_visit(
GroupAccessMode access_mode,F&& f,Args&&... args)
{
return unprotected_norehash_emplace_and_visit(
access_mode,[&](const value_type&){},
std::forward<F>(f),std::forward<Args>(args)...);
}
struct reserve_size
{
reserve_size(concurrent_table& x_):x(x_)
@@ -1539,10 +1679,10 @@ private:
bool commit_=false;
};
template<typename GroupAccessMode,typename F,typename... Args>
template<typename GroupAccessMode,typename F1,typename F2,typename... Args>
BOOST_FORCEINLINE int
unprotected_norehash_emplace_or_visit(
GroupAccessMode access_mode,F&& f,Args&&... args)
unprotected_norehash_emplace_and_visit(
GroupAccessMode access_mode,F1&& f1,F2&& f2,Args&&... args)
{
const auto &k=this->key_from(std::forward<Args>(args)...);
auto hash=this->hash_for(k);
@@ -1552,7 +1692,7 @@ private:
startover:
boost::uint32_t counter=insert_counter(pos0);
if(unprotected_visit(
access_mode,k,pos0,hash,std::forward<F>(f)))return 0;
access_mode,k,pos0,hash,std::forward<F2>(f2)))return 0;
reserve_size rsize(*this);
if(BOOST_LIKELY(rsize.succeeded())){
@@ -1572,6 +1712,7 @@ private:
this->construct_element(p,std::forward<Args>(args)...);
rslot.commit();
rsize.commit();
f1(cast_for(group_exclusive{},type_policy::value_from(*p)));
BOOST_UNORDERED_ADD_STATS(this->cstats.insertion,(pb.length()));
return 1;
}
@@ -1,4 +1,4 @@
/* Copyright 2023 Joaquin M Lopez Munoz.
/* Copyright 2023-2024 Joaquin M Lopez Munoz.
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
@@ -19,27 +19,28 @@ namespace unordered{
namespace detail{
namespace foa{
template<typename Tuple>
template<std::size_t Offset,typename Tuple>
using tuple_rotate_right_return_type=mp11::mp_rotate_right_c<
typename std::remove_cv<typename std::remove_reference<Tuple>::type>::type,
1
Offset
>;
template<std::size_t... Is,typename Tuple>
tuple_rotate_right_return_type<Tuple>
template<std::size_t Offset,std::size_t... Is,typename Tuple>
tuple_rotate_right_return_type<Offset,Tuple>
tuple_rotate_right_aux(mp11::index_sequence<Is...>,Tuple&& x)
{
return tuple_rotate_right_return_type<Tuple>{
std::get<(Is+sizeof...(Is)-1)%sizeof...(Is)>(std::forward<Tuple>(x))...};
return tuple_rotate_right_return_type<Offset,Tuple>{
std::get<(Is+sizeof...(Is)-Offset)%sizeof...(Is)>(
std::forward<Tuple>(x))...};
}
template<typename Tuple>
tuple_rotate_right_return_type<Tuple> tuple_rotate_right(Tuple&& x)
template<std::size_t Offset=1,typename Tuple>
tuple_rotate_right_return_type<Offset,Tuple> tuple_rotate_right(Tuple&& x)
{
using RawTuple=typename std::remove_cv<
typename std::remove_reference<Tuple>::type>::type;
return tuple_rotate_right_aux(
return tuple_rotate_right_aux<Offset>(
mp11::make_index_sequence<std::tuple_size<RawTuple>::value>{},
std::forward<Tuple>(x));
}