Bug 1482046 - mfbt: FunctionTypeTraits - r=froydnj
authorGerald Squelart <gsquelart@mozilla.com>
Wed, 15 Aug 2018 10:27:45 +0000
changeset 431650 85f7b7200eea172cc5cc1d4eb853cb5062a35baa
parent 431649 92832d7ecc29acb44a81ceb7836baaf528c20282
child 431651 5a743f540b83abbbe4fb145d8ba1e713a4125153
push id34450
push userebalazs@mozilla.com
push dateThu, 16 Aug 2018 09:22:59 +0000
treeherdermozilla-central@9c569226e852 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1482046
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1482046 - mfbt: FunctionTypeTraits - r=froydnj FunctionTypeTraits< function type > makes it easier to inspect a function's return type, arity, and parameter types. It works with free functions, struct/class methods, function objects like non-generic lambdas and std::function. Differential Revision: https://phabricator.services.mozilla.com/D2998
mfbt/FunctionTypeTraits.h
mfbt/moz.build
mfbt/tests/TestFunctionTypeTraits.cpp
mfbt/tests/moz.build
new file mode 100644
--- /dev/null
+++ b/mfbt/FunctionTypeTraits.h
@@ -0,0 +1,136 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* Helpers to manipulate function types that don't fit in TypeTraits.h */
+
+#ifndef mozilla_FunctionTypeTraits_h
+#define mozilla_FunctionTypeTraits_h
+
+#include <tuple>
+
+namespace mozilla {
+
+// Main FunctionTypeTraits declaration, taking one template argument.
+//
+// Given a function type, FunctionTypeTraits will expose the following members:
+// - ReturnType: Return type.
+// - arity: Number of parameters (size_t).
+// - ParameterType<N>: Type of the Nth** parameter, 0-indexed.
+//
+// ** `ParameterType<N>` with `N` >= `arity` is allowed and gives `void`.
+// This prevents compilation errors when trying to access a type outside of the
+// function's parameters, which is useful for parameters checks, e.g.:
+//   template<typename F>
+//   auto foo(F&&)
+//    -> enable_if(FunctionTypeTraits<F>::arity == 1 &&
+//                 is_same<FunctionTypeTraits<F>::template ParameterType<0>,
+//                         int>::value,
+//                 void)
+//   {
+//     // This function will only be enabled if `F` takes one `int`.
+//     // Without the permissive ParameterType<any N>, it wouldn't even compile.
+//
+// Note: FunctionTypeTraits does not work with generic lambdas `[](auto&) {}`,
+// because parameter types cannot be known until an actual invocation when types
+// are inferred from the given arguments.
+template<typename T>
+struct FunctionTypeTraits;
+
+// Remove reference and pointer wrappers, if any.
+template<typename T>
+struct FunctionTypeTraits<T&> : public FunctionTypeTraits<T>
+{
+};
+template<typename T>
+struct FunctionTypeTraits<T&&> : public FunctionTypeTraits<T>
+{
+};
+template<typename T>
+struct FunctionTypeTraits<T*> : public FunctionTypeTraits<T>
+{
+};
+
+// Extract `operator()` function from callables (e.g. lambdas, std::function).
+template<typename T>
+struct FunctionTypeTraits : public FunctionTypeTraits<decltype(&T::operator())>
+{
+};
+
+namespace detail {
+
+// If `safe`, retrieve the `N`th type from `As`, otherwise `void`.
+// See top description for reason.
+template<bool safe, size_t N, typename... As>
+struct TupleElementSafe;
+template<size_t N, typename... As>
+struct TupleElementSafe<true, N, As...>
+{
+  using Type = typename std::tuple_element<N, std::tuple<As...>>::type;
+};
+template<size_t N, typename... As>
+struct TupleElementSafe<false, N, As...>
+{
+  using Type = void;
+};
+
+template<typename R, typename... As>
+struct FunctionTypeTraitsHelper
+{
+  using ReturnType = R;
+  static constexpr size_t arity = sizeof...(As);
+  template<size_t N>
+  using ParameterType =
+    typename TupleElementSafe<(N < sizeof...(As)), N, As...>::Type;
+};
+
+} // namespace detail
+
+// Specialization for free functions.
+template<typename R, typename... As>
+struct FunctionTypeTraits<R(As...)> : detail::FunctionTypeTraitsHelper<R, As...>
+{
+};
+
+// Specialization for non-const member functions.
+template<typename C, typename R, typename... As>
+struct FunctionTypeTraits<R (C::*)(As...)>
+  : detail::FunctionTypeTraitsHelper<R, As...>
+{
+};
+
+// Specialization for const member functions.
+template<typename C, typename R, typename... As>
+struct FunctionTypeTraits<R (C::*)(As...) const>
+  : detail::FunctionTypeTraitsHelper<R, As...>
+{
+};
+
+#ifdef NS_HAVE_STDCALL
+// Specialization for __stdcall free functions.
+template<typename R, typename... As>
+struct FunctionTypeTraits<R NS_STDCALL(As...)>
+  : detail::FunctionTypeTraitsHelper<R, As...>
+{
+};
+
+// Specialization for __stdcall non-const member functions.
+template<typename C, typename R, typename... As>
+struct FunctionTypeTraits<R (NS_STDCALL C::*)(As...)>
+  : detail::FunctionTypeTraitsHelper<R, As...>
+{
+};
+
+// Specialization for __stdcall const member functions.
+template<typename C, typename R, typename... As>
+struct FunctionTypeTraits<R (NS_STDCALL C::*)(As...) const>
+  : detail::FunctionTypeTraitsHelper<R, As...>
+{
+};
+#endif // NS_HAVE_STDCALL
+
+} // namespace mozilla
+
+#endif // mozilla_FunctionTypeTraits_h
--- a/mfbt/moz.build
+++ b/mfbt/moz.build
@@ -37,16 +37,17 @@ EXPORTS.mozilla = [
     'EndianUtils.h',
     'EnumeratedArray.h',
     'EnumeratedRange.h',
     'EnumSet.h',
     'EnumTypeTraits.h',
     'FastBernoulliTrial.h',
     'FloatingPoint.h',
     'FStream.h',
+    'FunctionTypeTraits.h',
     'GuardObjects.h',
     'HashFunctions.h',
     'HashTable.h',
     'IntegerPrintfMacros.h',
     'IntegerRange.h',
     'IntegerTypeTraits.h',
     'JSONWriter.h',
     'Likely.h',
new file mode 100644
--- /dev/null
+++ b/mfbt/tests/TestFunctionTypeTraits.cpp
@@ -0,0 +1,261 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/FunctionTypeTraits.h"
+
+#include <functional>
+
+using mozilla::FunctionTypeTraits;
+
+void
+f0()
+{
+}
+
+int
+f1(char)
+{
+  return 0;
+}
+
+#ifdef NS_HAVE_STDCALL
+void NS_STDCALL
+f0s()
+{
+}
+
+int NS_STDCALL
+f1s(char)
+{
+  return 0;
+}
+#endif // NS_HAVE_STDCALL
+
+struct S
+{
+  void f0() {}
+  void f0c() const {}
+  int f1(char) { return 0; }
+  int f1c(char) const { return 0; }
+#ifdef NS_HAVE_STDCALL
+  void NS_STDCALL f0s() {}
+  void NS_STDCALL f0cs() const {}
+  int NS_STDCALL f1s(char) { return 0; }
+  int NS_STDCALL f1cs(char) const { return 0; }
+#endif // NS_HAVE_STDCALL
+};
+
+static_assert(
+  std::is_same<typename FunctionTypeTraits<decltype(f0)>::ReturnType,
+               void>::value,
+  "f0 returns void");
+static_assert(FunctionTypeTraits<decltype(f0)>::arity == 0,
+              "f0 takes no parameters");
+static_assert(
+  std::is_same<
+    typename FunctionTypeTraits<decltype(f0)>::template ParameterType<0>,
+    void>::value,
+  "f0 has no first parameter");
+
+static_assert(
+  std::is_same<typename FunctionTypeTraits<decltype(&S::f0)>::ReturnType,
+               void>::value,
+  "S::f0 returns void");
+static_assert(FunctionTypeTraits<decltype(&S::f0)>::arity == 0,
+              "S::f0 takes no parameters");
+static_assert(
+  std::is_same<
+    typename FunctionTypeTraits<decltype(&S::f0)>::template ParameterType<0>,
+    void>::value,
+  "S::f0 has no first parameter");
+
+static_assert(
+  std::is_same<typename FunctionTypeTraits<decltype(&S::f0c)>::ReturnType,
+               void>::value,
+  "S::f0c returns void");
+static_assert(FunctionTypeTraits<decltype(&S::f0c)>::arity == 0,
+              "S::f0c takes no parameters");
+static_assert(
+  std::is_same<
+    typename FunctionTypeTraits<decltype(&S::f0c)>::template ParameterType<0>,
+    void>::value,
+  "S::f0c has no first parameter");
+
+static_assert(
+  std::is_same<typename FunctionTypeTraits<decltype(f1)>::ReturnType,
+               int>::value,
+  "f1 returns int");
+static_assert(FunctionTypeTraits<decltype(f1)>::arity == 1,
+              "f1 takes one parameter");
+static_assert(
+  std::is_same<
+    typename FunctionTypeTraits<decltype(f1)>::template ParameterType<0>,
+    char>::value,
+  "f1 takes a char");
+
+static_assert(
+  std::is_same<typename FunctionTypeTraits<decltype(&S::f1)>::ReturnType,
+               int>::value,
+  "S::f1 returns int");
+static_assert(FunctionTypeTraits<decltype(&S::f1)>::arity == 1,
+              "S::f1 takes one parameter");
+static_assert(
+  std::is_same<
+    typename FunctionTypeTraits<decltype(&S::f1)>::template ParameterType<0>,
+    char>::value,
+  "S::f1 takes a char");
+
+static_assert(
+  std::is_same<typename FunctionTypeTraits<decltype(&S::f1c)>::ReturnType,
+               int>::value,
+  "S::f1c returns int");
+static_assert(FunctionTypeTraits<decltype(&S::f1c)>::arity == 1,
+              "S::f1c takes one parameter");
+static_assert(
+  std::is_same<
+    typename FunctionTypeTraits<decltype(&S::f1c)>::template ParameterType<0>,
+    char>::value,
+  "S::f1c takes a char");
+
+#ifdef NS_HAVE_STDCALL
+static_assert(
+  std::is_same<typename FunctionTypeTraits<decltype(f0s)>::ReturnType,
+               void>::value,
+  "f0s returns void");
+static_assert(FunctionTypeTraits<decltype(f0s)>::arity == 0,
+              "f0s takes no parameters");
+static_assert(
+  std::is_same<
+    typename FunctionTypeTraits<decltype(f0s)>::template ParameterType<0>,
+    void>::value,
+  "f0s has no first parameter");
+
+static_assert(
+  std::is_same<typename FunctionTypeTraits<decltype(&S::f0s)>::ReturnType,
+               void>::value,
+  "S::f0s returns void");
+static_assert(FunctionTypeTraits<decltype(&S::f0s)>::arity == 0,
+              "S::f0s takes no parameters");
+static_assert(
+  std::is_same<
+    typename FunctionTypeTraits<decltype(&S::f0s)>::template ParameterType<0>,
+    void>::value,
+  "S::f0s has no first parameter");
+
+static_assert(
+  std::is_same<typename FunctionTypeTraits<decltype(&S::f0cs)>::ReturnType,
+               void>::value,
+  "S::f0cs returns void");
+static_assert(FunctionTypeTraits<decltype(&S::f0cs)>::arity == 0,
+              "S::f0cs takes no parameters");
+static_assert(
+  std::is_same<
+    typename FunctionTypeTraits<decltype(&S::f0cs)>::template ParameterType<0>,
+    void>::value,
+  "S::f0cs has no first parameter");
+
+static_assert(
+  std::is_same<typename FunctionTypeTraits<decltype(f1s)>::ReturnType,
+               int>::value,
+  "f1s returns int");
+static_assert(FunctionTypeTraits<decltype(f1s)>::arity == 1,
+              "f1s takes one parameter");
+static_assert(
+  std::is_same<
+    typename FunctionTypeTraits<decltype(f1s)>::template ParameterType<0>,
+    char>::value,
+  "f1s takes a char");
+
+static_assert(
+  std::is_same<typename FunctionTypeTraits<decltype(&S::f1s)>::ReturnType,
+               int>::value,
+  "S::f1s returns int");
+static_assert(FunctionTypeTraits<decltype(&S::f1s)>::arity == 1,
+              "S::f1s takes one parameter");
+static_assert(
+  std::is_same<
+    typename FunctionTypeTraits<decltype(&S::f1s)>::template ParameterType<0>,
+    char>::value,
+  "S::f1s takes a char");
+
+static_assert(
+  std::is_same<typename FunctionTypeTraits<decltype(&S::f1cs)>::ReturnType,
+               int>::value,
+  "S::f1cs returns int");
+static_assert(FunctionTypeTraits<decltype(&S::f1cs)>::arity == 1,
+              "S::f1cs takes one parameter");
+static_assert(
+  std::is_same<
+    typename FunctionTypeTraits<decltype(&S::f1cs)>::template ParameterType<0>,
+    char>::value,
+  "S::f1cs takes a char");
+#endif // NS_HAVE_STDCALL
+
+template<typename F>
+void
+TestVoidVoid(F&&)
+{
+  static_assert(
+    std::is_same<typename FunctionTypeTraits<F>::ReturnType, void>::value,
+    "Should return void");
+  static_assert(FunctionTypeTraits<F>::arity == 0, "Should take no parameters");
+  static_assert(
+    std::is_same<typename FunctionTypeTraits<F>::template ParameterType<0>,
+                 void>::value,
+    "Should have no first parameter");
+}
+
+template<typename F>
+void
+TestIntChar(F&&)
+{
+  static_assert(
+    std::is_same<typename FunctionTypeTraits<F>::ReturnType, int>::value,
+    "Should return int");
+  static_assert(FunctionTypeTraits<F>::arity == 1, "Should take one parameter");
+  static_assert(
+    std::is_same<typename FunctionTypeTraits<F>::template ParameterType<0>,
+                 char>::value,
+    "Should take a char");
+}
+
+int
+main()
+{
+  TestVoidVoid(f0);
+  TestVoidVoid(&f0);
+  TestVoidVoid(&S::f0);
+  TestVoidVoid(&S::f0c);
+  TestVoidVoid([]() {});
+  std::function<void()> ff0 = f0;
+  TestVoidVoid(ff0);
+
+  TestIntChar(f1);
+  TestIntChar(&f1);
+  TestIntChar(&S::f1);
+  TestIntChar(&S::f1c);
+  TestIntChar([](char) { return 0; });
+  std::function<int(char)> ff1 = f1;
+  TestIntChar(ff1);
+
+#ifdef NS_HAVE_STDCALL
+  TestVoidVoid(f0s);
+  TestVoidVoid(&f0s);
+  TestVoidVoid(&S::f0s);
+  TestVoidVoid(&S::f0cs);
+  std::function<void()> ff0s = f0s;
+  TestVoidVoid(ff0s);
+
+  TestIntChar(f1s);
+  TestIntChar(&f1s);
+  TestIntChar(&S::f1s);
+  TestIntChar(&S::f1cs);
+  std::function<int(char)> ff1s = f1s;
+  TestIntChar(ff1s);
+#endif // NS_HAVE_STDCALL
+
+  return 0;
+}
--- a/mfbt/tests/moz.build
+++ b/mfbt/tests/moz.build
@@ -25,16 +25,17 @@ CppUnitTests([
     'TestDefineEnum',
     'TestDoublyLinkedList',
     'TestEndian',
     'TestEnumeratedArray',
     'TestEnumSet',
     'TestEnumTypeTraits',
     'TestFastBernoulliTrial',
     'TestFloatingPoint',
+    'TestFunctionTypeTraits',
     'TestIntegerPrintfMacros',
     'TestIntegerRange',
     'TestJSONWriter',
     'TestLinkedList',
     'TestMacroArgs',
     'TestMacroForEach',
     'TestMathAlgorithms',
     'TestMaybe',