Add static_assert on mismatches of base::Bind args and the target params
This CL adds a static_assert to detect the Bind earlier and to emit an
readable error message.
Bug: 746955
Change-Id: Id8081be63f789c7896a9a4229fe3978a7fdedc41
Reviewed-on: https://chromium-review.googlesource.com/583976
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Commit-Queue: Taiju Tsuiki <tzik@chromium.org>
Cr-Commit-Position: refs/heads/master@{#490741}
diff --git a/base/bind.h b/base/bind.h
index ce71797..a8dcdfc 100644
--- a/base/bind.h
+++ b/base/bind.h
@@ -24,10 +24,129 @@
namespace base {
+namespace internal {
+
+// IsOnceCallback<T> is a std::true_type if |T| is a OnceCallback.
+template <typename T>
+struct IsOnceCallback : std::false_type {};
+
+template <typename Signature>
+struct IsOnceCallback<OnceCallback<Signature>> : std::true_type {};
+
+// Asserts |Param| is constructible from |Unwrapped|. |Arg| is here just to
+// show it in the compile error message as a hint to fix the error.
+template <size_t i, typename Arg, typename Unwrapped, typename Param>
+struct AssertConstructible {
+ static_assert(std::is_constructible<Param, Unwrapped>::value,
+ "|Param| needs to be constructible from |Unwrapped| type. "
+ "The failing argument is passed as the |i|th parameter, whose "
+ "type is |Arg|, and delivered as |Unwrapped| into |Param|.");
+};
+
+// Takes three same-length TypeLists, and applies AssertConstructible for each
+// triples.
+template <typename Index,
+ typename ArgsList,
+ typename UnwrappedTypeList,
+ typename ParamsList>
+struct AssertBindArgsValidity;
+
+template <size_t... Ns,
+ typename... Args,
+ typename... Unwrapped,
+ typename... Params>
+struct AssertBindArgsValidity<IndexSequence<Ns...>,
+ TypeList<Args...>,
+ TypeList<Unwrapped...>,
+ TypeList<Params...>>
+ : AssertConstructible<Ns, Args, Unwrapped, Params>... {
+ static constexpr bool ok = true;
+};
+
+// The implementation of TransformToUnwrappedType below.
+template <RepeatMode, typename T>
+struct TransformToUnwrappedTypeImpl;
+
+template <typename T>
+struct TransformToUnwrappedTypeImpl<RepeatMode::Once, T> {
+ using StoredType = typename std::decay<T>::type;
+ using ForwardType = StoredType&&;
+ using Unwrapped = decltype(Unwrap(std::declval<ForwardType>()));
+};
+
+template <typename T>
+struct TransformToUnwrappedTypeImpl<RepeatMode::Repeating, T> {
+ using StoredType = typename std::decay<T>::type;
+ using ForwardType = const StoredType&;
+ using Unwrapped = decltype(Unwrap(std::declval<ForwardType>()));
+};
+
+// Transform |T| into `Unwrapped` type, which is passed to the target function.
+// Example:
+// In repeat_mode == RepeatMode::Once case,
+// `int&&` -> `int&&`,
+// `const int&` -> `int&&`,
+// `OwnedWrapper<int>&` -> `int*&&`.
+// In repeat_mode == RepeatMode::Repeating case,
+// `int&&` -> `const int&`,
+// `const int&` -> `const int&`,
+// `OwnedWrapper<int>&` -> `int* const &`.
+template <RepeatMode repeat_mode, typename T>
+using TransformToUnwrappedType =
+ typename TransformToUnwrappedTypeImpl<repeat_mode, T>::Unwrapped;
+
+// Transforms |Args| into `Unwrapped` types, and packs them into a TypeList.
+// If |is_method| is true, tries to dereference the first argument to support
+// smart pointers.
+template <RepeatMode repeat_mode, bool is_method, typename... Args>
+struct MakeUnwrappedTypeListImpl {
+ using Type = TypeList<TransformToUnwrappedType<repeat_mode, Args>...>;
+};
+
+// Performs special handling for this pointers.
+// Example:
+// int* -> int*,
+// std::unique_ptr<int> -> int*.
+template <RepeatMode repeat_mode, typename Receiver, typename... Args>
+struct MakeUnwrappedTypeListImpl<repeat_mode, true, Receiver, Args...> {
+ using UnwrappedReceiver = TransformToUnwrappedType<repeat_mode, Receiver>;
+ using Type = TypeList<decltype(&*std::declval<UnwrappedReceiver>()),
+ TransformToUnwrappedType<repeat_mode, Args>...>;
+};
+
+template <RepeatMode repeat_mode, bool is_method, typename... Args>
+using MakeUnwrappedTypeList =
+ typename MakeUnwrappedTypeListImpl<repeat_mode, is_method, Args...>::Type;
+
+} // namespace internal
+
// Bind as OnceCallback.
template <typename Functor, typename... Args>
inline OnceCallback<MakeUnboundRunType<Functor, Args...>>
BindOnce(Functor&& functor, Args&&... args) {
+ static_assert(
+ !internal::IsOnceCallback<typename std::decay<Functor>::type>() ||
+ (std::is_rvalue_reference<Functor&&>() &&
+ !std::is_const<typename std::remove_reference<Functor>::type>()),
+ "BindOnce requires non-const rvalue for OnceCallback binding."
+ " I.e.: base::BindOnce(std::move(callback)).");
+
+ // This block checks if each |args| matches to the corresponding params of the
+ // target function. This check does not affect the behavior of Bind, but its
+ // error message should be more readable.
+ using Helper = internal::BindTypeHelper<Functor, Args...>;
+ using FunctorTraits = typename Helper::FunctorTraits;
+ using BoundArgsList = typename Helper::BoundArgsList;
+ using UnwrappedArgsList =
+ internal::MakeUnwrappedTypeList<internal::RepeatMode::Once,
+ FunctorTraits::is_method, Args&&...>;
+ using BoundParamsList = typename Helper::BoundParamsList;
+ static_assert(
+ internal::AssertBindArgsValidity<MakeIndexSequence<Helper::num_bounds>,
+ BoundArgsList, UnwrappedArgsList,
+ BoundParamsList>::ok,
+ "The bound args need to be convertible to the target params.");
+
using BindState = internal::MakeBindStateType<Functor, Args...>;
using UnboundRunType = MakeUnboundRunType<Functor, Args...>;
using Invoker = internal::Invoker<BindState, UnboundRunType>;
@@ -50,6 +169,26 @@
template <typename Functor, typename... Args>
inline RepeatingCallback<MakeUnboundRunType<Functor, Args...>>
BindRepeating(Functor&& functor, Args&&... args) {
+ static_assert(
+ !internal::IsOnceCallback<typename std::decay<Functor>::type>(),
+ "BindRepeating cannot bind OnceCallback. Use BindOnce with std::move().");
+
+ // This block checks if each |args| matches to the corresponding params of the
+ // target function. This check does not affect the behavior of Bind, but its
+ // error message should be more readable.
+ using Helper = internal::BindTypeHelper<Functor, Args...>;
+ using FunctorTraits = typename Helper::FunctorTraits;
+ using BoundArgsList = typename Helper::BoundArgsList;
+ using UnwrappedArgsList =
+ internal::MakeUnwrappedTypeList<internal::RepeatMode::Repeating,
+ FunctorTraits::is_method, Args&&...>;
+ using BoundParamsList = typename Helper::BoundParamsList;
+ static_assert(
+ internal::AssertBindArgsValidity<MakeIndexSequence<Helper::num_bounds>,
+ BoundArgsList, UnwrappedArgsList,
+ BoundParamsList>::ok,
+ "The bound args need to be convertible to the target params.");
+
using BindState = internal::MakeBindStateType<Functor, Args...>;
using UnboundRunType = MakeUnboundRunType<Functor, Args...>;
using Invoker = internal::Invoker<BindState, UnboundRunType>;