From d1ef992fcda7e916a82fd342226aa39c508c885f Mon Sep 17 00:00:00 2001 From: Yedidya Feldblum Date: Tue, 19 Dec 2017 18:57:03 -0800 Subject: [PATCH] propagate_const Summary: [Folly] `propagate_const`, backported from C++ Library Fundamentals TS v2. Reviewed By: ericniebler Differential Revision: D6589681 fbshipit-source-id: cdc8981d17938b99afe60e2baefff7deb5316612 --- folly/lang/PropagateConst.h | 430 +++++++++++++++++++++++++ folly/lang/test/PropagateConstTest.cpp | 329 +++++++++++++++++++ 2 files changed, 759 insertions(+) create mode 100644 folly/lang/PropagateConst.h create mode 100644 folly/lang/test/PropagateConstTest.cpp diff --git a/folly/lang/PropagateConst.h b/folly/lang/PropagateConst.h new file mode 100644 index 00000000..5b359dc7 --- /dev/null +++ b/folly/lang/PropagateConst.h @@ -0,0 +1,430 @@ +/* + * Copyright 2017-present Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +#include +#include + +namespace folly { + +template +class propagate_const; + +template +constexpr Pointer& get_underlying(propagate_const& obj) { + return obj.pointer_; +} + +template +constexpr Pointer const& get_underlying(propagate_const const& obj) { + return obj.pointer_; +} + +namespace detail { +template +struct is_propagate_const : std::false_type {}; +template +struct is_propagate_const> : std::true_type {}; +template +using is_decay_propagate_const = is_propagate_const<_t>>; + +namespace propagate_const_adl { +using std::swap; +template +auto adl_swap(T& a, T& b) noexcept(noexcept(swap(a, b))) + -> decltype(swap(a, b)) { + swap(a, b); +} +} // namespace propagate_const_adl +} // namespace detail + +// mimic: std::experimental::propagate_const, C++ Library Fundamentals TS v2 +template +class propagate_const { + public: + using element_type = + _t())>>; + + constexpr propagate_const() = default; + constexpr propagate_const(propagate_const&&) = default; + constexpr propagate_const(propagate_const const&) = delete; + + template < + typename OtherPointer, + _t::value && + !std::is_convertible::value, + int>> = 0> + constexpr explicit propagate_const(propagate_const&& other) + : pointer_(static_cast(other.pointer_)) {} + + template < + typename OtherPointer, + _t::value && + std::is_convertible::value, + int>> = 0> + constexpr propagate_const(propagate_const&& other) + : pointer_(static_cast(other.pointer_)) {} + + template < + typename OtherPointer, + _t::value && + std::is_constructible::value && + !std::is_convertible::value, + int>> = 0> + constexpr explicit propagate_const(OtherPointer&& other) + : pointer_(static_cast(other)) {} + + template < + typename OtherPointer, + _t::value && + std::is_constructible::value && + std::is_convertible::value, + int>> = 0> + constexpr propagate_const(OtherPointer&& other) + : pointer_(static_cast(other)) {} + + constexpr propagate_const& operator=(propagate_const&&) = default; + constexpr propagate_const& operator=(propagate_const const&) = delete; + + template < + typename OtherPointer, + typename = _t< + std::enable_if::value>>> + FOLLY_CPP14_CONSTEXPR propagate_const& operator=( + propagate_const&& other) { + pointer_ = static_cast(other.pointer_); + } + + template < + typename OtherPointer, + typename = _t::value && + std::is_convertible::value>>> + FOLLY_CPP14_CONSTEXPR propagate_const& operator=(OtherPointer&& other) { + pointer_ = static_cast(other); + } + + FOLLY_CPP14_CONSTEXPR void swap(propagate_const& other) noexcept(noexcept( + detail::propagate_const_adl::adl_swap(pointer_, other.pointer_))) { + detail::propagate_const_adl::adl_swap(pointer_, other.pointer_); + } + + constexpr element_type* get() { + return get_(pointer_); + } + + constexpr element_type const* get() const { + return get_(pointer_); + } + + constexpr explicit operator bool() const { + return static_cast(pointer_); + } + + constexpr element_type& operator*() { + return *get(); + } + + constexpr element_type const& operator*() const { + return *get(); + } + + constexpr element_type* operator->() { + return get(); + } + + constexpr element_type const* operator->() const { + return get(); + } + + template < + typename OtherPointer = Pointer, + typename = _t::value || + std::is_convertible::value>>> + constexpr operator element_type*() { + return get(); + } + + template < + typename OtherPointer = Pointer, + typename = _t::value || + std::is_convertible::value>>> + constexpr operator element_type const*() const { + return get(); + } + + private: + friend Pointer& get_underlying<>(propagate_const&); + friend Pointer const& get_underlying<>(propagate_const const&); + + template + static T* get_(T* t) { + return t; + } + template + static auto get_(T& t) -> decltype(t.get()) { + return t.get(); + } + + Pointer pointer_; +}; + +template +FOLLY_CPP14_CONSTEXPR void swap( + propagate_const& a, + propagate_const& b) noexcept(noexcept(a.swap(b))) { + a.swap(b); +} + +template +constexpr bool operator==(propagate_const const& a, std::nullptr_t) { + return get_underlying(a) == nullptr; +} + +template +constexpr bool operator==(std::nullptr_t, propagate_const const& a) { + return nullptr == get_underlying(a); +} + +template +constexpr bool operator!=(propagate_const const& a, std::nullptr_t) { + return get_underlying(a) != nullptr; +} + +template +constexpr bool operator!=(std::nullptr_t, propagate_const const& a) { + return nullptr != get_underlying(a); +} + +template +constexpr bool operator==( + propagate_const const& a, + propagate_const const& b) { + return get_underlying(a) == get_underlying(b); +} + +template +constexpr bool operator!=( + propagate_const const& a, + propagate_const const& b) { + return get_underlying(a) != get_underlying(b); +} + +template +constexpr bool operator<( + propagate_const const& a, + propagate_const const& b) { + return get_underlying(a) < get_underlying(b); +} + +template +constexpr bool operator<=( + propagate_const const& a, + propagate_const const& b) { + return get_underlying(a) <= get_underlying(b); +} + +template +constexpr bool operator>( + propagate_const const& a, + propagate_const const& b) { + return get_underlying(a) > get_underlying(b); +} + +template +constexpr bool operator>=( + propagate_const const& a, + propagate_const const& b) { + return get_underlying(a) >= get_underlying(b); +} + +// Note: contrary to the specification, the heterogeneous comparison operators +// only participate in overload resolution when the equivalent heterogeneous +// comparison operators on the underlying pointers, as returned by invocation +// of get_underlying, would also participate in overload resolution. + +template +constexpr auto operator==(propagate_const const& a, Other const& b) + -> decltype(get_underlying(a) == b, false) { + return get_underlying(a) == b; +} + +template +constexpr auto operator!=(propagate_const const& a, Other const& b) + -> decltype(get_underlying(a) != b, false) { + return get_underlying(a) != b; +} + +template +constexpr auto operator<(propagate_const const& a, Other const& b) + -> decltype(get_underlying(a) < b, false) { + return get_underlying(a) < b; +} + +template +constexpr auto operator<=(propagate_const const& a, Other const& b) + -> decltype(get_underlying(a) <= b, false) { + return get_underlying(a) <= b; +} + +template +constexpr auto operator>(propagate_const const& a, Other const& b) + -> decltype(get_underlying(a) > b, false) { + return get_underlying(a) > b; +} + +template +constexpr auto operator>=(propagate_const const& a, Other const& b) + -> decltype(get_underlying(a) >= b, false) { + return get_underlying(a) >= b; +} + +template +constexpr auto operator==(Other const& a, propagate_const const& b) + -> decltype(a == get_underlying(b), false) { + return a == get_underlying(b); +} + +template +constexpr auto operator!=(Other const& a, propagate_const const& b) + -> decltype(a != get_underlying(b), false) { + return a != get_underlying(b); +} + +template +constexpr auto operator<(Other const& a, propagate_const const& b) + -> decltype(a < get_underlying(b), false) { + return a < get_underlying(b); +} + +template +constexpr auto operator<=(Other const& a, propagate_const const& b) + -> decltype(a <= get_underlying(b), false) { + return a <= get_underlying(b); +} + +template +constexpr auto operator>(Other const& a, propagate_const const& b) + -> decltype(a > get_underlying(b), false) { + return a > get_underlying(b); +} + +template +constexpr auto operator>=(Other const& a, propagate_const const& b) + -> decltype(a >= get_underlying(b), false) { + return a >= get_underlying(b); +} + +} // namespace folly + +namespace std { + +template +struct hash> : private hash { + using hash::hash; + + size_t operator()(folly::propagate_const const& obj) const { + return hash::operator()(folly::get_underlying(obj)); + } +}; + +template +struct equal_to> : private equal_to { + using equal_to::equal_to; + + constexpr bool operator()( + folly::propagate_const const& a, + folly::propagate_const const& b) { + return equal_to::operator()( + folly::get_underlying(a), folly::get_underlying(b)); + } +}; + +template +struct not_equal_to> + : private not_equal_to { + using not_equal_to::not_equal_to; + + constexpr bool operator()( + folly::propagate_const const& a, + folly::propagate_const const& b) { + return not_equal_to::operator()( + folly::get_underlying(a), folly::get_underlying(b)); + } +}; + +template +struct less> : private less { + using less::less; + + constexpr bool operator()( + folly::propagate_const const& a, + folly::propagate_const const& b) { + return less::operator()( + folly::get_underlying(a), folly::get_underlying(b)); + } +}; + +template +struct greater> : private greater { + using greater::greater; + + constexpr bool operator()( + folly::propagate_const const& a, + folly::propagate_const const& b) { + return greater::operator()( + folly::get_underlying(a), folly::get_underlying(b)); + } +}; + +template +struct less_equal> + : private less_equal { + using less_equal::less_equal; + + constexpr bool operator()( + folly::propagate_const const& a, + folly::propagate_const const& b) { + return less_equal::operator()( + folly::get_underlying(a), folly::get_underlying(b)); + } +}; + +template +struct greater_equal> + : private greater_equal { + using greater_equal::greater_equal; + + constexpr bool operator()( + folly::propagate_const const& a, + folly::propagate_const const& b) { + return greater_equal::operator()( + folly::get_underlying(a), folly::get_underlying(b)); + } +}; + +} // namespace std diff --git a/folly/lang/test/PropagateConstTest.cpp b/folly/lang/test/PropagateConstTest.cpp new file mode 100644 index 00000000..f68c28f1 --- /dev/null +++ b/folly/lang/test/PropagateConstTest.cpp @@ -0,0 +1,329 @@ +/* + * Copyright 2017-present Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include + +using namespace folly; + +class PropagateConstTest : public testing::Test {}; + +// force complete template instantiations +template class folly::propagate_const; +template class folly::propagate_const>; +template class folly::propagate_const>; + +template +static bool is_const(T&&) { + return std::is_const<_t>>::value; +} + +template +using pc = propagate_const; + +TEST_F(PropagateConstTest, construct_assign) { + struct Source { + int& operator*(); + int* get(); + }; + struct Implicit { + int& operator*(); + int* get(); + /* implicit */ Implicit(Source) {} + }; + struct Explicit { + int& operator*(); + int* get(); + explicit Explicit(Source) {} + }; + + EXPECT_TRUE((std::is_constructible, Source>::value)); + EXPECT_TRUE((std::is_constructible, Source>::value)); + EXPECT_TRUE((std::is_convertible>::value)); + EXPECT_FALSE((std::is_convertible>::value)); + EXPECT_TRUE((std::is_assignable, Source>::value)); + EXPECT_FALSE((std::is_assignable, Source>::value)); + + EXPECT_TRUE((std::is_constructible, pc>::value)); + EXPECT_TRUE((std::is_constructible, pc>::value)); + EXPECT_TRUE((std::is_convertible, pc>::value)); + EXPECT_FALSE((std::is_convertible, pc>::value)); + EXPECT_TRUE((std::is_assignable, pc>::value)); + EXPECT_FALSE((std::is_assignable, pc>::value)); +} + +TEST_F(PropagateConstTest, get) { + int a = 3; + auto pc_a = pc(&a); + + EXPECT_EQ(&a, pc_a.get()); + EXPECT_EQ(&a, as_const(pc_a).get()); + EXPECT_FALSE(is_const(*pc_a.get())); + EXPECT_TRUE(is_const(*as_const(pc_a).get())); +} + +TEST_F(PropagateConstTest, op_indirect) { + int a = 3; + auto pc_a = pc(&a); + + EXPECT_EQ(&a, &*pc_a); + EXPECT_EQ(&a, &*as_const(pc_a)); + EXPECT_FALSE(is_const(*pc_a)); + EXPECT_TRUE(is_const(*as_const(pc_a))); +} + +TEST_F(PropagateConstTest, op_element_type_ptr) { + int a = 3; + auto pc_a = pc(&a); + + EXPECT_EQ(&a, static_cast(pc_a)); + EXPECT_EQ(&a, static_cast(as_const(pc_a))); +} + +TEST_F(PropagateConstTest, op_bool) { + int a = 3; + auto pc_a = pc(&a); + auto pc_0 = pc(nullptr); + + EXPECT_TRUE(pc_a); + EXPECT_FALSE(pc_0); +} + +TEST_F(PropagateConstTest, get_underlying) { + int a = 3; + auto pc_a = pc(&a); + + EXPECT_EQ(&a, get_underlying(pc_a)); + EXPECT_EQ(&a, get_underlying(as_const(pc_a))); + EXPECT_FALSE(is_const(get_underlying(pc_a))); + EXPECT_TRUE(is_const(get_underlying(as_const(pc_a)))); + EXPECT_TRUE(&get_underlying(pc_a) == &get_underlying(as_const(pc_a))); +} + +TEST_F(PropagateConstTest, swap) { + int a = 3; + int b = 4; + auto pc_a = pc(&a); + auto pc_b = pc(&b); + + swap(pc_a, pc_b); + EXPECT_EQ(3, *pc_b); + EXPECT_EQ(4, *pc_a); + + swap(pc_a, pc_b); + EXPECT_EQ(3, *pc_a); + EXPECT_EQ(4, *pc_b); +} + +TEST_F(PropagateConstTest, op_equal_to) { + int a = 3; + int b = 4; + auto pc_a = pc(&a); + auto pc_b = pc(&b); + + auto _ = [](auto&& x, auto&& y) { return x == y; }; + EXPECT_TRUE(_(pc_a, pc_a)); + EXPECT_FALSE(_(pc_a, pc_b)); + EXPECT_FALSE(_(pc_a, nullptr)); + EXPECT_TRUE(_(pc_a, &a)); + EXPECT_FALSE(_(pc_a, &b)); + EXPECT_TRUE(_(&a, pc_a)); + EXPECT_FALSE(_(&b, pc_a)); +} + +TEST_F(PropagateConstTest, op_not_equal_to) { + int a = 3; + int b = 4; + auto pc_a = pc(&a); + auto pc_b = pc(&b); + + auto _ = [](auto&& x, auto&& y) { return x != y; }; + EXPECT_FALSE(_(pc_a, pc_a)); + EXPECT_TRUE(_(pc_a, pc_b)); + EXPECT_TRUE(_(pc_a, nullptr)); + EXPECT_FALSE(_(pc_a, &a)); + EXPECT_TRUE(_(pc_a, &b)); + EXPECT_FALSE(_(&a, pc_a)); + EXPECT_TRUE(_(&b, pc_a)); +} + +TEST_F(PropagateConstTest, op_less) { + int a = 3; + int b = 4; + auto pc_a = pc(&a); + auto pc_b = pc(&b); + + auto _ = [](auto&& x, auto&& y) { return x < y; }; + EXPECT_FALSE(_(pc_a, pc_a)); + EXPECT_FALSE(_(pc_a, &a)); + EXPECT_FALSE(_(&a, pc_a)); + EXPECT_TRUE(_(pc_a, pc_b)); + EXPECT_TRUE(_(pc_a, &b)); + EXPECT_TRUE(_(&a, pc_b)); + EXPECT_FALSE(_(pc_b, pc_a)); + EXPECT_FALSE(_(pc_b, &a)); + EXPECT_FALSE(_(&b, pc_a)); + EXPECT_FALSE(_(pc_b, pc_b)); + EXPECT_FALSE(_(pc_b, &b)); + EXPECT_FALSE(_(&b, pc_b)); +} + +TEST_F(PropagateConstTest, op_greater) { + int a = 3; + int b = 4; + auto pc_a = pc(&a); + auto pc_b = pc(&b); + + auto _ = [](auto&& x, auto&& y) { return x > y; }; + EXPECT_FALSE(_(pc_a, pc_a)); + EXPECT_FALSE(_(pc_a, &a)); + EXPECT_FALSE(_(&a, pc_a)); + EXPECT_FALSE(_(pc_a, pc_b)); + EXPECT_FALSE(_(pc_a, &b)); + EXPECT_FALSE(_(&a, pc_b)); + EXPECT_TRUE(_(pc_b, pc_a)); + EXPECT_TRUE(_(pc_b, &a)); + EXPECT_TRUE(_(&b, pc_a)); + EXPECT_FALSE(_(pc_b, pc_b)); + EXPECT_FALSE(_(pc_b, &b)); + EXPECT_FALSE(_(&b, pc_b)); +} + +TEST_F(PropagateConstTest, op_less_equal) { + int a = 3; + int b = 4; + auto pc_a = pc(&a); + auto pc_b = pc(&b); + + auto _ = [](auto&& x, auto&& y) { return x <= y; }; + EXPECT_TRUE(_(pc_a, pc_a)); + EXPECT_TRUE(_(pc_a, &a)); + EXPECT_TRUE(_(&a, pc_a)); + EXPECT_TRUE(_(pc_a, pc_b)); + EXPECT_TRUE(_(pc_a, &b)); + EXPECT_TRUE(_(&a, pc_b)); + EXPECT_FALSE(_(pc_b, pc_a)); + EXPECT_FALSE(_(pc_b, &a)); + EXPECT_FALSE(_(&b, pc_a)); + EXPECT_TRUE(_(pc_b, pc_b)); + EXPECT_TRUE(_(pc_b, &b)); + EXPECT_TRUE(_(&b, pc_b)); +} + +TEST_F(PropagateConstTest, op_greater_equal) { + int a = 3; + int b = 4; + auto pc_a = pc(&a); + auto pc_b = pc(&b); + + auto _ = [](auto&& x, auto&& y) { return x >= y; }; + EXPECT_TRUE(_(pc_a, pc_a)); + EXPECT_TRUE(_(pc_a, &a)); + EXPECT_TRUE(_(&a, pc_a)); + EXPECT_FALSE(_(pc_a, pc_b)); + EXPECT_FALSE(_(pc_a, &b)); + EXPECT_FALSE(_(&a, pc_b)); + EXPECT_TRUE(_(pc_b, pc_a)); + EXPECT_TRUE(_(pc_b, &a)); + EXPECT_TRUE(_(&b, pc_a)); + EXPECT_TRUE(_(pc_b, pc_b)); + EXPECT_TRUE(_(pc_b, &b)); + EXPECT_TRUE(_(&b, pc_b)); +} + +TEST_F(PropagateConstTest, hash) { + int a = 3; + auto pc_a = pc(&a); + + EXPECT_EQ(std::hash()(&a), std::hash>()(pc_a)); +} + +TEST_F(PropagateConstTest, equal_to) { + int a = 3; + int b = 4; + auto pc_a = pc(&a); + auto pc_b = pc(&b); + + auto _ = std::equal_to>{}; + EXPECT_TRUE(_(pc_a, pc_a)); + EXPECT_FALSE(_(pc_a, pc_b)); +} + +TEST_F(PropagateConstTest, not_equal_to) { + int a = 3; + int b = 4; + auto pc_a = pc(&a); + auto pc_b = pc(&b); + + auto _ = std::not_equal_to>{}; + EXPECT_FALSE(_(pc_a, pc_a)); + EXPECT_TRUE(_(pc_a, pc_b)); +} + +TEST_F(PropagateConstTest, less) { + int a = 3; + int b = 4; + auto pc_a = pc(&a); + auto pc_b = pc(&b); + + auto _ = std::less>{}; + EXPECT_FALSE(_(pc_a, pc_a)); + EXPECT_TRUE(_(pc_a, pc_b)); + EXPECT_FALSE(_(pc_b, pc_a)); + EXPECT_FALSE(_(pc_b, pc_b)); +} + +TEST_F(PropagateConstTest, greater) { + int a = 3; + int b = 4; + auto pc_a = pc(&a); + auto pc_b = pc(&b); + + auto _ = std::greater>{}; + EXPECT_FALSE(_(pc_a, pc_a)); + EXPECT_FALSE(_(pc_a, pc_b)); + EXPECT_TRUE(_(pc_b, pc_a)); + EXPECT_FALSE(_(pc_b, pc_b)); +} + +TEST_F(PropagateConstTest, less_equal) { + int a = 3; + int b = 4; + auto pc_a = pc(&a); + auto pc_b = pc(&b); + + auto _ = std::less_equal>{}; + EXPECT_TRUE(_(pc_a, pc_a)); + EXPECT_TRUE(_(pc_a, pc_b)); + EXPECT_FALSE(_(pc_b, pc_a)); + EXPECT_TRUE(_(pc_b, pc_b)); +} + +TEST_F(PropagateConstTest, greater_equal) { + int a = 3; + int b = 4; + auto pc_a = pc(&a); + auto pc_b = pc(&b); + + auto _ = std::greater_equal>{}; + EXPECT_TRUE(_(pc_a, pc_a)); + EXPECT_FALSE(_(pc_a, pc_b)); + EXPECT_TRUE(_(pc_b, pc_a)); + EXPECT_TRUE(_(pc_b, pc_b)); +} -- 2.34.1