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