mirror of
https://gitlab.kitware.com/vtk/vtk-m
synced 2024-09-08 13:23:51 +00:00
Add FunctionInterface class.
The FunctionInterface class is a convienient way to wrap up a variable number of arguments and pass them around templated interfaces without requiring variadic template arguments. It also correctly hands return arguments.
This commit is contained in:
parent
c008213561
commit
ebc20b432d
@ -22,6 +22,7 @@ set(headers
|
||||
ConfigureFor32.h
|
||||
ConfigureFor64.h
|
||||
ExportMacros.h
|
||||
FunctionInterface.h
|
||||
)
|
||||
|
||||
vtkm_declare_headers(${headers})
|
||||
|
770
vtkm/internal/FunctionInterface.h
Normal file
770
vtkm/internal/FunctionInterface.h
Normal file
@ -0,0 +1,770 @@
|
||||
//============================================================================
|
||||
// Copyright (c) Kitware, Inc.
|
||||
// All rights reserved.
|
||||
// See LICENSE.txt for details.
|
||||
// This software is distributed WITHOUT ANY WARRANTY; without even
|
||||
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
// PURPOSE. See the above copyright notice for more information.
|
||||
//
|
||||
// Copyright 2014 Sandia Corporation.
|
||||
// Copyright 2014 UT-Battelle, LLC.
|
||||
// Copyright 2014. Los Alamos National Security
|
||||
//
|
||||
// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
|
||||
// the U.S. Government retains certain rights in this software.
|
||||
//
|
||||
// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National
|
||||
// Laboratory (LANL), the U.S. Government retains certain rights in
|
||||
// this software.
|
||||
//============================================================================
|
||||
#ifndef vtk_m_cont_internal_FunctionInterface_h
|
||||
#define vtk_m_cont_internal_FunctionInterface_h
|
||||
|
||||
#include <vtkm/Types.h>
|
||||
#include <vtkm/cont/ErrorControlBadValue.h>
|
||||
|
||||
#include <boost/function_types/components.hpp>
|
||||
#include <boost/function_types/function_arity.hpp>
|
||||
#include <boost/function_types/function_type.hpp>
|
||||
#include <boost/function_types/parameter_types.hpp>
|
||||
#include <boost/function_types/result_type.hpp>
|
||||
#include <boost/integer/static_min_max.hpp>
|
||||
#include <boost/mpl/advance.hpp>
|
||||
#include <boost/mpl/at.hpp>
|
||||
#include <boost/mpl/begin.hpp>
|
||||
#include <boost/mpl/erase.hpp>
|
||||
#include <boost/mpl/insert.hpp>
|
||||
#include <boost/mpl/push_back.hpp>
|
||||
#include <boost/preprocessor/cat.hpp>
|
||||
#include <boost/preprocessor/control/if.hpp>
|
||||
#include <boost/preprocessor/dec.hpp>
|
||||
#include <boost/preprocessor/inc.hpp>
|
||||
#include <boost/preprocessor/punctuation/comma_if.hpp>
|
||||
#include <boost/preprocessor/repetition/enum_params.hpp>
|
||||
#include <boost/preprocessor/repetition/enum_shifted.hpp>
|
||||
#include <boost/preprocessor/repetition/enum_shifted_binary_params.hpp>
|
||||
#include <boost/preprocessor/repetition/enum_shifted_params.hpp>
|
||||
#include <boost/preprocessor/repetition/repeat.hpp>
|
||||
#include <boost/utility/enable_if.hpp>
|
||||
|
||||
#define VTKM_MAX_FUNCTION_PARAMETERS 10
|
||||
|
||||
namespace vtkm {
|
||||
namespace internal {
|
||||
|
||||
/// This struct is used internally by FunctionInterface to store the return
|
||||
/// value of a function. There is a special implementation for a return type of
|
||||
/// void, which stores nothing.
|
||||
///
|
||||
template<typename T>
|
||||
struct FunctionInterfaceReturnContainer {
|
||||
T Value;
|
||||
static const bool VALID = true;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct FunctionInterfaceReturnContainer<void> {
|
||||
// Nothing to store for void return.
|
||||
static const bool VALID = false;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
// If you get a compiler error stating that this class is not specialized, that
|
||||
// probably means that you are using FunctionInterface with an unsupported
|
||||
// number of arguments.
|
||||
template<typename FunctionSignature>
|
||||
struct ParameterContainer;
|
||||
|
||||
// The following code uses the Boost preprocessor utilities to create
|
||||
// definitions of ParameterContainer for all supported number of arguments.
|
||||
// The created classes are conceptually defined as follows:
|
||||
//
|
||||
// template<typename P0, // Return type
|
||||
// typename P1,
|
||||
// typename P2, ...>
|
||||
// struct ParameterContainer<P0(P1,P2,...)> {
|
||||
// P1 Parameter1;
|
||||
// P2 Parameter2;
|
||||
// ...
|
||||
// };
|
||||
//
|
||||
// These are defined for 0 to VTKM_MAX_FUNCTION_PARAMETERS parameters.
|
||||
|
||||
#define VTK_M_PARAMETER_DEFINITION(z, ParamIndex, data) \
|
||||
BOOST_PP_IF(ParamIndex, \
|
||||
BOOST_PP_CAT(P,ParamIndex) BOOST_PP_CAT(Parameter,ParamIndex);,)
|
||||
|
||||
#define VTK_M_PARAMETER_CONTAINER(NumParamsPlusOne) \
|
||||
template<BOOST_PP_ENUM_PARAMS(NumParamsPlusOne, typename P)> \
|
||||
struct ParameterContainer<P0(BOOST_PP_ENUM_SHIFTED_PARAMS(NumParamsPlusOne, P))> \
|
||||
{ \
|
||||
BOOST_PP_REPEAT(NumParamsPlusOne, VTK_M_PARAMETER_DEFINITION,) \
|
||||
};
|
||||
|
||||
#define VTK_M_PARAMETER_CONTAINER_REPEAT(z, NumParams, data) \
|
||||
VTK_M_PARAMETER_CONTAINER(BOOST_PP_INC(NumParams))
|
||||
BOOST_PP_REPEAT(BOOST_PP_INC(VTKM_MAX_FUNCTION_PARAMETERS),
|
||||
VTK_M_PARAMETER_CONTAINER_REPEAT,)
|
||||
|
||||
#undef VTK_M_PARAMETER_CONTAINER_REPEAT
|
||||
#undef VTK_M_PARAMETER_CONTAINER
|
||||
#undef VTK_M_PARAMETER_DEFINITION
|
||||
|
||||
template<int ParameterIndex, typename FunctionSignature>
|
||||
struct ParameterContainerAccess;
|
||||
|
||||
// The following code uses the Boost preprocessor utilities to create
|
||||
// definitions of ParameterContainerAccess for all supported number of
|
||||
// arguments. The created class specalizations conceptually create the
|
||||
// following interface:
|
||||
//
|
||||
// template<int ParameterIndex, typename R(P1,P2,...)>
|
||||
// struct ParameterContainerAccess
|
||||
// {
|
||||
// VTKM_EXEC_CONT_EXPORT
|
||||
// static ParameterType
|
||||
// GetParameter(const ParameterContainer<R(P1,P2,...)> ¶meters);
|
||||
//
|
||||
// VTKM_EXEC_CONT_EXPORT
|
||||
// static void SetParameter(ParameterContainer<R(P1,P2,...)> ¶meters,
|
||||
// const ParameterType &value);
|
||||
// };
|
||||
//
|
||||
// Here ParameterType is the P# type in the function signature for the given
|
||||
// ParameterIndex. It is the same you would get for
|
||||
// FunctionInterface::ParameterType.
|
||||
|
||||
#define VTK_M_PARAMETER_CONTAINER_ACCESS(ParameterIndex) \
|
||||
template<typename FunctionSignature> \
|
||||
struct ParameterContainerAccess<ParameterIndex, FunctionSignature> { \
|
||||
typedef typename boost::mpl::at_c< \
|
||||
boost::function_types::components<FunctionSignature>, \
|
||||
ParameterIndex>::type ParameterType; \
|
||||
VTKM_EXEC_CONT_EXPORT \
|
||||
static \
|
||||
ParameterType \
|
||||
GetParameter(const ParameterContainer<FunctionSignature> ¶meters) { \
|
||||
return parameters.BOOST_PP_CAT(Parameter, ParameterIndex); \
|
||||
} \
|
||||
VTKM_EXEC_CONT_EXPORT \
|
||||
static \
|
||||
void SetParameter(ParameterContainer<FunctionSignature> ¶meters, \
|
||||
const ParameterType &value) { \
|
||||
parameters.BOOST_PP_CAT(Parameter, ParameterIndex) = value; \
|
||||
} \
|
||||
};
|
||||
|
||||
#define VTK_M_PARAMETER_CONTAINER_ACCESS_REPEAT(z, i, data) \
|
||||
VTK_M_PARAMETER_CONTAINER_ACCESS(BOOST_PP_INC(i))
|
||||
BOOST_PP_REPEAT(BOOST_PP_INC(VTKM_MAX_FUNCTION_PARAMETERS),
|
||||
VTK_M_PARAMETER_CONTAINER_ACCESS_REPEAT,)
|
||||
|
||||
#undef VTK_M_PARAMETER_CONTAINER_ACCESS_REPEAT
|
||||
#undef VTK_M_PARAMETER_CONTAINER_ACCESS
|
||||
|
||||
template<int ParameterIndex, typename FunctionSignature>
|
||||
VTKM_EXEC_CONT_EXPORT
|
||||
typename ParameterContainerAccess<ParameterIndex,FunctionSignature>::ParameterType
|
||||
GetParameter(const ParameterContainer<FunctionSignature> ¶meters) {
|
||||
return ParameterContainerAccess<ParameterIndex,FunctionSignature>::GetParameter(parameters);
|
||||
}
|
||||
|
||||
template<int ParameterIndex, typename FunctionSignature>
|
||||
VTKM_EXEC_CONT_EXPORT
|
||||
|
||||
void SetParameter(ParameterContainer<FunctionSignature> ¶meters,
|
||||
const typename ParameterContainerAccess<ParameterIndex,FunctionSignature>::ParameterType &value) {
|
||||
return ParameterContainerAccess<ParameterIndex,FunctionSignature>::SetParameter(parameters, value);
|
||||
}
|
||||
|
||||
struct IdentityFunctor {
|
||||
template<typename T>
|
||||
VTKM_EXEC_CONT_EXPORT
|
||||
T &operator()(T &x) const { return x; }
|
||||
|
||||
template<typename T>
|
||||
VTKM_EXEC_CONT_EXPORT
|
||||
const T &operator()(const T &x) const { return x; }
|
||||
};
|
||||
|
||||
// The following code uses the Boost preprocessor utilities to create
|
||||
// definitions of DoInvoke functions for all supported number of arguments.
|
||||
// The created functions are conceptually defined as follows:
|
||||
//
|
||||
// template<typename Function, typename Signature, typename TransformFunctor>
|
||||
// VTKM_CONT_EXPORT
|
||||
// void DoInvokeCont(const Function &f,
|
||||
// ParameterContainer<Signature> ¶meters,
|
||||
// FunctionInterfaceReturnContainer<R> &result,
|
||||
// const TransformFunctor &transform)
|
||||
// {
|
||||
// result.Value = transform(f(transform(parameters.Parameter1),...));
|
||||
// }
|
||||
//
|
||||
// We define multiple DoInvokeCont and DoInvokeExec that do identical things
|
||||
// with different exports. It is important to have these separate definitions
|
||||
// instead of a single version with VTKM_EXEC_CONT_EXPORT because the function
|
||||
// to be invoked may only be viable in one or the other. There are also
|
||||
// separate versions that support const functions and non-const functions.
|
||||
// (However, the structures from the FunctionInterface must always be
|
||||
// non-const.) Finally, there is a special version for functions that return
|
||||
// void so that the function does not try to invalidly save a void value.
|
||||
|
||||
#define VTK_M_DO_INVOKE_TPARAM(z, count, data) \
|
||||
transform(BOOST_PP_CAT(parameters.Parameter, count))
|
||||
|
||||
#define VTK_M_DO_INVOKE(NumParamsPlusOne) \
|
||||
template<typename Function, \
|
||||
typename TransformFunctor, \
|
||||
BOOST_PP_ENUM_PARAMS(NumParamsPlusOne, typename P)> \
|
||||
VTK_M_DO_INVOKE_EXPORT \
|
||||
void VTK_M_DO_INVOKE_NAME( \
|
||||
VTK_M_DO_INVOKE_FUNCTION_CONST Function &f, \
|
||||
ParameterContainer<P0(BOOST_PP_ENUM_SHIFTED_PARAMS(NumParamsPlusOne, P))> ¶meters, \
|
||||
FunctionInterfaceReturnContainer<P0> &result, \
|
||||
const TransformFunctor &transform) \
|
||||
{ \
|
||||
(void)parameters; \
|
||||
result.Value = \
|
||||
transform( \
|
||||
f(BOOST_PP_ENUM_SHIFTED(NumParamsPlusOne, VTK_M_DO_INVOKE_TPARAM, ))); \
|
||||
} \
|
||||
\
|
||||
template<typename Function, \
|
||||
typename TransformFunctor BOOST_PP_COMMA_IF(BOOST_PP_DEC(NumParamsPlusOne)) \
|
||||
BOOST_PP_ENUM_SHIFTED_PARAMS(NumParamsPlusOne, typename P)> \
|
||||
VTK_M_DO_INVOKE_EXPORT \
|
||||
void VTK_M_DO_INVOKE_NAME( \
|
||||
VTK_M_DO_INVOKE_FUNCTION_CONST Function &f, \
|
||||
ParameterContainer<void(BOOST_PP_ENUM_SHIFTED_PARAMS(NumParamsPlusOne, P))> ¶meters, \
|
||||
FunctionInterfaceReturnContainer<void> &, \
|
||||
const TransformFunctor &transform) \
|
||||
{ \
|
||||
(void)parameters; \
|
||||
f(BOOST_PP_ENUM_SHIFTED(NumParamsPlusOne, VTK_M_DO_INVOKE_TPARAM, )); \
|
||||
}
|
||||
#define VTK_M_DO_INVOKE_REPEAT(z, NumParams, data) \
|
||||
VTK_M_DO_INVOKE(BOOST_PP_INC(NumParams));
|
||||
|
||||
#define VTK_M_DO_INVOKE_NAME DoInvokeCont
|
||||
#define VTK_M_DO_INVOKE_EXPORT VTKM_CONT_EXPORT
|
||||
#define VTK_M_DO_INVOKE_FUNCTION_CONST const
|
||||
BOOST_PP_REPEAT(BOOST_PP_INC(VTKM_MAX_FUNCTION_PARAMETERS),
|
||||
VTK_M_DO_INVOKE_REPEAT,)
|
||||
#undef VTK_M_DO_INVOKE_NAME
|
||||
#undef VTK_M_DO_INVOKE_EXPORT
|
||||
#undef VTK_M_DO_INVOKE_FUNCTION_CONST
|
||||
|
||||
#define VTK_M_DO_INVOKE_NAME DoInvokeCont
|
||||
#define VTK_M_DO_INVOKE_EXPORT VTKM_CONT_EXPORT
|
||||
#define VTK_M_DO_INVOKE_FUNCTION_CONST
|
||||
BOOST_PP_REPEAT(BOOST_PP_INC(VTKM_MAX_FUNCTION_PARAMETERS),
|
||||
VTK_M_DO_INVOKE_REPEAT,)
|
||||
#undef VTK_M_DO_INVOKE_NAME
|
||||
#undef VTK_M_DO_INVOKE_EXPORT
|
||||
#undef VTK_M_DO_INVOKE_FUNCTION_CONST
|
||||
|
||||
#define VTK_M_DO_INVOKE_NAME DoInvokeExec
|
||||
#define VTK_M_DO_INVOKE_EXPORT VTKM_EXEC_EXPORT
|
||||
#define VTK_M_DO_INVOKE_FUNCTION_CONST const
|
||||
BOOST_PP_REPEAT(BOOST_PP_INC(VTKM_MAX_FUNCTION_PARAMETERS),
|
||||
VTK_M_DO_INVOKE_REPEAT,)
|
||||
#undef VTK_M_DO_INVOKE_NAME
|
||||
#undef VTK_M_DO_INVOKE_EXPORT
|
||||
#undef VTK_M_DO_INVOKE_FUNCTION_CONST
|
||||
|
||||
#define VTK_M_DO_INVOKE_NAME DoInvokeExec
|
||||
#define VTK_M_DO_INVOKE_EXPORT VTKM_EXEC_EXPORT
|
||||
#define VTK_M_DO_INVOKE_FUNCTION_CONST
|
||||
BOOST_PP_REPEAT(BOOST_PP_INC(VTKM_MAX_FUNCTION_PARAMETERS),
|
||||
VTK_M_DO_INVOKE_REPEAT,)
|
||||
#undef VTK_M_DO_INVOKE_NAME
|
||||
#undef VTK_M_DO_INVOKE_EXPORT
|
||||
#undef VTK_M_DO_INVOKE_FUNCTION_CONST
|
||||
|
||||
#undef VTK_M_DO_INVOKE_REPEAT
|
||||
#undef VTK_M_DO_INVOKE
|
||||
#undef VTK_M_DO_INVOKE_TPARAM
|
||||
|
||||
|
||||
// These functions exist to help copy components of a FunctionInterface.
|
||||
|
||||
template<int NumToCopy, int ParameterIndex = 1>
|
||||
struct FunctionInterfaceCopyParameters {
|
||||
template<typename DestSignature, typename SrcSignature>
|
||||
static
|
||||
VTKM_EXEC_CONT_EXPORT
|
||||
void Copy(vtkm::internal::detail::ParameterContainer<DestSignature> &dest,
|
||||
const vtkm::internal::detail::ParameterContainer<SrcSignature> &src)
|
||||
{
|
||||
vtkm::internal::detail::SetParameter<ParameterIndex>(
|
||||
dest,vtkm::internal::detail::GetParameter<ParameterIndex>(src));
|
||||
FunctionInterfaceCopyParameters<NumToCopy-1,ParameterIndex+1>::Copy(dest, src);
|
||||
}
|
||||
};
|
||||
|
||||
template<int ParameterIndex>
|
||||
struct FunctionInterfaceCopyParameters<0, ParameterIndex> {
|
||||
template<typename DestSignature, typename SrcSignature>
|
||||
static
|
||||
VTKM_EXEC_CONT_EXPORT
|
||||
void Copy(vtkm::internal::detail::ParameterContainer<DestSignature> &,
|
||||
const vtkm::internal::detail::ParameterContainer<SrcSignature> &)
|
||||
{
|
||||
// Nothing left to copy.
|
||||
}
|
||||
};
|
||||
|
||||
template<typename OriginalFunction,
|
||||
typename NewFunction,
|
||||
typename TransformFunctor,
|
||||
typename FinishFunctor>
|
||||
class FunctionInterfaceDynamicTransformContContinue;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<typename FunctionSignature>
|
||||
class FunctionInterface
|
||||
{
|
||||
template<typename OtherSignature>
|
||||
friend class FunctionInterface;
|
||||
|
||||
public:
|
||||
typedef FunctionSignature Signature;
|
||||
|
||||
typedef typename boost::function_types::result_type<FunctionSignature>::type
|
||||
ResultType;
|
||||
template<int ParameterIndex>
|
||||
struct ParameterType {
|
||||
typedef typename boost::mpl::at_c<
|
||||
boost::function_types::components<FunctionSignature>,
|
||||
ParameterIndex>::type type;
|
||||
};
|
||||
static const bool RETURN_VALID = FunctionInterfaceReturnContainer<ResultType>::VALID;
|
||||
|
||||
/// The number of parameters in this \c Function Interface.
|
||||
///
|
||||
static const int ARITY =
|
||||
boost::function_types::function_arity<FunctionSignature>::value;
|
||||
|
||||
/// Returns the number of parameters held in this \c FunctionInterface. The
|
||||
/// return value is the same as \c ARITY.
|
||||
///
|
||||
VTKM_EXEC_CONT_EXPORT
|
||||
int GetArity() const { return ARITY; }
|
||||
|
||||
/// Retrieves the return value from the last invocation called. This method
|
||||
/// will result in a compiler error if used with a function having a void
|
||||
/// return type.
|
||||
///
|
||||
VTKM_EXEC_CONT_EXPORT
|
||||
ResultType GetReturnValue() const { return this->Result.Value; }
|
||||
|
||||
/// Retrieves the return value from the last invocation wrapped in a \c
|
||||
/// FunctionInterfaceReturnContainer object. This call can succeed even if
|
||||
/// the return type is void. You still have to somehow check to make sure the
|
||||
/// return is non-void before trying to use it, but using this method can
|
||||
/// simplify templated programming.
|
||||
///
|
||||
VTKM_EXEC_CONT_EXPORT
|
||||
const FunctionInterfaceReturnContainer<ResultType> &GetReturnValueSafe() const
|
||||
{
|
||||
return this->Result;
|
||||
}
|
||||
VTKM_EXEC_CONT_EXPORT
|
||||
FunctionInterfaceReturnContainer<ResultType> &GetReturnValueSafe()
|
||||
{
|
||||
return this->Result;
|
||||
}
|
||||
|
||||
/// Gets the value for the parameter of the given index. Parameters are
|
||||
/// indexed starting at 1. To use this method you have to specify the index
|
||||
/// as a template parameter. If you are using FunctionInterface within a
|
||||
/// template (which is almost always the case), then you will have to use the
|
||||
/// template keyword. For example, here is a simple implementation of a
|
||||
/// method that grabs the first parameter of FunctionInterface.
|
||||
///
|
||||
/// \code{.cpp}
|
||||
/// template<FunctionSignature>
|
||||
/// void Foo(const vtkm::cont::internal::FunctionInterface<FunctionSignature> &fInterface)
|
||||
/// {
|
||||
/// bar(fInterface.template GetParameter<1>());
|
||||
/// }
|
||||
/// \endcode
|
||||
///
|
||||
template<int ParameterIndex>
|
||||
VTKM_EXEC_CONT_EXPORT
|
||||
typename ParameterType<ParameterIndex>::type
|
||||
GetParameter() {
|
||||
return detail::GetParameter<ParameterIndex>(this->Parameters);
|
||||
}
|
||||
|
||||
/// Sets the value for the parameter of the given index. Parameters are
|
||||
/// indexed starting at 1. To use this method you have to specify the index
|
||||
/// as a template parameter. If you are using FunctionInterface within a
|
||||
/// template (which is almost always the case), then you will have to use the
|
||||
/// template keyword.
|
||||
///
|
||||
template<int ParameterIndex>
|
||||
VTKM_EXEC_CONT_EXPORT
|
||||
void SetParameter(typename ParameterType<ParameterIndex>::type parameter)
|
||||
{
|
||||
detail::SetParameter<ParameterIndex>(this->Parameters, parameter);
|
||||
}
|
||||
|
||||
/// Copies the parameters and return values from the given \c
|
||||
/// FunctionInterface to this object. The types must be copiable from source
|
||||
/// to destination. If the number of parameters in the two objects are not
|
||||
/// the same, copies the first N arguments, where N is the smaller arity of
|
||||
/// the two function interfaces.
|
||||
///
|
||||
template<typename SrcFunctionSignature>
|
||||
void Copy(const FunctionInterface<SrcFunctionSignature> &src)
|
||||
{
|
||||
this->Result = src.GetReturnValueSafe();
|
||||
detail::FunctionInterfaceCopyParameters<
|
||||
boost::static_unsigned_min<ARITY, FunctionInterface<SrcFunctionSignature>::ARITY>::value>::
|
||||
Copy(this->Parameters, src.Parameters);
|
||||
}
|
||||
|
||||
/// Invoke a function \c f using the arguments stored in this
|
||||
/// FunctionInterface.
|
||||
///
|
||||
/// If this FunctionInterface specifies a non-void return value, then the
|
||||
/// result of the function call is stored within this FunctionInterface and
|
||||
/// can be retrieved with GetReturnValue().
|
||||
///
|
||||
template<typename Function>
|
||||
VTKM_CONT_EXPORT
|
||||
void InvokeCont(const Function &f) {
|
||||
detail::DoInvokeCont(f,
|
||||
this->Parameters,
|
||||
this->Result,
|
||||
detail::IdentityFunctor());
|
||||
}
|
||||
template<typename Function>
|
||||
VTKM_CONT_EXPORT
|
||||
void InvokeCont(Function &f) {
|
||||
detail::DoInvokeCont(f,
|
||||
this->Parameters,
|
||||
this->Result,
|
||||
detail::IdentityFunctor());
|
||||
}
|
||||
template<typename Function>
|
||||
VTKM_EXEC_EXPORT
|
||||
void InvokeExec(const Function &f) {
|
||||
detail::DoInvokeExec(f,
|
||||
this->Parameters,
|
||||
this->Result,
|
||||
detail::IdentityFunctor());
|
||||
}
|
||||
template<typename Function>
|
||||
VTKM_EXEC_EXPORT
|
||||
void InvokeExec(Function &f) {
|
||||
detail::DoInvokeExec(f,
|
||||
this->Parameters,
|
||||
this->Result,
|
||||
detail::IdentityFunctor());
|
||||
}
|
||||
|
||||
/// Invoke a function \c f using the arguments stored in this
|
||||
/// FunctionInterface and a transform.
|
||||
///
|
||||
/// These versions of invoke also apply a transform to the input arguments.
|
||||
/// The transform is a second functor passed a second argument. If this
|
||||
/// FunctionInterface specifies a non-void return value, then the result of
|
||||
/// the function call is also transformed and stored within this
|
||||
/// FunctionInterface and can be retrieved with GetReturnValue().
|
||||
///
|
||||
template<typename Function, typename TransformFunctor>
|
||||
VTKM_CONT_EXPORT
|
||||
void InvokeCont(const Function &f, const TransformFunctor &transform) {
|
||||
detail::DoInvokeCont(f, this->Parameters, this->Result, transform);
|
||||
}
|
||||
template<typename Function, typename TransformFunctor>
|
||||
VTKM_CONT_EXPORT
|
||||
void InvokeCont(Function &f, const TransformFunctor &transform) {
|
||||
detail::DoInvokeCont(f, this->Parameters, this->Result, transform);
|
||||
}
|
||||
template<typename Function, typename TransformFunctor>
|
||||
VTKM_EXEC_EXPORT
|
||||
void InvokeExec(const Function &f, const TransformFunctor &transform) {
|
||||
detail::DoInvokeExec(f, this->Parameters, this->Result, transform);
|
||||
}
|
||||
template<typename Function, typename TransformFunctor>
|
||||
VTKM_EXEC_EXPORT
|
||||
void InvokeExec(Function &f, const TransformFunctor &transform) {
|
||||
detail::DoInvokeExec(f, this->Parameters, this->Result, transform);
|
||||
}
|
||||
|
||||
template<typename NewType>
|
||||
struct AppendType {
|
||||
typedef FunctionInterface<
|
||||
typename boost::function_types::function_type<
|
||||
typename boost::mpl::push_back<
|
||||
boost::function_types::components<FunctionSignature>,
|
||||
NewType
|
||||
>::type
|
||||
>::type
|
||||
> type;
|
||||
};
|
||||
|
||||
/// Returns a new \c FunctionInterface with all the parameters of this \c
|
||||
/// FunctionInterface and the given method argument appended to these
|
||||
/// parameters. The return type can be determined with the \c AppendType
|
||||
/// template.
|
||||
///
|
||||
template<typename NewType>
|
||||
VTKM_EXEC_CONT_EXPORT
|
||||
typename AppendType<NewType>::type
|
||||
Append(NewType newParameter) const {
|
||||
typename AppendType<NewType>::type appendedFuncInterface;
|
||||
appendedFuncInterface.Copy(*this);
|
||||
appendedFuncInterface.template SetParameter<ARITY+1>(newParameter);
|
||||
return appendedFuncInterface;
|
||||
}
|
||||
|
||||
template<int ParameterIndex, typename NewType>
|
||||
class ReplaceType {
|
||||
typedef boost::function_types::components<FunctionSignature> ThisFunctionComponents;
|
||||
typedef typename boost::mpl::advance_c<typename boost::mpl::begin<ThisFunctionComponents>::type, ParameterIndex>::type ToRemovePos;
|
||||
typedef typename boost::mpl::erase<ThisFunctionComponents, ToRemovePos>::type ComponentRemoved;
|
||||
typedef typename boost::mpl::advance_c<typename boost::mpl::begin<ComponentRemoved>::type, ParameterIndex>::type ToInsertPos;
|
||||
typedef typename boost::mpl::insert<ComponentRemoved, ToInsertPos, NewType>::type ComponentInserted;
|
||||
typedef typename boost::function_types::function_type<ComponentInserted>::type NewSignature;
|
||||
public:
|
||||
typedef FunctionInterface<NewSignature> type;
|
||||
};
|
||||
|
||||
/// Returns a new \c FunctionInterface with all the parameters of this \c
|
||||
/// FunctionInterface except that the parameter indexed at the template
|
||||
/// parameter \c ParameterIndex is replaced with the given argument. This
|
||||
/// method can be used in place of SetParameter when the parameter type
|
||||
/// changes. The return type can be determined with the \c ReplaceType
|
||||
/// template.
|
||||
///
|
||||
template<int ParameterIndex, typename NewType>
|
||||
VTKM_EXEC_CONT_EXPORT
|
||||
typename ReplaceType<ParameterIndex, NewType>::type
|
||||
Replace(NewType newParameter) const {
|
||||
typename ReplaceType<ParameterIndex, NewType>::type replacedFuncInterface;
|
||||
detail::FunctionInterfaceCopyParameters<ParameterIndex-1>::
|
||||
Copy(replacedFuncInterface.Parameters, this->Parameters);
|
||||
replacedFuncInterface.SetParameter<ParameterIndex>(newParameter);
|
||||
detail::FunctionInterfaceCopyParameters<ARITY-ParameterIndex,ParameterIndex+1>::
|
||||
Copy(replacedFuncInterface.Parameters, this->Parameters);
|
||||
return replacedFuncInterface;
|
||||
}
|
||||
|
||||
/// \brief Transforms the \c FunctionInterface based on run-time information.
|
||||
///
|
||||
/// The \c DynamicTransform method transforms all the parameters of this \c
|
||||
/// FunctionInterface to different types and values based on run-time
|
||||
/// information. It operates by accepting two functors. The first functor
|
||||
/// accepts two arguments. The first argument is a parameter to transform and
|
||||
/// the second is a functor to call with the transformed result.
|
||||
///
|
||||
/// The second argument to \c DynamicTransform is another function that
|
||||
/// accepts the transformed \c FunctionInterface and does something. If that
|
||||
/// transformed \c FunctionInterface has a return value, that return value
|
||||
/// will be passed back to this \c FunctionInterface.
|
||||
///
|
||||
/// Here is a contrived but illustrative example. This transformation will
|
||||
/// pass all arguments except any string that looks like a number will be
|
||||
/// converted to a vtkm::Scalar. Note that because the types are not
|
||||
/// determined till runtime, this transform cannot be determined at compile
|
||||
/// time with meta-template programming.
|
||||
///
|
||||
/// \code
|
||||
/// struct MyTransformFunctor {
|
||||
/// template<typename InputType, typename ContinueFunctor>
|
||||
/// VTKM_CONT_EXPORT
|
||||
/// void operator()(const InputType &input,
|
||||
/// const ContinueFunctor &continue) const
|
||||
/// {
|
||||
/// continue(input);
|
||||
/// }
|
||||
///
|
||||
/// template<typename ContinueFunctor>
|
||||
/// VTKM_CONT_EXPORT
|
||||
/// void operator()(const std::string &input,
|
||||
/// const ContinueFunctor &continueFunc) const
|
||||
/// {
|
||||
/// if ((input[0] >= '0' && (input[0] <= '9'))
|
||||
/// {
|
||||
/// std::stringstream stream(input);
|
||||
/// vtkm::Scalar value;
|
||||
/// stream >> value;
|
||||
/// continueFunc(value);
|
||||
/// }
|
||||
/// else
|
||||
/// {
|
||||
/// continueFunc(input);
|
||||
/// }
|
||||
/// }
|
||||
/// };
|
||||
///
|
||||
/// struct MyFinishFunctor {
|
||||
/// template<typename FunctionSignature>
|
||||
/// VTKM_CONT_EXPORT
|
||||
/// void operator()(vtkm::internal::FunctionInterface<FunctionSignature> &funcInterface) const
|
||||
/// {
|
||||
/// // Do something
|
||||
/// }
|
||||
/// };
|
||||
///
|
||||
/// template<typename FunctionSignature>
|
||||
/// void ImportantStuff(vtkm::internal::FunctionInterface<FunctionSignature> &funcInterface)
|
||||
/// {
|
||||
/// funcInterface.DynamicTransformCont(MyContinueFunctor(), MyFinishFunctor());
|
||||
/// }
|
||||
/// \endcode
|
||||
///
|
||||
/// An interesting feature of \c DynamicTransform is that there does not have
|
||||
/// to be a one-to-one transform. It is possible to make many valid
|
||||
/// transforms by calling the continue functor multiple times within the
|
||||
/// transform functor. It is also possible to abort the transform by not
|
||||
/// calling the continue functor.
|
||||
///
|
||||
template<typename TransformFunctor, typename FinishFunctor>
|
||||
VTKM_CONT_EXPORT
|
||||
void
|
||||
DynamicTransformCont(const TransformFunctor &transform,
|
||||
const FinishFunctor &finish) {
|
||||
typedef detail::FunctionInterfaceDynamicTransformContContinue<
|
||||
FunctionSignature,
|
||||
ResultType(),
|
||||
TransformFunctor,
|
||||
FinishFunctor> ContinueFunctorType;
|
||||
|
||||
FunctionInterface<ResultType()> emptyInterface;
|
||||
ContinueFunctorType continueFunctor =
|
||||
ContinueFunctorType(*this, emptyInterface, transform, finish);
|
||||
|
||||
continueFunctor.DoNextTransform(emptyInterface);
|
||||
this->Result = emptyInterface.GetReturnValueSafe();
|
||||
}
|
||||
|
||||
private:
|
||||
vtkm::internal::FunctionInterfaceReturnContainer<ResultType> Result;
|
||||
detail::ParameterContainer<FunctionSignature> Parameters;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename OriginalFunction,
|
||||
typename NewFunction,
|
||||
typename TransformFunctor,
|
||||
typename FinishFunctor>
|
||||
class FunctionInterfaceDynamicTransformContContinue
|
||||
{
|
||||
public:
|
||||
FunctionInterfaceDynamicTransformContContinue(
|
||||
vtkm::internal::FunctionInterface<OriginalFunction> &originalInterface,
|
||||
vtkm::internal::FunctionInterface<NewFunction> &newInterface,
|
||||
const TransformFunctor &transform,
|
||||
const FinishFunctor &finish)
|
||||
: OriginalInterface(originalInterface),
|
||||
NewInterface(newInterface),
|
||||
Transform(transform),
|
||||
Finish(finish)
|
||||
{ }
|
||||
|
||||
template<typename T>
|
||||
VTKM_CONT_EXPORT
|
||||
void operator()(T newParameter) const
|
||||
{
|
||||
typedef typename vtkm::internal::FunctionInterface<NewFunction>::template AppendType<T>::type
|
||||
NextInterfaceType;
|
||||
NextInterfaceType nextInterface = this->NewInterface.Append(newParameter);
|
||||
this->DoNextTransform(nextInterface);
|
||||
this->NewInterface.GetReturnValueSafe()
|
||||
= nextInterface.GetReturnValueSafe();
|
||||
}
|
||||
|
||||
template<typename NextFunction>
|
||||
VTKM_CONT_EXPORT
|
||||
typename boost::enable_if_c<
|
||||
vtkm::internal::FunctionInterface<NextFunction>::ARITY
|
||||
< vtkm::internal::FunctionInterface<OriginalFunction>::ARITY>::type
|
||||
DoNextTransform(
|
||||
vtkm::internal::FunctionInterface<NextFunction> &nextInterface) const
|
||||
{
|
||||
typedef FunctionInterfaceDynamicTransformContContinue<
|
||||
OriginalFunction,NextFunction,TransformFunctor,FinishFunctor> NextContinueType;
|
||||
NextContinueType nextContinue = NextContinueType(this->OriginalInterface,
|
||||
nextInterface,
|
||||
this->Transform,
|
||||
this->Finish);
|
||||
this->Transform(this->OriginalInterface.template GetParameter<vtkm::internal::FunctionInterface<NextFunction>::ARITY + 1>(),
|
||||
nextContinue);
|
||||
}
|
||||
|
||||
template<typename NextFunction>
|
||||
VTKM_CONT_EXPORT
|
||||
typename boost::disable_if_c<
|
||||
vtkm::internal::FunctionInterface<NextFunction>::ARITY
|
||||
< vtkm::internal::FunctionInterface<OriginalFunction>::ARITY>::type
|
||||
DoNextTransform(
|
||||
vtkm::internal::FunctionInterface<NextFunction> &nextInterface) const
|
||||
{
|
||||
this->Finish(nextInterface);
|
||||
}
|
||||
|
||||
private:
|
||||
vtkm::internal::FunctionInterface<OriginalFunction> &OriginalInterface;
|
||||
vtkm::internal::FunctionInterface<NewFunction> &NewInterface;
|
||||
const TransformFunctor &Transform;
|
||||
const FinishFunctor &Finish;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
// The following code uses the Boost preprocessor utilities to create
|
||||
// definitions of make_FunctionInterface for all supported number of arguments.
|
||||
// The created functions are conceptually defined as follows:
|
||||
//
|
||||
// template<typename P0, // Return type
|
||||
// typename P1,
|
||||
// typename P2, ...>
|
||||
// VTKM_EXEC_CONT_EXPORT
|
||||
// FunctionInterface<P0(P1,P2,...)>
|
||||
// make_FunctionInterface(P1 p1, P2 p2,...) {
|
||||
// FunctionInterface<P0(P1,P2,...)> fi;
|
||||
// fi.template SetParameters<1>(p1);
|
||||
// fi.template SetParameters<2>(p2);
|
||||
// ...
|
||||
// return fi;
|
||||
// }
|
||||
|
||||
#define VTK_M_SET_PARAMETER(z, ParamIndex, data) \
|
||||
BOOST_PP_IF( \
|
||||
ParamIndex, \
|
||||
fi.template SetParameter<ParamIndex>(BOOST_PP_CAT(p, ParamIndex));,)
|
||||
|
||||
#define VTK_M_MAKE_FUNCTION_INTERFACE(NumParamsPlusOne) \
|
||||
template<BOOST_PP_ENUM_PARAMS(NumParamsPlusOne, typename P)> \
|
||||
VTKM_EXEC_CONT_EXPORT \
|
||||
FunctionInterface<P0(BOOST_PP_ENUM_SHIFTED_PARAMS(NumParamsPlusOne, P))> \
|
||||
make_FunctionInterface( \
|
||||
BOOST_PP_ENUM_SHIFTED_BINARY_PARAMS(NumParamsPlusOne, P, p)) \
|
||||
{ \
|
||||
FunctionInterface<P0(BOOST_PP_ENUM_SHIFTED_PARAMS(NumParamsPlusOne, P))> fi; \
|
||||
BOOST_PP_REPEAT(NumParamsPlusOne, VTK_M_SET_PARAMETER,) \
|
||||
return fi; \
|
||||
}
|
||||
|
||||
#define VTK_M_MAKE_FUNCITON_INTERFACE_REPEAT(z, NumParams, data) \
|
||||
VTK_M_MAKE_FUNCTION_INTERFACE(BOOST_PP_INC(NumParams))
|
||||
BOOST_PP_REPEAT(BOOST_PP_INC(VTKM_MAX_FUNCTION_PARAMETERS),
|
||||
VTK_M_MAKE_FUNCITON_INTERFACE_REPEAT,)
|
||||
|
||||
#undef VTK_M_MAKE_FUNCITON_INTERFACE_REPEAT
|
||||
#undef VTK_M_MAKE_FUNCTION_INTERFACE
|
||||
#undef VTK_M_SET_PARAMETER
|
||||
|
||||
}
|
||||
} // namespace vtkm::internal
|
||||
|
||||
#endif //vtk_m_cont_internal_FunctionInterface_h
|
@ -21,5 +21,6 @@
|
||||
set(unit_tests
|
||||
UnitTestConfigureFor32.cxx
|
||||
UnitTestConfigureFor64.cxx
|
||||
UnitTestFunctionInterface.cxx
|
||||
)
|
||||
vtkm_unit_tests(SOURCES ${unit_tests})
|
||||
|
499
vtkm/internal/testing/UnitTestFunctionInterface.cxx
Normal file
499
vtkm/internal/testing/UnitTestFunctionInterface.cxx
Normal file
@ -0,0 +1,499 @@
|
||||
//============================================================================
|
||||
// Copyright (c) Kitware, Inc.
|
||||
// All rights reserved.
|
||||
// See LICENSE.txt for details.
|
||||
// This software is distributed WITHOUT ANY WARRANTY; without even
|
||||
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
// PURPOSE. See the above copyright notice for more information.
|
||||
//
|
||||
// Copyright 2014 Sandia Corporation.
|
||||
// Copyright 2014 UT-Battelle, LLC.
|
||||
// Copyright 2014. Los Alamos National Security
|
||||
//
|
||||
// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
|
||||
// the U.S. Government retains certain rights in this software.
|
||||
//
|
||||
// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National
|
||||
// Laboratory (LANL), the U.S. Government retains certain rights in
|
||||
// this software.
|
||||
//============================================================================
|
||||
|
||||
#include <vtkm/internal/FunctionInterface.h>
|
||||
|
||||
#include <vtkm/testing/Testing.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <limits.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
// TODO: Once device adapters are implemented and contain timers, this class
|
||||
// should be removed and replaced with that. Also remove the inclusion of
|
||||
// limits.h, sys/time.h, and unistd.h.
|
||||
class Timer
|
||||
{
|
||||
public:
|
||||
VTKM_CONT_EXPORT Timer()
|
||||
{
|
||||
this->Reset();
|
||||
}
|
||||
|
||||
VTKM_CONT_EXPORT void Reset()
|
||||
{
|
||||
this->StartTime = this->GetCurrentTime();
|
||||
}
|
||||
|
||||
VTKM_CONT_EXPORT vtkm::Scalar GetElapsedTime()
|
||||
{
|
||||
TimeStamp currentTime = this->GetCurrentTime();
|
||||
|
||||
vtkm::Scalar elapsedTime;
|
||||
elapsedTime = currentTime.Seconds - this->StartTime.Seconds;
|
||||
elapsedTime += ((currentTime.Microseconds - this->StartTime.Microseconds)
|
||||
/vtkm::Scalar(1000000));
|
||||
|
||||
return elapsedTime;
|
||||
}
|
||||
|
||||
private:
|
||||
struct TimeStamp {
|
||||
vtkm::internal::Int64Type Seconds;
|
||||
vtkm::internal::Int64Type Microseconds;
|
||||
};
|
||||
TimeStamp StartTime;
|
||||
|
||||
VTKM_CONT_EXPORT
|
||||
TimeStamp GetCurrentTime()
|
||||
{
|
||||
TimeStamp retval;
|
||||
#ifdef _WIN32
|
||||
timeb currentTime;
|
||||
::ftime(¤tTime);
|
||||
retval.Seconds = currentTime.time;
|
||||
retval.Microseconds = 1000*currentTime.millitm;
|
||||
#else
|
||||
timeval currentTime;
|
||||
gettimeofday(¤tTime, NULL);
|
||||
retval.Seconds = currentTime.tv_sec;
|
||||
retval.Microseconds = currentTime.tv_usec;
|
||||
#endif
|
||||
return retval;
|
||||
}
|
||||
};
|
||||
|
||||
typedef vtkm::Id Type1;
|
||||
const Type1 Arg1 = 1234;
|
||||
|
||||
typedef vtkm::Scalar Type2;
|
||||
const Type2 Arg2 = 5678.125;
|
||||
|
||||
typedef std::string Type3;
|
||||
const Type3 Arg3("Third argument");
|
||||
|
||||
typedef vtkm::Vector3 Type4;
|
||||
const Type4 Arg4(1.2, 3.4, 5.6);
|
||||
|
||||
typedef vtkm::Id3 Type5;
|
||||
const Type5 Arg5(4, 5, 6);
|
||||
|
||||
struct ThreeArgFunctor {
|
||||
void operator()(const Type1 &a1, const Type2 &a2, const Type3 &a3) const
|
||||
{
|
||||
std::cout << "In 3 arg functor." << std::endl;
|
||||
|
||||
VTKM_TEST_ASSERT(a1 == Arg1, "Arg 1 incorrect.");
|
||||
VTKM_TEST_ASSERT(a2 == Arg2, "Arg 2 incorrect.");
|
||||
VTKM_TEST_ASSERT(a3 == Arg3, "Arg 3 incorrect.");
|
||||
}
|
||||
};
|
||||
|
||||
struct ThreeArgModifyFunctor {
|
||||
void operator()(Type1 &a1, Type2 &a2, Type3 &a3) const
|
||||
{
|
||||
std::cout << "In 3 arg modify functor." << std::endl;
|
||||
|
||||
a1 = Arg1;
|
||||
a2 = Arg2;
|
||||
a3 = Arg3;
|
||||
}
|
||||
};
|
||||
|
||||
struct GetReferenceFunctor
|
||||
{
|
||||
template<typename T>
|
||||
struct ReturnType {
|
||||
typedef const typename boost::remove_reference<T>::type *type;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
const T *operator()(const T &x) const { return &x; }
|
||||
};
|
||||
|
||||
struct ThreePointerArgFunctor {
|
||||
void operator()(const Type1 *a1, const Type2 *a2, const Type3 *a3) const
|
||||
{
|
||||
std::cout << "In 3 arg functor." << std::endl;
|
||||
|
||||
VTKM_TEST_ASSERT(*a1 == Arg1, "Arg 1 incorrect.");
|
||||
VTKM_TEST_ASSERT(*a2 == Arg2, "Arg 2 incorrect.");
|
||||
VTKM_TEST_ASSERT(*a3 == Arg3, "Arg 3 incorrect.");
|
||||
}
|
||||
};
|
||||
|
||||
struct ThreeArgFunctorWithReturn {
|
||||
std::string operator()(const Type1 &a1,
|
||||
const Type2 &a2,
|
||||
const Type3 &a3) const
|
||||
{
|
||||
std::cout << "In 3 arg functor with return." << std::endl;
|
||||
|
||||
std::stringstream buffer;
|
||||
buffer.precision(10);
|
||||
buffer << a1 << " " << a2 << " " << a3;
|
||||
return buffer.str();
|
||||
}
|
||||
};
|
||||
|
||||
struct FiveArgFunctor {
|
||||
void operator()(const Type1 &a1,
|
||||
const Type2 &a2,
|
||||
const Type3 &a3,
|
||||
const Type4 &a4,
|
||||
const Type5 &a5) const
|
||||
{
|
||||
std::cout << "In 5 arg functor." << std::endl;
|
||||
|
||||
VTKM_TEST_ASSERT(a1 == Arg1, "Arg 1 incorrect.");
|
||||
VTKM_TEST_ASSERT(a2 == Arg2, "Arg 2 incorrect.");
|
||||
VTKM_TEST_ASSERT(a3 == Arg3, "Arg 3 incorrect.");
|
||||
VTKM_TEST_ASSERT(a4 == Arg4, "Arg 4 incorrect.");
|
||||
VTKM_TEST_ASSERT(a5 == Arg5, "Arg 5 incorrect.");
|
||||
}
|
||||
};
|
||||
|
||||
struct FiveArgSwizzledFunctor {
|
||||
void operator()(const Type5 &a5,
|
||||
const Type1 &a1,
|
||||
const Type3 &a3,
|
||||
const Type4 &a4,
|
||||
const Type2 &a2) const
|
||||
{
|
||||
std::cout << "In 5 arg functor." << std::endl;
|
||||
|
||||
VTKM_TEST_ASSERT(a1 == Arg1, "Arg 1 incorrect.");
|
||||
VTKM_TEST_ASSERT(a2 == Arg2, "Arg 2 incorrect.");
|
||||
VTKM_TEST_ASSERT(a3 == Arg3, "Arg 3 incorrect.");
|
||||
VTKM_TEST_ASSERT(a4 == Arg4, "Arg 4 incorrect.");
|
||||
VTKM_TEST_ASSERT(a5 == Arg5, "Arg 5 incorrect.");
|
||||
}
|
||||
};
|
||||
|
||||
struct LotsOfArgsFunctor {
|
||||
LotsOfArgsFunctor() : Field(0) { }
|
||||
|
||||
void operator()(vtkm::Scalar arg1,
|
||||
vtkm::Scalar arg2,
|
||||
vtkm::Scalar arg3,
|
||||
vtkm::Scalar arg4,
|
||||
vtkm::Scalar arg5,
|
||||
vtkm::Scalar arg6,
|
||||
vtkm::Scalar arg7,
|
||||
vtkm::Scalar arg8,
|
||||
vtkm::Scalar arg9,
|
||||
vtkm::Scalar arg10) {
|
||||
VTKM_TEST_ASSERT(arg1 == 1.0, "Got bad argument");
|
||||
VTKM_TEST_ASSERT(arg2 == 2.0, "Got bad argument");
|
||||
VTKM_TEST_ASSERT(arg3 == 3.0, "Got bad argument");
|
||||
VTKM_TEST_ASSERT(arg4 == 4.0, "Got bad argument");
|
||||
VTKM_TEST_ASSERT(arg5 == 5.0, "Got bad argument");
|
||||
VTKM_TEST_ASSERT(arg6 == 6.0, "Got bad argument");
|
||||
VTKM_TEST_ASSERT(arg7 == 7.0, "Got bad argument");
|
||||
VTKM_TEST_ASSERT(arg8 == 8.0, "Got bad argument");
|
||||
VTKM_TEST_ASSERT(arg9 == 9.0, "Got bad argument");
|
||||
VTKM_TEST_ASSERT(arg10 == 10.0, "Got bad argument");
|
||||
|
||||
this->Field +=
|
||||
arg1 + arg2 + arg3 + arg4 + arg5 + arg6 + arg7 + arg8 + arg9 + arg10;
|
||||
}
|
||||
vtkm::Scalar Field;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
std::string ToString(const T &value)
|
||||
{
|
||||
std::stringstream stream;
|
||||
stream.precision(10);
|
||||
stream << value;
|
||||
return stream.str();
|
||||
}
|
||||
std::string ToString(const std::string &value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
struct StringTransform
|
||||
{
|
||||
template<typename T>
|
||||
std::string operator()(const T &input) const { return ToString(input); }
|
||||
};
|
||||
|
||||
struct ThreeArgStringFunctorWithReturn
|
||||
{
|
||||
std::string operator()(std::string arg1,
|
||||
std::string arg2,
|
||||
std::string arg3) const
|
||||
{
|
||||
return arg1 + " " + arg2 + " " + arg3;
|
||||
}
|
||||
};
|
||||
|
||||
struct DynamicTransformFunctor
|
||||
{
|
||||
template<typename T, typename ContinueFunctor>
|
||||
void operator()(const T &input, const ContinueFunctor continueFunc) const
|
||||
{
|
||||
continueFunc(input);
|
||||
continueFunc(ToString(input));
|
||||
}
|
||||
|
||||
template<typename ContinueFunctor>
|
||||
void operator()(const std::string &input, const ContinueFunctor continueFunc) const
|
||||
{
|
||||
continueFunc(input);
|
||||
}
|
||||
};
|
||||
|
||||
vtkm::Id g_DynamicTransformFinishCalls;
|
||||
|
||||
struct DynamicTransformFinish
|
||||
{
|
||||
template<typename Signature>
|
||||
void operator()(vtkm::internal::FunctionInterface<Signature> &funcInterface) const
|
||||
{
|
||||
g_DynamicTransformFinishCalls++;
|
||||
VTKM_TEST_ASSERT(ToString(funcInterface.template GetParameter<1>()) == ToString(Arg1),
|
||||
"Arg 1 incorrect");
|
||||
VTKM_TEST_ASSERT(ToString(funcInterface.template GetParameter<2>()) == ToString(Arg2),
|
||||
"Arg 2 incorrect");
|
||||
VTKM_TEST_ASSERT(ToString(funcInterface.template GetParameter<3>()) == ToString(Arg3),
|
||||
"Arg 3 incorrect");
|
||||
}
|
||||
};
|
||||
|
||||
void TryFunctionInterface5(
|
||||
vtkm::internal::FunctionInterface<void(Type1,Type2,Type3,Type4,Type5)> funcInterface)
|
||||
{
|
||||
std::cout << "Checking 5 parameter function interface." << std::endl;
|
||||
VTKM_TEST_ASSERT(funcInterface.GetArity() == 5,
|
||||
"Got wrong number of parameters.");
|
||||
VTKM_TEST_ASSERT(funcInterface.GetParameter<1>() == Arg1, "Arg 1 incorrect.");
|
||||
VTKM_TEST_ASSERT(funcInterface.GetParameter<2>() == Arg2, "Arg 2 incorrect.");
|
||||
VTKM_TEST_ASSERT(funcInterface.GetParameter<3>() == Arg3, "Arg 3 incorrect.");
|
||||
VTKM_TEST_ASSERT(funcInterface.GetParameter<4>() == Arg4, "Arg 4 incorrect.");
|
||||
VTKM_TEST_ASSERT(funcInterface.GetParameter<5>() == Arg5, "Arg 5 incorrect.");
|
||||
|
||||
std::cout << "Checking invocation." << std::endl;
|
||||
funcInterface.InvokeCont(FiveArgFunctor());
|
||||
funcInterface.InvokeExec(FiveArgFunctor());
|
||||
|
||||
std::cout << "Swizzling parameters with replace." << std::endl;
|
||||
funcInterface.Replace<1>(Arg5)
|
||||
.Replace<2>(Arg1)
|
||||
.Replace<5>(Arg2)
|
||||
.InvokeCont(FiveArgSwizzledFunctor());
|
||||
}
|
||||
|
||||
void TestBasicFunctionInterface()
|
||||
{
|
||||
std::cout << "Creating basic function interface." << std::endl;
|
||||
vtkm::internal::FunctionInterface<void(Type1,Type2,Type3)> funcInterface =
|
||||
vtkm::internal::make_FunctionInterface<void>(Arg1, Arg2, Arg3);
|
||||
|
||||
std::cout << "Checking parameters." << std::endl;
|
||||
VTKM_TEST_ASSERT(funcInterface.GetArity() == 3,
|
||||
"Got wrong number of parameters.");
|
||||
VTKM_TEST_ASSERT(funcInterface.GetParameter<1>() == Arg1, "Arg 1 incorrect.");
|
||||
VTKM_TEST_ASSERT(funcInterface.GetParameter<2>() == Arg2, "Arg 2 incorrect.");
|
||||
VTKM_TEST_ASSERT(funcInterface.GetParameter<3>() == Arg3, "Arg 3 incorrect.");
|
||||
|
||||
std::cout << "Checking invocation." << std::endl;
|
||||
funcInterface.InvokeCont(ThreeArgFunctor());
|
||||
funcInterface.InvokeExec(ThreeArgFunctor());
|
||||
|
||||
std::cout << "Checking invocation with argument modification." << std::endl;
|
||||
funcInterface.SetParameter<1>(Type1());
|
||||
funcInterface.SetParameter<2>(Type2());
|
||||
funcInterface.SetParameter<3>(Type3());
|
||||
VTKM_TEST_ASSERT(funcInterface.GetParameter<1>() != Arg1, "Arg 1 not cleared.");
|
||||
VTKM_TEST_ASSERT(funcInterface.GetParameter<2>() != Arg2, "Arg 2 not cleared.");
|
||||
VTKM_TEST_ASSERT(funcInterface.GetParameter<3>() != Arg3, "Arg 3 not cleared.");
|
||||
|
||||
funcInterface.InvokeCont(ThreeArgModifyFunctor());
|
||||
VTKM_TEST_ASSERT(funcInterface.GetParameter<1>() == Arg1, "Arg 1 incorrect.");
|
||||
VTKM_TEST_ASSERT(funcInterface.GetParameter<2>() == Arg2, "Arg 2 incorrect.");
|
||||
VTKM_TEST_ASSERT(funcInterface.GetParameter<3>() == Arg3, "Arg 3 incorrect.");
|
||||
|
||||
TryFunctionInterface5(
|
||||
vtkm::internal::make_FunctionInterface<void>(Arg1,Arg2,Arg3,Arg4,Arg5));
|
||||
}
|
||||
|
||||
void TestInvokeResult()
|
||||
{
|
||||
std::cout << "Checking invocation with return." << std::endl;
|
||||
vtkm::internal::FunctionInterface<std::string(Type1,Type2,Type3)> funcInterface
|
||||
= vtkm::internal::make_FunctionInterface<std::string>(Arg1, Arg2, Arg3);
|
||||
|
||||
funcInterface.InvokeCont(ThreeArgFunctorWithReturn());
|
||||
std::string result = funcInterface.GetReturnValue();
|
||||
|
||||
std::cout << "Got result: " << result << std::endl;
|
||||
VTKM_TEST_ASSERT(result == "1234 5678.125 Third argument",
|
||||
"Got bad result from invoke.");
|
||||
}
|
||||
|
||||
void TestAppend()
|
||||
{
|
||||
std::cout << "Appending interface with return value." << std::endl;
|
||||
vtkm::internal::FunctionInterface<std::string(Type1,Type2)>
|
||||
funcInterface2ArgWRet =
|
||||
vtkm::internal::make_FunctionInterface<std::string>(Arg1,Arg2);
|
||||
|
||||
vtkm::internal::FunctionInterface<std::string(Type1,Type2,Type3)>
|
||||
funcInterface3ArgWRet = funcInterface2ArgWRet.Append(Arg3);
|
||||
VTKM_TEST_ASSERT(funcInterface3ArgWRet.GetParameter<1>() == Arg1, "Arg 1 incorrect.");
|
||||
VTKM_TEST_ASSERT(funcInterface3ArgWRet.GetParameter<2>() == Arg2, "Arg 2 incorrect.");
|
||||
VTKM_TEST_ASSERT(funcInterface3ArgWRet.GetParameter<3>() == Arg3, "Arg 3 incorrect.");
|
||||
|
||||
std::cout << "Invoking appended function interface." << std::endl;
|
||||
funcInterface3ArgWRet.InvokeExec(ThreeArgFunctorWithReturn());
|
||||
std::string result = funcInterface3ArgWRet.GetReturnValue();
|
||||
|
||||
std::cout << "Got result: " << result << std::endl;
|
||||
VTKM_TEST_ASSERT(result == "1234 5678.125 Third argument",
|
||||
"Got bad result from invoke.");
|
||||
|
||||
std::cout << "Appending another value." << std::endl;
|
||||
vtkm::internal::FunctionInterface<std::string(Type1,Type2,Type3,Type4)>
|
||||
funcInterface4ArgWRet = funcInterface3ArgWRet.Append(Arg4);
|
||||
VTKM_TEST_ASSERT(funcInterface4ArgWRet.GetParameter<4>() == Arg4, "Arg 4 incorrect.");
|
||||
VTKM_TEST_ASSERT(funcInterface4ArgWRet.GetReturnValue() == "1234 5678.125 Third argument",
|
||||
"Got bad result from copy.");
|
||||
|
||||
std::cout << "Checking double append." << std::endl;
|
||||
vtkm::internal::FunctionInterface<void(Type1,Type2,Type3)> funcInterface3 =
|
||||
vtkm::internal::make_FunctionInterface<void>(Arg1,Arg2,Arg3);
|
||||
TryFunctionInterface5(funcInterface3
|
||||
.Append(Arg4)
|
||||
.Append(Arg5));
|
||||
}
|
||||
|
||||
void TestTransformInvoke()
|
||||
{
|
||||
std::cout << "Trying transform invoke." << std::endl;
|
||||
vtkm::internal::FunctionInterface<std::string(Type1,Type2,Type3)>
|
||||
funcInterface =
|
||||
vtkm::internal::make_FunctionInterface<std::string>(Arg1, Arg2, Arg3);
|
||||
|
||||
funcInterface.InvokeCont(ThreeArgStringFunctorWithReturn(),
|
||||
StringTransform());
|
||||
std::string result = funcInterface.GetReturnValue();
|
||||
|
||||
std::cout << "Got result: " << result << std::endl;
|
||||
VTKM_TEST_ASSERT(result == "1234 5678.125 Third argument",
|
||||
"Got bad result from invoke.");
|
||||
}
|
||||
|
||||
void TestDynamicTransform()
|
||||
{
|
||||
std::cout << "Trying dynamic transform." << std::endl;
|
||||
vtkm::internal::FunctionInterface<void(Type1,Type2,Type3)> funcInterface =
|
||||
vtkm::internal::make_FunctionInterface<void>(Arg1, Arg2, Arg3);
|
||||
|
||||
g_DynamicTransformFinishCalls = 0;
|
||||
|
||||
funcInterface.DynamicTransformCont(DynamicTransformFunctor(),
|
||||
DynamicTransformFinish());
|
||||
|
||||
// We use an idiosyncrasy of DynamicTransform to call finish with two
|
||||
// permutations for every non string argument and one permutation for every
|
||||
// string argument. Thus, we expect it to be called 4 times.
|
||||
std::cout << "Number of finish calls: " << g_DynamicTransformFinishCalls
|
||||
<< std::endl;
|
||||
VTKM_TEST_ASSERT(g_DynamicTransformFinishCalls == 4,
|
||||
"DynamicTransform did not call finish the right number of times.");
|
||||
}
|
||||
|
||||
void TestInvokeTime()
|
||||
{
|
||||
std::cout << "Checking time to call lots of args lots of times." << std::endl;
|
||||
static vtkm::Id NUM_TRIALS = 50000;
|
||||
LotsOfArgsFunctor f;
|
||||
|
||||
Timer timer;
|
||||
for (vtkm::Id trial = 0; trial < NUM_TRIALS; trial++)
|
||||
{
|
||||
f(1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f);
|
||||
}
|
||||
vtkm::Scalar directCallTime = timer.GetElapsedTime();
|
||||
std::cout << "Time for direct call: " << directCallTime << " seconds"
|
||||
<< std::endl;
|
||||
|
||||
vtkm::internal::FunctionInterface<void(vtkm::Scalar,
|
||||
vtkm::Scalar,
|
||||
vtkm::Scalar,
|
||||
vtkm::Scalar,
|
||||
vtkm::Scalar,
|
||||
vtkm::Scalar,
|
||||
vtkm::Scalar,
|
||||
vtkm::Scalar,
|
||||
vtkm::Scalar,
|
||||
vtkm::Scalar)> funcInterface =
|
||||
vtkm::internal::make_FunctionInterface<void>(vtkm::Scalar(1),
|
||||
vtkm::Scalar(2),
|
||||
vtkm::Scalar(3),
|
||||
vtkm::Scalar(4),
|
||||
vtkm::Scalar(5),
|
||||
vtkm::Scalar(6),
|
||||
vtkm::Scalar(7),
|
||||
vtkm::Scalar(8),
|
||||
vtkm::Scalar(9),
|
||||
vtkm::Scalar(10));
|
||||
|
||||
timer.Reset();
|
||||
for (vtkm::Id trial = 0; trial < NUM_TRIALS; trial++)
|
||||
{
|
||||
funcInterface.InvokeCont(f);
|
||||
}
|
||||
vtkm::Scalar invokeCallTime = timer.GetElapsedTime();
|
||||
std::cout << "Time for invoking function interface: " << invokeCallTime
|
||||
<< " seconds" << std::endl;
|
||||
std::cout << "Pointless result (makeing sure compiler computes it) "
|
||||
<< f.Field << std::endl;
|
||||
|
||||
// Might need to disable this for non-release builds.
|
||||
VTKM_TEST_ASSERT(invokeCallTime < 1.05*directCallTime,
|
||||
"Function interface invoke took longer than expected.");
|
||||
}
|
||||
|
||||
void TestFunctionInterface()
|
||||
{
|
||||
TestBasicFunctionInterface();
|
||||
TestInvokeResult();
|
||||
TestAppend();
|
||||
TestTransformInvoke();
|
||||
TestDynamicTransform();
|
||||
TestInvokeTime();
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
int UnitTestFunctionInterface(int, char *[])
|
||||
{
|
||||
return vtkm::testing::Testing::Run(TestFunctionInterface);
|
||||
}
|
Loading…
Reference in New Issue
Block a user