##// END OF EJS Templates
Removed bad dependency between VC and VariableModel, moved mime stuff...
Removed bad dependency between VC and VariableModel, moved mime stuff from VC to static Variable methods Signed-off-by: Alexis Jeandet <alexis.jeandet@member.fsf.org>

File last commit:

r4:96a6baa9f92b
r27:c08d1b8ad297
Show More
binop_overload.hpp
308 lines | 13.0 KiB | text/x-c++hdr | CppLexer
#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 <type_traits>
namespace opaque {
namespace binop {
/// \addtogroup internal
/// @{
///
/// Overloads of binary operators
///
template <typename OP, typename RT, bool apply_commutativity=false,
typename P1=RT, typename P2=RT, typename I1=P1, typename I2=P2>
struct overload;
template <typename OP, typename RT,
typename P1, typename P2, typename I1, typename I2>
struct overload<OP,RT,false,const P1& ,const P2& ,I1,I2> {
// No return type conversion - enable NRVO
template <typename R=RT> static constexpr14
typename std::enable_if< std::is_same<RT,I1>::value, R>::type
func(const P1& p1, const P2& p2, OP op=OP{}) noexcept(
std::is_nothrow_constructible<I1,const P1&>::value and
noexcept(op(std::declval<I1>(), convert<I2>(p2)))) {
I1 temp(p1);
op(temp, convert<I2>(p2));
return temp;
}
// Return type conversion
template <typename R=RT> static constexpr14
typename std::enable_if<not std::is_same<RT,I1>::value, R>::type
func(const P1& p1, const P2& p2, OP op=OP{}) noexcept(
std::is_nothrow_constructible<I1,const P1&>::value and
std::is_nothrow_constructible<RT,I1&&>::value and
noexcept(op(std::declval<I1>(), convert<I2>(p2)))) {
I1 temp(p1);
op(temp, convert<I2>(p2));
return static_cast<RT>(opaque::move(temp));
}
};
template <typename OP, typename RT,
typename P1, typename P2, typename I1, typename I2>
struct overload<OP,RT,false,const P1& , P2&&,I1,I2> {
// No return type conversion - enable NRVO
template <typename R=RT> static constexpr14
typename std::enable_if< std::is_same<RT,I1>::value, R>::type
func(const P1& p1, P2&& p2, OP op=OP{}) noexcept(
std::is_nothrow_constructible<I1,const P1&>::value and
noexcept(op(std::declval<I1>(), convert<I2>(opaque::move(p2))))) {
I1 temp(p1);
op(temp, convert<I2>(opaque::move(p2)));
return temp;
}
// Return type conversion
template <typename R=RT> static constexpr14
typename std::enable_if<not std::is_same<RT,I1>::value, R>::type
func(const P1& p1, P2&& p2, OP op=OP{}) noexcept(
std::is_nothrow_constructible<I1,const P1&>::value and
std::is_nothrow_constructible<RT,I1&&>::value and
noexcept(op(std::declval<I1>(), convert<I2>(opaque::move(p2))))) {
I1 temp(p1);
op(temp, convert<I2>(opaque::move(p2)));
return static_cast<RT>(opaque::move(temp));
}
};
template <typename OP, typename RT,
typename P1, typename P2, typename I1, typename I2>
struct overload<OP,RT,false, P1&&,const P2& ,I1,I2> {
static constexpr RT func( P1&& p1, const P2& p2, OP op=OP{}) noexcept(
noexcept(static_cast<RT>(
op(convert_mutable<I1>(opaque::move(p1)),
convert<I2>( p2 ))))) {
return static_cast<RT>(
op(convert_mutable<I1>(opaque::move(p1)),
convert<I2>( p2 ))); }
};
template <typename OP, typename RT,
typename P1, typename P2, typename I1, typename I2>
struct overload<OP,RT,false, P1&&, P2&&,I1,I2> {
static constexpr RT func( P1&& p1, P2&& p2, OP op=OP{}) noexcept(
noexcept(static_cast<RT>(
op(convert_mutable<I1>(opaque::move(p1)),
convert<I2>( opaque::move(p2)))))) {
return static_cast<RT>(
op(convert_mutable<I1>(opaque::move(p1)),
convert<I2>( opaque::move(p2)))); }
};
template <typename OP, typename RT,
typename P1, typename P2, typename I1, typename I2>
struct overload<OP,RT,true,P1,P2,I1,I2> {
using overload_t = overload<OP,RT,false,P2,P1,I2,I1>;
static constexpr RT func(P1&& p1, P2&& p2, OP op=OP{}) noexcept(
noexcept(overload_t::func(
opaque::forward<P2>(p2), opaque::forward<P1>(p1), op))) {
return overload_t::func(
opaque::forward<P2>(p2), opaque::forward<P1>(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 <typename OP, typename RT, bool apply_commutativity,
typename P1, typename P2, typename I1, typename I2>
struct overload_1 : overload<OP,RT,apply_commutativity,P1,P2,I1,I2> { };
template <typename OP, typename RT, bool apply_commutativity,
typename P1, typename P2, typename I1, typename I2>
struct overload_2 : overload<OP,RT,apply_commutativity,P1,P2,I1,I2> { };
template <typename OP, typename RT, bool apply_commutativity,
typename P1, typename P2, typename I1, typename I2>
struct overload_3 : overload<OP,RT,apply_commutativity,P1,P2,I1,I2> { };
template <typename OP, typename RT, bool apply_commutativity,
typename P1, typename P2, typename I1, typename I2>
struct overload_4 : overload<OP,RT,apply_commutativity,P1,P2,I1,I2> { };
///
/// 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<typename return_type, typename result_type,
typename inter1_type, typename inter2_type,
typename param1_type, typename param2_type>
static constexpr unsigned binop_conversion_cost() noexcept {
static_assert(is_decayed<return_type>::value, "");
static_assert(is_decayed<result_type>::value, "");
static_assert(is_decayed<inter1_type>::value, "");
static_assert(is_decayed<inter2_type>::value, "");
return converter<inter1_type,param1_type>::mutable_cost() +
converter<inter2_type,param2_type>::cost() +
converter<return_type,result_type>::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 <bool regular_well_formed, bool swapped_well_formed,
typename OPN, typename RT, bool commutative=false,
typename P1=RT, typename P2=RT, typename I1=P1, typename I2=P2,
typename OPS=OPN>
struct overload_selector {
// overload_selector<false,false,OPN,RT,commutative,P1,P2,I1,I2,OPS>
// --> No overload is well-formed
static_assert( swapped_well_formed, "Operation is not well-formed");
// overload_selector<false,true ,OPN,RT,false,P1,P2,I1,I2,OPS>>
// --> Only swapped overload is well-formed, but we can't use it
static_assert(not swapped_well_formed, "Operation is not commutative");
};
template <typename OPN, typename RT,
typename P1, typename P2, typename I1, typename I2, typename OPS>
struct overload_selector<false,true ,OPN,RT,true ,P1,P2,I1,I2,OPS> {
// Only swapped overload is well-formed, and we can use it
using type_1 = overload_1<OPS,RT,true ,const P1& , const P2& ,I1,I2>;
using type_2 = overload_2<OPS,RT,true ,const P1& , P2&&,I1,I2>;
using type_3 = overload_3<OPS,RT,true , P1&&, const P2& ,I1,I2>;
using type_4 = overload_4<OPS,RT,true , P1&&, P2&&,I1,I2>;
};
template <typename OPN, typename RT, bool commutative,
typename P1, typename P2, typename I1, typename I2, typename OPS>
struct overload_selector<true ,false,OPN,RT,commutative,P1,P2,I1,I2,OPS> {
// Only regular overload is well-formed
using type_1 = overload_1<OPN,RT,false,const P1& , const P2& ,I1,I2>;
using type_2 = overload_2<OPN,RT,false,const P1& , P2&&,I1,I2>;
using type_3 = overload_3<OPN,RT,false, P1&&, const P2& ,I1,I2>;
using type_4 = overload_4<OPN,RT,false, P1&&, P2&&,I1,I2>;
};
template <typename OPN, typename RT,
typename P1, typename P2, typename I1, typename I2, typename OPS>
struct overload_selector<true ,true ,OPN,RT,false,P1,P2,I1,I2,OPS> {
// Both overloads are well-formed, but we must use the regular one
using type_1 = overload_1<OPN,RT,false,const P1& , const P2& ,I1,I2>;
using type_2 = overload_2<OPN,RT,false,const P1& , P2&&,I1,I2>;
using type_3 = overload_3<OPN,RT,false, P1&&, const P2& ,I1,I2>;
using type_4 = overload_4<OPN,RT,false, P1&&, P2&&,I1,I2>;
};
template <typename OPN, typename RT,
typename P1, typename P2, typename I1, typename I2, typename OPS>
struct overload_selector<true ,true ,OPN,RT,true ,P1,P2,I1,I2,OPS> {
// Both overloads are well-formed, and we must choose between them
template <typename result, typename param1, typename param2>
static constexpr unsigned norm_cost() noexcept {
return binop_conversion_cost<RT,result,I1,I2,param1,param2>();
}
template <typename result, typename param1, typename param2>
static constexpr unsigned swap_cost() noexcept {
return binop_conversion_cost<RT,result,I2,I1,param2,param1>();
}
using RN = typename std::decay<typename
is_functor_call_well_formed<OPN,I1&,const I2&>::result_type>::type;
using RS = typename std::decay<typename
is_functor_call_well_formed<OPS,I2&,const I1&>::result_type>::type;
using type_1 = typename std::conditional<
swap_cost <RS,const P1& ,const P2& >() <
norm_cost <RN,const P1& ,const P2& >(),
overload_1<OPS,RT,true ,const P1& ,const P2& ,I1,I2>,
overload_1<OPN,RT,false,const P1& ,const P2& ,I1,I2>>::type;
using type_2 = typename std::conditional<
swap_cost <RS,const P1& , P2&&>() <
norm_cost <RN,const P1& , P2&&>(),
overload_2<OPS,RT,true ,const P1& , P2&&,I1,I2>,
overload_2<OPN,RT,false,const P1& , P2&&,I1,I2>>::type;
using type_3 = typename std::conditional<
swap_cost <RS, P1&&,const P2& >() <
norm_cost <RN, P1&&,const P2& >(),
overload_3<OPS,RT,true , P1&&,const P2& ,I1,I2>,
overload_3<OPN,RT,false, P1&&,const P2& ,I1,I2>>::type;
using type_4 = typename std::conditional<
swap_cost <RS, P1&&, P2&&>() <
norm_cost <RN, P1&&, P2&&>(),
overload_4<OPS,RT,true , P1&&, P2&&,I1,I2>,
overload_4<OPN,RT,false, P1&&, P2&&,I1,I2>>::type;
};
template <typename OPN, typename RT, bool commutative,
typename P1, typename P2, typename I1, typename I2, typename OPS>
using overload_selector_t = overload_selector<
is_functor_call_well_formed<OPN,I1&,I2>::value,
is_functor_call_well_formed<OPS,I2&,I1>::value,
OPN,RT,commutative,P1,P2,I1,I2,OPS>;
///
/// Generalized binary operator
///
template <typename OPN, typename RT, bool commutative=false,
typename P1=RT, typename P2=RT, typename I1=P1, typename I2=P2,
typename OPS=OPN>
struct binary_operator
: overload_selector_t<OPN,RT,commutative,P1,P2,I1,I2,OPS>::type_1
, overload_selector_t<OPN,RT,commutative,P1,P2,I1,I2,OPS>::type_2
, overload_selector_t<OPN,RT,commutative,P1,P2,I1,I2,OPS>::type_3
, overload_selector_t<OPN,RT,commutative,P1,P2,I1,I2,OPS>::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<OPN,RT,commutative,P1,P2,I1,I2,OPS>::type_1::func;
using overload_selector_t<OPN,RT,commutative,P1,P2,I1,I2,OPS>::type_2::func;
using overload_selector_t<OPN,RT,commutative,P1,P2,I1,I2,OPS>::type_3::func;
using overload_selector_t<OPN,RT,commutative,P1,P2,I1,I2,OPS>::type_4::func;
};
/// @}
}
}
#endif