#ifndef OPAQUE_BINOP_OVERLOAD_HPP #define OPAQUE_BINOP_OVERLOAD_HPP // // Copyright (c) 2015, 2016 // Kyle Markley. All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // 3. Neither the name of the author nor the names of any contributors may be // used to endorse or promote products derived from this software without // specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // #include "../constexpr14.hpp" #include "../type_traits.hpp" #include "../utility.hpp" #include "../convert.hpp" #include namespace opaque { namespace binop { /// \addtogroup internal /// @{ /// /// Overloads of binary operators /// template struct overload; template struct overload { // No return type conversion - enable NRVO template static constexpr14 typename std::enable_if< std::is_same::value, R>::type func(const P1& p1, const P2& p2, OP op=OP{}) noexcept( std::is_nothrow_constructible::value and noexcept(op(std::declval(), convert(p2)))) { I1 temp(p1); op(temp, convert(p2)); return temp; } // Return type conversion template static constexpr14 typename std::enable_if::value, R>::type func(const P1& p1, const P2& p2, OP op=OP{}) noexcept( std::is_nothrow_constructible::value and std::is_nothrow_constructible::value and noexcept(op(std::declval(), convert(p2)))) { I1 temp(p1); op(temp, convert(p2)); return static_cast(opaque::move(temp)); } }; template struct overload { // No return type conversion - enable NRVO template static constexpr14 typename std::enable_if< std::is_same::value, R>::type func(const P1& p1, P2&& p2, OP op=OP{}) noexcept( std::is_nothrow_constructible::value and noexcept(op(std::declval(), convert(opaque::move(p2))))) { I1 temp(p1); op(temp, convert(opaque::move(p2))); return temp; } // Return type conversion template static constexpr14 typename std::enable_if::value, R>::type func(const P1& p1, P2&& p2, OP op=OP{}) noexcept( std::is_nothrow_constructible::value and std::is_nothrow_constructible::value and noexcept(op(std::declval(), convert(opaque::move(p2))))) { I1 temp(p1); op(temp, convert(opaque::move(p2))); return static_cast(opaque::move(temp)); } }; template struct overload { static constexpr RT func( P1&& p1, const P2& p2, OP op=OP{}) noexcept( noexcept(static_cast( op(convert_mutable(opaque::move(p1)), convert( p2 ))))) { return static_cast( op(convert_mutable(opaque::move(p1)), convert( p2 ))); } }; template struct overload { static constexpr RT func( P1&& p1, P2&& p2, OP op=OP{}) noexcept( noexcept(static_cast( op(convert_mutable(opaque::move(p1)), convert( opaque::move(p2)))))) { return static_cast( op(convert_mutable(opaque::move(p1)), convert( opaque::move(p2)))); } }; template struct overload { using overload_t = overload; static constexpr RT func(P1&& p1, P2&& p2, OP op=OP{}) noexcept( noexcept(overload_t::func( opaque::forward(p2), opaque::forward(p1), op))) { return overload_t::func( opaque::forward(p2), opaque::forward(p1), op); } }; // // Four flavors of overload are required, but we cannot inherit from the same // base class more than once (ISO/IEC 14882 §10.1/3). Work around this by // creating four distinct classes to inherit from. // template struct overload_1 : overload { }; template struct overload_2 : overload { }; template struct overload_3 : overload { }; template struct overload_4 : overload { }; /// /// Conversion costs associated with a binary operation /// /// The parameter types must reflect the actual types passed to the binary /// operation, e.g. const T& or T&&. All the other types must be decayed /// types. /// template static constexpr unsigned binop_conversion_cost() noexcept { static_assert(is_decayed::value, ""); static_assert(is_decayed::value, ""); static_assert(is_decayed::value, ""); static_assert(is_decayed::value, ""); return converter::mutable_cost() + converter::cost() + converter::cost(); } /// /// Select between the regular overload and the commutative alternative /// /// If the operator is not commutative, use the regular overload. /// Otherwise, if only one option is well-formed, use that. /// Otherwise, if both are well-formed, select the lower-cost one. /// template struct overload_selector { // overload_selector // --> No overload is well-formed static_assert( swapped_well_formed, "Operation is not well-formed"); // overload_selector> // --> Only swapped overload is well-formed, but we can't use it static_assert(not swapped_well_formed, "Operation is not commutative"); }; template struct overload_selector { // Only swapped overload is well-formed, and we can use it using type_1 = overload_1; using type_2 = overload_2; using type_3 = overload_3; using type_4 = overload_4; }; template struct overload_selector { // Only regular overload is well-formed using type_1 = overload_1; using type_2 = overload_2; using type_3 = overload_3; using type_4 = overload_4; }; template struct overload_selector { // Both overloads are well-formed, but we must use the regular one using type_1 = overload_1; using type_2 = overload_2; using type_3 = overload_3; using type_4 = overload_4; }; template struct overload_selector { // Both overloads are well-formed, and we must choose between them template static constexpr unsigned norm_cost() noexcept { return binop_conversion_cost(); } template static constexpr unsigned swap_cost() noexcept { return binop_conversion_cost(); } using RN = typename std::decay::result_type>::type; using RS = typename std::decay::result_type>::type; using type_1 = typename std::conditional< swap_cost () < norm_cost (), overload_1, overload_1>::type; using type_2 = typename std::conditional< swap_cost () < norm_cost (), overload_2, overload_2>::type; using type_3 = typename std::conditional< swap_cost () < norm_cost (), overload_3, overload_3>::type; using type_4 = typename std::conditional< swap_cost () < norm_cost (), overload_4, overload_4>::type; }; template using overload_selector_t = overload_selector< is_functor_call_well_formed::value, is_functor_call_well_formed::value, OPN,RT,commutative,P1,P2,I1,I2,OPS>; /// /// Generalized binary operator /// template struct binary_operator : overload_selector_t::type_1 , overload_selector_t::type_2 , overload_selector_t::type_3 , overload_selector_t::type_4 { // // Inheriting a function with the same name from different base classes is // ambiguous (ISO/IEC 14882 §10.2/6). Work around this by pulling the // declarations into the derived class. // using overload_selector_t::type_1::func; using overload_selector_t::type_2::func; using overload_selector_t::type_3::func; using overload_selector_t::type_4::func; }; /// @} } } #endif