1 //============================================================================
2 // Copyright (c) Kitware, Inc.
3 // All rights reserved.
4 // See LICENSE.txt for details.
5 //
6 // This software is distributed WITHOUT ANY WARRANTY; without even
7 // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
8 // PURPOSE. See the above copyright notice for more information.
9 //============================================================================
10 #ifndef vtk_m_internal_FunctionInterface_h
11 #define vtk_m_internal_FunctionInterface_h
12
13 #include <vtkm/Deprecated.h>
14 #include <vtkm/Types.h>
15
16 #include <vtkm/internal/FunctionInterfaceDetailPre.h>
17 #include <vtkm/internal/IndexTag.h>
18
19 #include <utility>
20
21 namespace vtkm
22 {
23 namespace internal
24 {
25
26 namespace detail
27 {
28
29 template <typename OriginalSignature, typename Transform>
30 struct FunctionInterfaceStaticTransformType;
31
32
33 } // namespace detail
34
35 /// \brief Holds parameters and result of a function.
36 ///
37 /// To make VTK-m easier for the end user developer, the
38 /// \c Invoke method of dispatchers takes an arbitrary amount of
39 /// arguments that get transformed and swizzled into arguments and return value
40 /// for a worklet operator. In between these two invocations a complicated
41 /// series of transformations and operations can occur.
42 ///
43 /// Supporting arbitrary function and template arguments is difficult and
44 /// really requires separate implementations for pre-C++11 and C++11 versions of
45 /// compilers. Thus, variadic template arguments are, at this point in time,
46 /// something to be avoided when possible. The intention of \c
47 /// FunctionInterface is to collect most of the variadic template code into one
48 /// place. The \c FunctionInterface template class takes a function signature,
49 /// which can have a variable number of arguments. The \c FunctionInterface
50 /// will hold in its state a copy of all input parameters (regardless of number
51 /// or type) and the return value if it exists (i.e. non-nullptr) and the function
52 /// has been invoked. This means that all arguments can be passed around in a
53 /// single object so that objects and functions dealing with these variadic
54 /// parameters can be templated on a single type (the type of \c
55 /// FunctionInterface).
56 ///
57 /// Note that the indexing of the parameters in a \c FunctionInterface starts
58 /// at 1. You can think of the return value being the parameter at index 0,
59 /// even if there is no return value. Although this is uncommon in C++, it
60 /// matches better the parameter indexing for other classes that deal with
61 /// function signatures.
62 ///
63 /// The \c FunctionInterface contains several ways to invoke a functor whose
64 /// parameters match those of the function interface. This allows you to
65 /// complete the transition of calling an arbitrary function (like a worklet).
66 ///
67 /// The following is a rundown of how a \c FunctionInterface is created and
68 /// used. See the independent documentation for more details.
69 ///
70 /// Use the \c make_FunctionInterface function to create a \c FunctionInterface
71 /// and initialize the state of all the parameters. \c make_FunctionInterface
72 /// takes a variable number of arguments, one for each parameter. Since the
73 /// return type is not specified as an argument, you must always specify it as
74 /// a template parameter.
75 ///
76 /// \code{.cpp}
77 /// vtkm::internal::FunctionInterface<void(vtkm::IdComponent,double,char)> functionInterface =
78 /// vtkm::internal::make_FunctionInterface<void>(1, 2.5, 'a');
79 /// \endcode
80 ///
81 /// The number of parameters can be retrieved either with the constant field
82 /// \c ARITY or with the \c GetArity method.
83 ///
84 /// \code{.cpp}
85 /// functionInterface.GetArity();
86 /// \endcode
87 ///
88 /// You can get a particular parameter using the templated function \c
89 /// ParameterGet. The template parameter is the index of the parameter
90 /// (starting at 1).
91 ///
92 /// Finally, there is a way to replace all of the parameters at
93 /// once. The \c StaticTransform methods take a transform functor that modifies
94 /// each of the parameters. See the documentation for this method for
95 /// details on how it is used.
96 ///
97 template <typename FunctionSignature>
98 class FunctionInterface
99 {
100 template <typename OtherSignature>
101 friend class FunctionInterface;
102
103 public:
104 using Signature = FunctionSignature;
105
106 VTKM_SUPPRESS_EXEC_WARNINGS
FunctionInterface()107 FunctionInterface()
108 : Parameters()
109 {
110 }
111
112 VTKM_SUPPRESS_EXEC_WARNINGS
FunctionInterface(const detail::ParameterContainer<FunctionSignature> & p)113 explicit FunctionInterface(const detail::ParameterContainer<FunctionSignature>& p)
114 : Parameters(p)
115 {
116 }
117
118 // the number of parameters as an integral constant
119 using SigInfo = detail::FunctionSigInfo<FunctionSignature>;
120 using ComponentSig = typename SigInfo::Components;
121 using ParameterSig = typename SigInfo::Parameters;
122
123 template <vtkm::IdComponent ParameterIndex>
124 struct ParameterType
125 {
126 using type = typename detail::AtType<ParameterIndex, FunctionSignature>::type;
127 };
128
129 /// The number of parameters in this \c Function Interface.
130 ///
131 static constexpr vtkm::IdComponent ARITY = SigInfo::Arity;
132
133 /// Returns the number of parameters held in this \c FunctionInterface. The
134 /// return value is the same as \c ARITY.
135 ///
136 VTKM_EXEC_CONT
GetArity()137 vtkm::IdComponent GetArity() const { return ARITY; }
138
139 template <typename Transform>
140 struct StaticTransformType
141 {
142 using type = FunctionInterface<
143 typename detail::FunctionInterfaceStaticTransformType<FunctionSignature, Transform>::type>;
144 };
145
146 /// \brief Transforms the \c FunctionInterface based on compile-time
147 /// information.
148 ///
149 /// The \c StaticTransform methods transform all the parameters of this \c
150 /// FunctionInterface to different types and values based on compile-time
151 /// information. It operates by accepting a functor that two arguments. The
152 /// first argument is the parameter to transform and the second argument is
153 /// an \c IndexTag specifying the index of the parameter (which can be
154 /// ignored in many cases). The functor's return value is the transformed
155 /// value. The functor must also contain a templated struct name ReturnType
156 /// with an internal type named \c type that defines the return type of the
157 /// transform for a given input type and parameter index.
158 ///
159 /// The transformation is only applied to the parameters of the function. The
160 /// return argument is unaffected.
161 ///
162 /// The return type can be determined with the \c StaticTransformType
163 /// template.
164 ///
165 /// Here is an example of a transformation that converts a \c
166 /// FunctionInterface to another \c FunctionInterface containing pointers to
167 /// all of the parameters.
168 ///
169 /// \code
170 /// struct MyTransformFunctor {
171 /// template<typename T, vtkm::IdComponent Index>
172 /// struct ReturnType {
173 /// typedef const T *type;
174 /// };
175 ///
176 /// template<typename T, vtkm::IdComponent Index>
177 /// VTKM_CONT
178 /// const T *operator()(const T &x, vtkm::internal::IndexTag<Index>) const {
179 /// return &x;
180 /// }
181 /// };
182 ///
183 /// template<typename FunctionSignature>
184 /// typename vtkm::internal::FunctionInterface<FunctionSignature>::template StaticTransformType<MyTransformFunctor>::type
185 /// ImportantStuff(const vtkm::internal::FunctionInterface<FunctionSignature> &funcInterface)
186 /// {
187 /// return funcInterface.StaticTransformCont(MyTransformFunctor());
188 /// }
189 /// \endcode
190 ///
191 template <typename Transform>
StaticTransformCont(const Transform & transform)192 VTKM_CONT typename StaticTransformType<Transform>::type StaticTransformCont(
193 const Transform& transform)
194 {
195 using FuncIface = typename StaticTransformType<Transform>::type;
196 using PC = detail::ParameterContainer<typename FuncIface::Signature>;
197 return FuncIface{ detail::DoStaticTransformCont<PC>(transform, this->Parameters) };
198 }
199
200 detail::ParameterContainer<FunctionSignature> Parameters;
201 };
202
203 /// Gets the value for the parameter of the given index. Parameters are
204 /// indexed starting at 1. To use this method you have to specify a static,
205 /// compile time index.
206 ///
207 /// \code{.cpp}
208 /// template<FunctionSignature>
209 /// void Foo(const vtkm::internal::FunctionInterface<FunctionSignature> &fInterface)
210 /// {
211 /// bar( ParameterGet<2>(fInterface) );
212 /// }
213 /// \endcode
214 ///
215 template <vtkm::IdComponent ParameterIndex, typename FunctionSignature>
216 VTKM_EXEC_CONT auto ParameterGet(const FunctionInterface<FunctionSignature>& fInterface)
217 -> decltype(detail::ParameterGet(fInterface.Parameters,
218 vtkm::internal::IndexTag<ParameterIndex>{}))
219 {
220 return detail::ParameterGet(fInterface.Parameters, vtkm::internal::IndexTag<ParameterIndex>{});
221 }
222
223
224 //============================================================================
225 /// \brief Create a \c FunctionInterface
226 ///
227 /// \c make_FunctionInterface is a function that takes a variable number of
228 /// arguments and returns a \c FunctionInterface object containing these
229 /// objects. Since the return type for the function signature is not specified,
230 /// you must always specify it as a template parameter
231 ///
232 /// \code{.cpp}
233 /// vtkm::internal::FunctionInterface<void(int,double,char)> functionInterface =
234 /// vtkm::internal::make_FunctionInterface<void>(1, 2.5, 'a');
235 /// \endcode
236 ///
237
238 template <typename R, typename... Args>
make_FunctionInterface(const Args &...args)239 FunctionInterface<R(Args...)> make_FunctionInterface(const Args&... args)
240 {
241 // MSVC will issue deprecation warnings if this templated method is instantiated with
242 // a deprecated class here even if the method is called from a section of code where
243 // deprecation warnings are suppressed. This is annoying behavior since this templated
244 // method has no control over what class it is used from. To get around it, we have to
245 // suppress all deprecation warnings here.
246 #ifdef VTKM_MSVC
247 VTKM_DEPRECATED_SUPPRESS_BEGIN
248 #endif
249 detail::ParameterContainer<R(Args...)> container = { args... };
250 return FunctionInterface<R(Args...)>{ container };
251 #ifdef VTKM_MSVC
252 VTKM_DEPRECATED_SUPPRESS_END
253 #endif
254 }
255 }
256 } // namespace vtkm::internal
257
258 #include <vtkm/internal/FunctionInterfaceDetailPost.h>
259
260 #endif //vtk_m_internal_FunctionInterface_h
261