KaMPIng 0.1.0
(Near) zero-overhead C++ MPI bindings.
Loading...
Searching...
No Matches
has_member.hpp
Go to the documentation of this file.
1// This file is part of KaMPIng.
2//
3// Copyright 2022 The KaMPIng Authors
4//
5// KaMPIng is free software : you can redistribute it and/or modify it under the
6// terms of the GNU Lesser General Public License as published by the Free
7// Software Foundation, either version 3 of the License, or (at your option) any
8// later version. KaMPIng is distributed in the hope that it will be useful, but
9// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
11// for more details.
12//
13// You should have received a copy of the GNU Lesser General Public License
14// along with KaMPIng. If not, see <https://www.gnu.org/licenses/>.
15
16/// @file
17/// @brief Macros for generating concept-like type traits to check for member functions of objects.
18
19#pragma once
20
21#include <type_traits>
22
23/// @brief Macro for generating has_member_xxx and has_member_xxx_v templates.
24/// They return true if the type given as template parameter has a member
25/// (template) function provided name.
26///
27/// If the function has no template parameters or they can be auto-deduced, use
28/// \c has_member_xxx::value or \c has_member_xxx_v, if not use \c
29/// has_member_xxx::value_with_template_params<...>.
30///
31/// If the member function takes arguments, pass their types as additional
32/// template parameters to \c has_member_xxx.
33///
34/// See the examples for details.
35///
36/// Example:
37/// \code
38/// // Add templates has_member_bar and has_member_bar_v
39/// KAMPING_MAKE_HAS_MEMBER(bar)
40///
41/// // Add templates has_member_baz and has_member_baz_v
42/// KAMPING_MAKE_HAS_MEMBER(baz)
43///
44/// // Add templates has_member_fizz and has_member_fizz_v
45/// KAMPING_MAKE_HAS_MEMBER(fizz)
46///
47/// struct Foo {
48/// int bar();
49/// int baz(char);
50/// template<typename T>
51/// int fizz(T);
52/// };
53///
54/// // check if Foo.bar() is callable
55/// static_assert(has_member_bar_v<Foo>)
56///
57/// // check if Foo.bar(char) is callable
58/// static_assert(!has_member_bar_v<Foo, char>)
59///
60/// // check if Foo.baz(char) is callable
61/// static_assert(has_member_baz_v<Foo, char>)
62///
63/// // check if Foo.baz() is callable
64/// static_assert(!has_member_baz_v<Foo>)
65///
66/// // check if Foo.fizz(int) is callable
67/// static_assert(has_member_fizz_v<Foo, int>)
68///
69/// // check if Foo.fizz<int>(int) is callable
70/// static_assert(has_member_fizz<Foo, int>::value_with_template_params<int>)
71///
72/// // check if Foo.fizz<int, double>() is callable
73/// static_assert(!has_member_fizz<Foo>::value_with_template_params<int, double>)
74/// \endcode
75///
76/// Explanation:
77/// - To obtain \c value, the static member function \c test is instantiated
78/// using the given type \c Type.
79/// - Using declval, we get an instance of \c Type and try to call the expected
80/// member function with instances of the passed \c MemberArgs.
81/// - Optionally, \c test_with_template_params also instantiates the
82/// functions template parameters with the passed types.
83/// - If that member does not exist, we can not obtain the \c decltype, and cannot
84/// instantiate \c std::void_t<...>, which fails to initialize the whole function
85/// \c test(int).
86/// - Then, the next best instantiation is \c test(long), which returns \c
87/// std::false_type.
88/// - If we find the requested member, we get \c std::true_type.
89/// - \c test has \c int and \c long overloads to resolve ambiguity. Passing 0
90/// to \c test ensure that we first try to instantiate the \c true variant.
91#define KAMPING_MAKE_HAS_MEMBER(Member) \
92 template <typename Type, typename... MemberArgs> \
93 class has_member_##Member { \
94 template < \
95 typename C, \
96 typename = std::void_t<decltype(std::declval<C>().Member(std::declval<MemberArgs>()...))>> \
97 static auto test(int) -> std::true_type; \
98 template <typename C> \
99 static auto test(long) -> std::false_type; \
100 template < \
101 typename C, \
102 typename... TemplateParams, \
103 typename = std::void_t< \
104 decltype(std::declval<C>().template Member<TemplateParams...>(std::declval<MemberArgs>()...))>> \
105 static auto test_with_template_params(int) -> std::true_type; \
106 template <typename C, typename... TemplateParams> \
107 static auto test_with_template_params(long) -> std::false_type; \
108 \
109 public: \
110 static constexpr bool value = decltype(test<Type>(0))::value; \
111 \
112 template <typename... TemplateParams> \
113 static constexpr bool value_with_template_params = \
114 decltype(test_with_template_params<Type, TemplateParams...>(0))::value; \
115 }; \
116 \
117 template <typename Type, typename... MemberArgs> \
118 [[maybe_unused]] static constexpr bool has_member_##Member##_v = has_member_##Member<Type, MemberArgs...>::value;