5.3. Function Interface Objects
For flexibility’s sake, a worklet is free to declare a ControlSignature
with whatever number of arguments are sensible for its operation.
The viskores::cont::Invoker is expected to support arguments that
match these arguments, and part of the invocation operation may require these
arguments to be augmented before the worklet is scheduled.
This leaves the invoker with the tricky task of managing some collection of
arguments of unknown size and unknown types.
To simplify this management, Viskores has the
viskores::internal::FunctionInterface class.
viskores::internal::FunctionInterface is a templated class that
manages a generic set of arguments and the return value from a function.
An instance of viskores::internal::FunctionInterface holds an
instance of each argument.
You can apply the arguments in a viskores::internal::FunctionInterface
object to a functor with a compatible prototype, and the resulting value of
the function call is saved in the
viskores::internal::FunctionInterface.
-
template<typename FunctionSignature>
class FunctionInterface Holds parameters and result of a function.
To make Viskores easier for the end user developer, the
Invokemethod of dispatchers takes an arbitrary amount of arguments that get transformed and swizzled into arguments and return value for a worklet operator. In between these two invocations a complicated series of transformations and operations can occur.Supporting arbitrary function and template arguments is difficult and really requires separate implementations for pre-C++11 and C++11 versions of compilers. Thus, variadic template arguments are, at this point in time, something to be avoided when possible. The intention of
FunctionInterfaceis to collect most of the variadic template code into one place. TheFunctionInterfacetemplate class takes a function signature, which can have a variable number of arguments. TheFunctionInterfacewill hold in its state a copy of all input parameters (regardless of number or type) and the return value if it exists (i.e. non-nullptr) and the function has been invoked. This means that all arguments can be passed around in a single object so that objects and functions dealing with these variadic parameters can be templated on a single type (the type ofFunctionInterface).Note that the indexing of the parameters in a
FunctionInterfacestarts at 1. You can think of the return value being the parameter at index 0, even if there is no return value. Although this is uncommon in C++, it matches better the parameter indexing for other classes that deal with function signatures.The
FunctionInterfacecontains several ways to invoke a functor whose parameters match those of the function interface. This allows you to complete the transition of calling an arbitrary function (like a worklet).The following is a rundown of how a
FunctionInterfaceis created and used. See the independent documentation for more details.Use the
make_FunctionInterfacefunction to create aFunctionInterfaceand initialize the state of all the parameters.make_FunctionInterfacetakes a variable number of arguments, one for each parameter. Since the return type is not specified as an argument, you must always specify it as a template parameter.viskores::internal::FunctionInterface<void(viskores::IdComponent,double,char)> functionInterface = viskores::internal::make_FunctionInterface<void>(1, 2.5, 'a');
The number of parameters can be retrieved either with the constant field
ARITYor with theGetAritymethod.functionInterface.GetArity();
You can get a particular parameter using the templated function
ParameterGet. The template parameter is the index of the parameter (starting at 1).Finally, there is a way to replace all of the parameters at once. The
StaticTransformmethods take a transform functor that modifies each of the parameters. See the documentation for this method for details on how it is used.Public Functions
-
inline viskores::IdComponent GetArity() const
Returns the number of parameters held in this
FunctionInterface.The return value is the same as
ARITY.
-
template<typename Transform>
inline StaticTransformType<Transform>::type StaticTransformCont(const Transform &transform) Transforms the
FunctionInterfacebased on compile-time information.The
StaticTransformmethods transform all the parameters of thisFunctionInterfaceto different types and values based on compile-time information. It operates by accepting a functor that two arguments. The first argument is the parameter to transform and the second argument is anIndexTagspecifying the index of the parameter (which can be ignored in many cases). The functor’s return value is the transformed value. The functor must also contain a templated struct name ReturnType with an internal type namedtypethat defines the return type of the transform for a given input type and parameter index.The transformation is only applied to the parameters of the function. The return argument is unaffected.
The return type can be determined with the
StaticTransformTypetemplate.Here is an example of a transformation that converts a
FunctionInterfaceto anotherFunctionInterfacecontaining pointers to all of the parameters.struct MyTransformFunctor { template<typename T, viskores::IdComponent Index> struct ReturnType { typedef const T *type; }; template<typename T, viskores::IdComponent Index> VISKORES_CONT const T *operator()(const T &x, viskores::internal::IndexTag<Index>) const { return &x; } }; template<typename FunctionSignature> typename viskores::internal::FunctionInterface<FunctionSignature>::template StaticTransformType<MyTransformFunctor>::type ImportantStuff(const viskores::internal::FunctionInterface<FunctionSignature> &funcInterface) { return funcInterface.StaticTransformCont(MyTransformFunctor()); }
Public Static Attributes
-
static constexpr viskores::IdComponent ARITY = SigInfo::Arity
The number of parameters in this
FunctionInterface.
-
template<viskores::IdComponent ParameterIndex>
struct ParameterType
-
template<typename Transform>
struct StaticTransformType
-
inline viskores::IdComponent GetArity() const
5.3.1. Declaring and Creating
viskores::internal::FunctionInterface is a templated class with a
single parameter.
The parameter is the function signature of the function.
A signature is a function type.
The syntax in C++ is the return type followed by the argument types enclosed
in parentheses.
1 // FunctionInterfaces matching some common POSIX functions.
2 viskores::internal::FunctionInterface<size_t(const char*)> strlenInterface;
3
4 viskores::internal::FunctionInterface<char*(char*, const char* s2, size_t)>
5 strncpyInterface;
The viskores::internal::make_FunctionInterface() function provides an
easy way to create a viskores::internal::FunctionInterface and
initialize the state of all the parameters.
viskores::internal::make_FunctionInterface() takes a variable number of
arguments, one for each parameter.
Since the return type is not specified as an argument, you must always specify
it as a template parameter.
-
template<typename R, typename ...Args>
FunctionInterface<R(Args...)> viskores::internal::make_FunctionInterface(const Args&... args) Create a
FunctionInterface.make_FunctionInterfaceis a function that takes a variable number of arguments and returns aFunctionInterfaceobject containing these objects. Since the return type for the function signature is not specified, you must always specify it as a template parameterviskores::internal::FunctionInterface<void(int,double,char)> functionInterface = viskores::internal::make_FunctionInterface<void>(1, 2.5, 'a');
1 const char* s = "Hello World";
2 static const size_t BUFFER_SIZE = 100;
3 char* buffer = (char*)malloc(BUFFER_SIZE);
4
5 strlenInterface = viskores::internal::make_FunctionInterface<size_t>(s);
6
7 strncpyInterface =
8 viskores::internal::make_FunctionInterface<char*>(buffer, s, BUFFER_SIZE);
5.3.2. Parameters
Once created, viskores::internal::FunctionInterface contains methods
to query and manage the parameters and objects associated with them.
The number of parameters can be retrieved either with the constant field
viskores::internal::FunctionInterface::ARITY or with the
viskores::internal::FunctionInterface::GetArity() method.
1 VISKORES_STATIC_ASSERT(
2 viskores::internal::FunctionInterface<size_t(const char*)>::ARITY == 1);
3
4 viskores::IdComponent arity = strncpyInterface.GetArity(); // arity = 3
You can use the viskores::internal::ParameterGet() function to retrieve
a parameter from a viskores::internal::FunctionInterface.
When using viskores::internal::ParameterGet(), you have to specify the
index of the parameter using a template argument.
Note that the parameters in viskores::internal::FunctionInterface
start at index 1.
Although this is uncommon in C++, it is customary to number function arguments
starting at 1.
-
template<viskores::IdComponent ParameterIndex, typename FunctionSignature>
auto viskores::internal::ParameterGet(const FunctionInterface<FunctionSignature> &fInterface) -> decltype(detail::ParameterGet(fInterface.Parameters, viskores::internal::IndexTag<ParameterIndex>{})) Gets the value for the parameter of the given index.
Parameters are indexed starting at 1. To use this method you have to specify a static, compile time index.
template<FunctionSignature> void Foo(const viskores::internal::FunctionInterface<FunctionSignature> &fInterface) { bar( ParameterGet<2>(fInterface) ); }
1template<typename FunctionSignature>
2void GetFirstParameter(
3 const viskores::internal::FunctionInterface<FunctionSignature>& interface)
4{
5 // The following two uses of GetParameter are equivalent
6 std::cout << viskores::internal::ParameterGet<1>(interface) << std::endl;
7}
5.3.3. Transformations
Rather than replace a single item in a
viskores::internal::FunctionInterface, it is desirable to change them
all in a similar way.
viskores::internal::FunctionInterface supports a static transform
that replaces all of the arguments with new types defined at compile time.
The static transform method,
viskores::internal::FunctionInterface::StaticTransformCont(), operates
by accepting a functor that defines a function with two arguments.
The first argument is the viskores::internal::FunctionInterface
parameter to transform.
The second argument is an instance of the
viskores::internal::IndexTag templated class that statically
identifies the parameter index being transformed.
An viskores::internal::IndexTag object has no state, but the class
contains a static integer named viskores::internal::IndexTag::INDEX.
The function returns the transformed argument.
-
template<viskores::IdComponent Index>
struct IndexTag A convenience tag to represent static indices.
Some classes like
FunctionInterfacehave a list of items that have numeric indices that must be resolved at compile time. Typically these are referenced with an integer template argument. However, such template arguments have to be explicitly defined in the template. They cannot be resolved through function or method arguments. In such cases, it is convenient to use this tag to encapsulate the index.
The functor must also contain a templated class named ReturnType with an
internal type named type that defines the return type of the transform for
a given parameter type.
ReturnType must have two template parameters.
The first template parameter is the type of the
viskores::internal::FunctionInterface parameter to transform.
It is the same type as passed to the operator.
The second template parameter is a viskores::IdComponent specifying
the index.
The transformation is only applied to the parameters of the function. The return argument is unaffected.
The return type can be determined with the
viskores::internal::FunctionInterface::StaticTransformType template in
the viskores::internal::FunctionInterface class.
viskores::internal::FunctionInterface::StaticTransformType has a
single parameter that is the transform functor and contains a type named
type that is the transformed
viskores::internal::FunctionInterface.
In the following example, a static transform is used to convert a
viskores::internal::FunctionInterface to a new object that has the
pointers to the parameters rather than the values themselves.
The parameter index is always ignored because all parameters are uniformly
transformed.
1struct ParametersToPointersFunctor
2{
3 template<typename T, viskores::IdComponent Index>
4 struct ReturnType
5 {
6 using type = const T*;
7 };
8
9 template<typename T, viskores::IdComponent Index>
10 VISKORES_CONT const T* operator()(const T& x,
11 viskores::internal::IndexTag<Index>) const
12 {
13 return &x;
14 }
15};
16
17template<typename FunctionInterfaceType>
18VISKORES_CONT typename FunctionInterfaceType::template StaticTransformType<
19 ParametersToPointersFunctor>::type
20ParametersToPointers(FunctionInterfaceType& functionInterface)
21{
22 return functionInterface.StaticTransformCont(ParametersToPointersFunctor());
23}