diff --git a/mfbt/FunctionTypeTraits.h b/mfbt/FunctionTypeTraits.h new file mode 100644 index 000000000000..bbfef32c9ccb --- /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 + +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: Type of the Nth** parameter, 0-indexed. +// +// ** `ParameterType` 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 +// auto foo(F&&) +// -> enable_if(FunctionTypeTraits::arity == 1 && +// is_same::template ParameterType<0>, +// int>::value, +// void) +// { +// // This function will only be enabled if `F` takes one `int`. +// // Without the permissive ParameterType, 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 +struct FunctionTypeTraits; + +// Remove reference and pointer wrappers, if any. +template +struct FunctionTypeTraits : public FunctionTypeTraits +{ +}; +template +struct FunctionTypeTraits : public FunctionTypeTraits +{ +}; +template +struct FunctionTypeTraits : public FunctionTypeTraits +{ +}; + +// Extract `operator()` function from callables (e.g. lambdas, std::function). +template +struct FunctionTypeTraits : public FunctionTypeTraits +{ +}; + +namespace detail { + +// If `safe`, retrieve the `N`th type from `As`, otherwise `void`. +// See top description for reason. +template +struct TupleElementSafe; +template +struct TupleElementSafe +{ + using Type = typename std::tuple_element>::type; +}; +template +struct TupleElementSafe +{ + using Type = void; +}; + +template +struct FunctionTypeTraitsHelper +{ + using ReturnType = R; + static constexpr size_t arity = sizeof...(As); + template + using ParameterType = + typename TupleElementSafe<(N < sizeof...(As)), N, As...>::Type; +}; + +} // namespace detail + +// Specialization for free functions. +template +struct FunctionTypeTraits : detail::FunctionTypeTraitsHelper +{ +}; + +// Specialization for non-const member functions. +template +struct FunctionTypeTraits + : detail::FunctionTypeTraitsHelper +{ +}; + +// Specialization for const member functions. +template +struct FunctionTypeTraits + : detail::FunctionTypeTraitsHelper +{ +}; + +#ifdef NS_HAVE_STDCALL +// Specialization for __stdcall free functions. +template +struct FunctionTypeTraits + : detail::FunctionTypeTraitsHelper +{ +}; + +// Specialization for __stdcall non-const member functions. +template +struct FunctionTypeTraits + : detail::FunctionTypeTraitsHelper +{ +}; + +// Specialization for __stdcall const member functions. +template +struct FunctionTypeTraits + : detail::FunctionTypeTraitsHelper +{ +}; +#endif // NS_HAVE_STDCALL + +} // namespace mozilla + +#endif // mozilla_FunctionTypeTraits_h diff --git a/mfbt/moz.build b/mfbt/moz.build index 108cc1348363..c3aebca99a22 100644 --- a/mfbt/moz.build +++ b/mfbt/moz.build @@ -42,6 +42,7 @@ EXPORTS.mozilla = [ 'FastBernoulliTrial.h', 'FloatingPoint.h', 'FStream.h', + 'FunctionTypeTraits.h', 'GuardObjects.h', 'HashFunctions.h', 'HashTable.h', diff --git a/mfbt/tests/TestFunctionTypeTraits.cpp b/mfbt/tests/TestFunctionTypeTraits.cpp new file mode 100644 index 000000000000..fdef061d20d8 --- /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 + +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::ReturnType, + void>::value, + "f0 returns void"); +static_assert(FunctionTypeTraits::arity == 0, + "f0 takes no parameters"); +static_assert( + std::is_same< + typename FunctionTypeTraits::template ParameterType<0>, + void>::value, + "f0 has no first parameter"); + +static_assert( + std::is_same::ReturnType, + void>::value, + "S::f0 returns void"); +static_assert(FunctionTypeTraits::arity == 0, + "S::f0 takes no parameters"); +static_assert( + std::is_same< + typename FunctionTypeTraits::template ParameterType<0>, + void>::value, + "S::f0 has no first parameter"); + +static_assert( + std::is_same::ReturnType, + void>::value, + "S::f0c returns void"); +static_assert(FunctionTypeTraits::arity == 0, + "S::f0c takes no parameters"); +static_assert( + std::is_same< + typename FunctionTypeTraits::template ParameterType<0>, + void>::value, + "S::f0c has no first parameter"); + +static_assert( + std::is_same::ReturnType, + int>::value, + "f1 returns int"); +static_assert(FunctionTypeTraits::arity == 1, + "f1 takes one parameter"); +static_assert( + std::is_same< + typename FunctionTypeTraits::template ParameterType<0>, + char>::value, + "f1 takes a char"); + +static_assert( + std::is_same::ReturnType, + int>::value, + "S::f1 returns int"); +static_assert(FunctionTypeTraits::arity == 1, + "S::f1 takes one parameter"); +static_assert( + std::is_same< + typename FunctionTypeTraits::template ParameterType<0>, + char>::value, + "S::f1 takes a char"); + +static_assert( + std::is_same::ReturnType, + int>::value, + "S::f1c returns int"); +static_assert(FunctionTypeTraits::arity == 1, + "S::f1c takes one parameter"); +static_assert( + std::is_same< + typename FunctionTypeTraits::template ParameterType<0>, + char>::value, + "S::f1c takes a char"); + +#ifdef NS_HAVE_STDCALL +static_assert( + std::is_same::ReturnType, + void>::value, + "f0s returns void"); +static_assert(FunctionTypeTraits::arity == 0, + "f0s takes no parameters"); +static_assert( + std::is_same< + typename FunctionTypeTraits::template ParameterType<0>, + void>::value, + "f0s has no first parameter"); + +static_assert( + std::is_same::ReturnType, + void>::value, + "S::f0s returns void"); +static_assert(FunctionTypeTraits::arity == 0, + "S::f0s takes no parameters"); +static_assert( + std::is_same< + typename FunctionTypeTraits::template ParameterType<0>, + void>::value, + "S::f0s has no first parameter"); + +static_assert( + std::is_same::ReturnType, + void>::value, + "S::f0cs returns void"); +static_assert(FunctionTypeTraits::arity == 0, + "S::f0cs takes no parameters"); +static_assert( + std::is_same< + typename FunctionTypeTraits::template ParameterType<0>, + void>::value, + "S::f0cs has no first parameter"); + +static_assert( + std::is_same::ReturnType, + int>::value, + "f1s returns int"); +static_assert(FunctionTypeTraits::arity == 1, + "f1s takes one parameter"); +static_assert( + std::is_same< + typename FunctionTypeTraits::template ParameterType<0>, + char>::value, + "f1s takes a char"); + +static_assert( + std::is_same::ReturnType, + int>::value, + "S::f1s returns int"); +static_assert(FunctionTypeTraits::arity == 1, + "S::f1s takes one parameter"); +static_assert( + std::is_same< + typename FunctionTypeTraits::template ParameterType<0>, + char>::value, + "S::f1s takes a char"); + +static_assert( + std::is_same::ReturnType, + int>::value, + "S::f1cs returns int"); +static_assert(FunctionTypeTraits::arity == 1, + "S::f1cs takes one parameter"); +static_assert( + std::is_same< + typename FunctionTypeTraits::template ParameterType<0>, + char>::value, + "S::f1cs takes a char"); +#endif // NS_HAVE_STDCALL + +template +void +TestVoidVoid(F&&) +{ + static_assert( + std::is_same::ReturnType, void>::value, + "Should return void"); + static_assert(FunctionTypeTraits::arity == 0, "Should take no parameters"); + static_assert( + std::is_same::template ParameterType<0>, + void>::value, + "Should have no first parameter"); +} + +template +void +TestIntChar(F&&) +{ + static_assert( + std::is_same::ReturnType, int>::value, + "Should return int"); + static_assert(FunctionTypeTraits::arity == 1, "Should take one parameter"); + static_assert( + std::is_same::template ParameterType<0>, + char>::value, + "Should take a char"); +} + +int +main() +{ + TestVoidVoid(f0); + TestVoidVoid(&f0); + TestVoidVoid(&S::f0); + TestVoidVoid(&S::f0c); + TestVoidVoid([]() {}); + std::function ff0 = f0; + TestVoidVoid(ff0); + + TestIntChar(f1); + TestIntChar(&f1); + TestIntChar(&S::f1); + TestIntChar(&S::f1c); + TestIntChar([](char) { return 0; }); + std::function ff1 = f1; + TestIntChar(ff1); + +#ifdef NS_HAVE_STDCALL + TestVoidVoid(f0s); + TestVoidVoid(&f0s); + TestVoidVoid(&S::f0s); + TestVoidVoid(&S::f0cs); + std::function ff0s = f0s; + TestVoidVoid(ff0s); + + TestIntChar(f1s); + TestIntChar(&f1s); + TestIntChar(&S::f1s); + TestIntChar(&S::f1cs); + std::function ff1s = f1s; + TestIntChar(ff1s); +#endif // NS_HAVE_STDCALL + + return 0; +} diff --git a/mfbt/tests/moz.build b/mfbt/tests/moz.build index d46386c6316d..b9a62f76019b 100644 --- a/mfbt/tests/moz.build +++ b/mfbt/tests/moz.build @@ -30,6 +30,7 @@ CppUnitTests([ 'TestEnumTypeTraits', 'TestFastBernoulliTrial', 'TestFloatingPoint', + 'TestFunctionTypeTraits', 'TestIntegerPrintfMacros', 'TestIntegerRange', 'TestJSONWriter',