From ebc20b432da61e8cad0b6e5e2e56b4faa066a8ef Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Thu, 10 Apr 2014 13:35:06 -0600 Subject: [PATCH 1/4] 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. --- vtkm/internal/CMakeLists.txt | 1 + vtkm/internal/FunctionInterface.h | 770 ++++++++++++++++++ vtkm/internal/testing/CMakeLists.txt | 1 + .../testing/UnitTestFunctionInterface.cxx | 499 ++++++++++++ 4 files changed, 1271 insertions(+) create mode 100644 vtkm/internal/FunctionInterface.h create mode 100644 vtkm/internal/testing/UnitTestFunctionInterface.cxx diff --git a/vtkm/internal/CMakeLists.txt b/vtkm/internal/CMakeLists.txt index 97cefb578..c366ddeb1 100644 --- a/vtkm/internal/CMakeLists.txt +++ b/vtkm/internal/CMakeLists.txt @@ -22,6 +22,7 @@ set(headers ConfigureFor32.h ConfigureFor64.h ExportMacros.h + FunctionInterface.h ) vtkm_declare_headers(${headers}) diff --git a/vtkm/internal/FunctionInterface.h b/vtkm/internal/FunctionInterface.h new file mode 100644 index 000000000..62e36b64f --- /dev/null +++ b/vtkm/internal/FunctionInterface.h @@ -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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +struct FunctionInterfaceReturnContainer { + T Value; + static const bool VALID = true; +}; + +template<> +struct FunctionInterfaceReturnContainer { + // 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 +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 +// struct ParameterContainer { +// 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 \ + struct ParameterContainer \ + { \ + 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 +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 +// struct ParameterContainerAccess +// { +// VTKM_EXEC_CONT_EXPORT +// static ParameterType +// GetParameter(const ParameterContainer ¶meters); +// +// VTKM_EXEC_CONT_EXPORT +// static void SetParameter(ParameterContainer ¶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 \ + struct ParameterContainerAccess { \ + typedef typename boost::mpl::at_c< \ + boost::function_types::components, \ + ParameterIndex>::type ParameterType; \ + VTKM_EXEC_CONT_EXPORT \ + static \ + ParameterType \ + GetParameter(const ParameterContainer ¶meters) { \ + return parameters.BOOST_PP_CAT(Parameter, ParameterIndex); \ + } \ + VTKM_EXEC_CONT_EXPORT \ + static \ + void SetParameter(ParameterContainer ¶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 +VTKM_EXEC_CONT_EXPORT +typename ParameterContainerAccess::ParameterType +GetParameter(const ParameterContainer ¶meters) { + return ParameterContainerAccess::GetParameter(parameters); +} + +template +VTKM_EXEC_CONT_EXPORT + +void SetParameter(ParameterContainer ¶meters, + const typename ParameterContainerAccess::ParameterType &value) { + return ParameterContainerAccess::SetParameter(parameters, value); +} + +struct IdentityFunctor { + template + VTKM_EXEC_CONT_EXPORT + T &operator()(T &x) const { return x; } + + template + 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 +// VTKM_CONT_EXPORT +// void DoInvokeCont(const Function &f, +// ParameterContainer ¶meters, +// FunctionInterfaceReturnContainer &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 \ + VTK_M_DO_INVOKE_EXPORT \ + void VTK_M_DO_INVOKE_NAME( \ + VTK_M_DO_INVOKE_FUNCTION_CONST Function &f, \ + ParameterContainer ¶meters, \ + FunctionInterfaceReturnContainer &result, \ + const TransformFunctor &transform) \ + { \ + (void)parameters; \ + result.Value = \ + transform( \ + f(BOOST_PP_ENUM_SHIFTED(NumParamsPlusOne, VTK_M_DO_INVOKE_TPARAM, ))); \ + } \ + \ + template \ + VTK_M_DO_INVOKE_EXPORT \ + void VTK_M_DO_INVOKE_NAME( \ + VTK_M_DO_INVOKE_FUNCTION_CONST Function &f, \ + ParameterContainer ¶meters, \ + FunctionInterfaceReturnContainer &, \ + 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 +struct FunctionInterfaceCopyParameters { + template + static + VTKM_EXEC_CONT_EXPORT + void Copy(vtkm::internal::detail::ParameterContainer &dest, + const vtkm::internal::detail::ParameterContainer &src) + { + vtkm::internal::detail::SetParameter( + dest,vtkm::internal::detail::GetParameter(src)); + FunctionInterfaceCopyParameters::Copy(dest, src); + } +}; + +template +struct FunctionInterfaceCopyParameters<0, ParameterIndex> { + template + static + VTKM_EXEC_CONT_EXPORT + void Copy(vtkm::internal::detail::ParameterContainer &, + const vtkm::internal::detail::ParameterContainer &) + { + // Nothing left to copy. + } +}; + +template +class FunctionInterfaceDynamicTransformContContinue; + +} // namespace detail + +template +class FunctionInterface +{ + template + friend class FunctionInterface; + +public: + typedef FunctionSignature Signature; + + typedef typename boost::function_types::result_type::type + ResultType; + template + struct ParameterType { + typedef typename boost::mpl::at_c< + boost::function_types::components, + ParameterIndex>::type type; + }; + static const bool RETURN_VALID = FunctionInterfaceReturnContainer::VALID; + + /// The number of parameters in this \c Function Interface. + /// + static const int ARITY = + boost::function_types::function_arity::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 &GetReturnValueSafe() const + { + return this->Result; + } + VTKM_EXEC_CONT_EXPORT + FunctionInterfaceReturnContainer &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 + /// void Foo(const vtkm::cont::internal::FunctionInterface &fInterface) + /// { + /// bar(fInterface.template GetParameter<1>()); + /// } + /// \endcode + /// + template + VTKM_EXEC_CONT_EXPORT + typename ParameterType::type + GetParameter() { + return detail::GetParameter(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 + VTKM_EXEC_CONT_EXPORT + void SetParameter(typename ParameterType::type parameter) + { + detail::SetParameter(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 + void Copy(const FunctionInterface &src) + { + this->Result = src.GetReturnValueSafe(); + detail::FunctionInterfaceCopyParameters< + boost::static_unsigned_min::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 + VTKM_CONT_EXPORT + void InvokeCont(const Function &f) { + detail::DoInvokeCont(f, + this->Parameters, + this->Result, + detail::IdentityFunctor()); + } + template + VTKM_CONT_EXPORT + void InvokeCont(Function &f) { + detail::DoInvokeCont(f, + this->Parameters, + this->Result, + detail::IdentityFunctor()); + } + template + VTKM_EXEC_EXPORT + void InvokeExec(const Function &f) { + detail::DoInvokeExec(f, + this->Parameters, + this->Result, + detail::IdentityFunctor()); + } + template + 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 + VTKM_CONT_EXPORT + void InvokeCont(const Function &f, const TransformFunctor &transform) { + detail::DoInvokeCont(f, this->Parameters, this->Result, transform); + } + template + VTKM_CONT_EXPORT + void InvokeCont(Function &f, const TransformFunctor &transform) { + detail::DoInvokeCont(f, this->Parameters, this->Result, transform); + } + template + VTKM_EXEC_EXPORT + void InvokeExec(const Function &f, const TransformFunctor &transform) { + detail::DoInvokeExec(f, this->Parameters, this->Result, transform); + } + template + VTKM_EXEC_EXPORT + void InvokeExec(Function &f, const TransformFunctor &transform) { + detail::DoInvokeExec(f, this->Parameters, this->Result, transform); + } + + template + struct AppendType { + typedef FunctionInterface< + typename boost::function_types::function_type< + typename boost::mpl::push_back< + boost::function_types::components, + 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 + VTKM_EXEC_CONT_EXPORT + typename AppendType::type + Append(NewType newParameter) const { + typename AppendType::type appendedFuncInterface; + appendedFuncInterface.Copy(*this); + appendedFuncInterface.template SetParameter(newParameter); + return appendedFuncInterface; + } + + template + class ReplaceType { + typedef boost::function_types::components ThisFunctionComponents; + typedef typename boost::mpl::advance_c::type, ParameterIndex>::type ToRemovePos; + typedef typename boost::mpl::erase::type ComponentRemoved; + typedef typename boost::mpl::advance_c::type, ParameterIndex>::type ToInsertPos; + typedef typename boost::mpl::insert::type ComponentInserted; + typedef typename boost::function_types::function_type::type NewSignature; + public: + typedef FunctionInterface 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 + VTKM_EXEC_CONT_EXPORT + typename ReplaceType::type + Replace(NewType newParameter) const { + typename ReplaceType::type replacedFuncInterface; + detail::FunctionInterfaceCopyParameters:: + Copy(replacedFuncInterface.Parameters, this->Parameters); + replacedFuncInterface.SetParameter(newParameter); + detail::FunctionInterfaceCopyParameters:: + 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 + /// VTKM_CONT_EXPORT + /// void operator()(const InputType &input, + /// const ContinueFunctor &continue) const + /// { + /// continue(input); + /// } + /// + /// template + /// 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 + /// VTKM_CONT_EXPORT + /// void operator()(vtkm::internal::FunctionInterface &funcInterface) const + /// { + /// // Do something + /// } + /// }; + /// + /// template + /// void ImportantStuff(vtkm::internal::FunctionInterface &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 + VTKM_CONT_EXPORT + void + DynamicTransformCont(const TransformFunctor &transform, + const FinishFunctor &finish) { + typedef detail::FunctionInterfaceDynamicTransformContContinue< + FunctionSignature, + ResultType(), + TransformFunctor, + FinishFunctor> ContinueFunctorType; + + FunctionInterface emptyInterface; + ContinueFunctorType continueFunctor = + ContinueFunctorType(*this, emptyInterface, transform, finish); + + continueFunctor.DoNextTransform(emptyInterface); + this->Result = emptyInterface.GetReturnValueSafe(); + } + +private: + vtkm::internal::FunctionInterfaceReturnContainer Result; + detail::ParameterContainer Parameters; +}; + +namespace detail { + +template +class FunctionInterfaceDynamicTransformContContinue +{ +public: + FunctionInterfaceDynamicTransformContContinue( + vtkm::internal::FunctionInterface &originalInterface, + vtkm::internal::FunctionInterface &newInterface, + const TransformFunctor &transform, + const FinishFunctor &finish) + : OriginalInterface(originalInterface), + NewInterface(newInterface), + Transform(transform), + Finish(finish) + { } + + template + VTKM_CONT_EXPORT + void operator()(T newParameter) const + { + typedef typename vtkm::internal::FunctionInterface::template AppendType::type + NextInterfaceType; + NextInterfaceType nextInterface = this->NewInterface.Append(newParameter); + this->DoNextTransform(nextInterface); + this->NewInterface.GetReturnValueSafe() + = nextInterface.GetReturnValueSafe(); + } + + template + VTKM_CONT_EXPORT + typename boost::enable_if_c< + vtkm::internal::FunctionInterface::ARITY + < vtkm::internal::FunctionInterface::ARITY>::type + DoNextTransform( + vtkm::internal::FunctionInterface &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::ARITY + 1>(), + nextContinue); + } + + template + VTKM_CONT_EXPORT + typename boost::disable_if_c< + vtkm::internal::FunctionInterface::ARITY + < vtkm::internal::FunctionInterface::ARITY>::type + DoNextTransform( + vtkm::internal::FunctionInterface &nextInterface) const + { + this->Finish(nextInterface); + } + +private: + vtkm::internal::FunctionInterface &OriginalInterface; + vtkm::internal::FunctionInterface &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 +// VTKM_EXEC_CONT_EXPORT +// FunctionInterface +// make_FunctionInterface(P1 p1, P2 p2,...) { +// FunctionInterface 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(BOOST_PP_CAT(p, ParamIndex));,) + +#define VTK_M_MAKE_FUNCTION_INTERFACE(NumParamsPlusOne) \ + template \ + VTKM_EXEC_CONT_EXPORT \ + FunctionInterface \ + make_FunctionInterface( \ + BOOST_PP_ENUM_SHIFTED_BINARY_PARAMS(NumParamsPlusOne, P, p)) \ + { \ + FunctionInterface 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 diff --git a/vtkm/internal/testing/CMakeLists.txt b/vtkm/internal/testing/CMakeLists.txt index 07b3d5e60..f747be3ee 100644 --- a/vtkm/internal/testing/CMakeLists.txt +++ b/vtkm/internal/testing/CMakeLists.txt @@ -21,5 +21,6 @@ set(unit_tests UnitTestConfigureFor32.cxx UnitTestConfigureFor64.cxx + UnitTestFunctionInterface.cxx ) vtkm_unit_tests(SOURCES ${unit_tests}) diff --git a/vtkm/internal/testing/UnitTestFunctionInterface.cxx b/vtkm/internal/testing/UnitTestFunctionInterface.cxx new file mode 100644 index 000000000..88cbf9108 --- /dev/null +++ b/vtkm/internal/testing/UnitTestFunctionInterface.cxx @@ -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 + +#include + +#include +#include + +#ifndef _WIN32 +#include +#include +#include +#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 + struct ReturnType { + typedef const typename boost::remove_reference::type *type; + }; + + template + 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 +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 + 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 + void operator()(const T &input, const ContinueFunctor continueFunc) const + { + continueFunc(input); + continueFunc(ToString(input)); + } + + template + void operator()(const std::string &input, const ContinueFunctor continueFunc) const + { + continueFunc(input); + } +}; + +vtkm::Id g_DynamicTransformFinishCalls; + +struct DynamicTransformFinish +{ + template + void operator()(vtkm::internal::FunctionInterface &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 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 funcInterface = + vtkm::internal::make_FunctionInterface(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(Arg1,Arg2,Arg3,Arg4,Arg5)); +} + +void TestInvokeResult() +{ + std::cout << "Checking invocation with return." << std::endl; + vtkm::internal::FunctionInterface funcInterface + = vtkm::internal::make_FunctionInterface(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 + funcInterface2ArgWRet = + vtkm::internal::make_FunctionInterface(Arg1,Arg2); + + vtkm::internal::FunctionInterface + 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 + 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 funcInterface3 = + vtkm::internal::make_FunctionInterface(Arg1,Arg2,Arg3); + TryFunctionInterface5(funcInterface3 + .Append(Arg4) + .Append(Arg5)); +} + +void TestTransformInvoke() +{ + std::cout << "Trying transform invoke." << std::endl; + vtkm::internal::FunctionInterface + funcInterface = + vtkm::internal::make_FunctionInterface(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 funcInterface = + vtkm::internal::make_FunctionInterface(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 funcInterface = + vtkm::internal::make_FunctionInterface(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); +} From defb4f00cf2e529a5cf10620e936bc18ae0e4de5 Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Wed, 30 Apr 2014 09:47:22 -0600 Subject: [PATCH 2/4] Fix CLANG errors and warnings for FunctionInterface. --- vtkm/internal/FunctionInterface.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/vtkm/internal/FunctionInterface.h b/vtkm/internal/FunctionInterface.h index 62e36b64f..665f0cf98 100644 --- a/vtkm/internal/FunctionInterface.h +++ b/vtkm/internal/FunctionInterface.h @@ -226,6 +226,7 @@ struct IdentityFunctor { const TransformFunctor &transform) \ { \ (void)parameters; \ + (void)transform; \ result.Value = \ transform( \ f(BOOST_PP_ENUM_SHIFTED(NumParamsPlusOne, VTK_M_DO_INVOKE_TPARAM, ))); \ @@ -242,6 +243,7 @@ struct IdentityFunctor { const TransformFunctor &transform) \ { \ (void)parameters; \ + (void)transform; \ f(BOOST_PP_ENUM_SHIFTED(NumParamsPlusOne, VTK_M_DO_INVOKE_TPARAM, )); \ } #define VTK_M_DO_INVOKE_REPEAT(z, NumParams, data) \ @@ -551,7 +553,7 @@ public: typename ReplaceType::type replacedFuncInterface; detail::FunctionInterfaceCopyParameters:: Copy(replacedFuncInterface.Parameters, this->Parameters); - replacedFuncInterface.SetParameter(newParameter); + replacedFuncInterface.template SetParameter(newParameter); detail::FunctionInterfaceCopyParameters:: Copy(replacedFuncInterface.Parameters, this->Parameters); return replacedFuncInterface; From 438fe0ff8de303088183240c901cf63a00e03e18 Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Tue, 6 May 2014 13:28:08 -0600 Subject: [PATCH 3/4] Implement FunctionInterface::StaticTransform This will allow a faster conversion than the dynamic transform and will allow you to define compile-time types for transformation unlike dynamic transform or invoke with transform. --- vtkm/internal/FunctionInterface.h | 178 +++++++++++++++++- .../testing/UnitTestFunctionInterface.cxx | 44 ++++- 2 files changed, 220 insertions(+), 2 deletions(-) diff --git a/vtkm/internal/FunctionInterface.h b/vtkm/internal/FunctionInterface.h index 665f0cf98..714e671d8 100644 --- a/vtkm/internal/FunctionInterface.h +++ b/vtkm/internal/FunctionInterface.h @@ -247,7 +247,7 @@ struct IdentityFunctor { 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)); + VTK_M_DO_INVOKE(BOOST_PP_INC(NumParams)) #define VTK_M_DO_INVOKE_NAME DoInvokeCont #define VTK_M_DO_INVOKE_EXPORT VTKM_CONT_EXPORT @@ -318,6 +318,69 @@ struct FunctionInterfaceCopyParameters<0, ParameterIndex> { } }; +template +struct FunctionInterfaceStaticTransformType; + +// The following code uses the Boost preprocessor utilities to create +// definitions of DoStaticTransform functions for all supported number of +// arguments. The created functions are conceptually defined as follows: +// +// template +// VTKM_CONT_EXPORT +// void DoStaticTransformCont( +// const Transform &transform, +// const ParameterContainer &originalParameters, +// ParameterContainer &transformedParameters) +// { +// transformedParameters.Parameter1 = transform(originalParameters.Parameter1); +// transformedParameters.Parameter2 = transform(originalParameters.Parameter2); +// ... +// } +// +// We define multiple DoStaticTransformCont and DoStaticTransformExec 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 transform to be invoked may only be viable in one or the other. + +#define VTK_M_DO_STATIC_TRANSFORM_ASSIGN(z, count, data) \ + BOOST_PP_IF(count, \ + BOOST_PP_CAT(transformedParameters.Parameter, count) = \ + transform(BOOST_PP_CAT(originalParameters.Parameter, count));,) + +#define VTK_M_DO_STATIC_TRANSFORM(NumParamsPlusOne) \ + template \ + VTK_M_DO_STATIC_TRANSFORM_EXPORT \ + void VTK_M_DO_STATIC_TRANSFORM_NAME( \ + const Transform &transform, \ + ParameterContainer &originalParameters, \ + ParameterContainer &transformedParameters) \ + { \ + (void)transform; \ + (void)originalParameters; \ + (void)transformedParameters; \ + BOOST_PP_REPEAT(NumParamsPlusOne, VTK_M_DO_STATIC_TRANSFORM_ASSIGN,) \ + } +#define VTK_M_DO_STATIC_TRANSFORM_REPEAT(z, NumParams, data) \ + VTK_M_DO_STATIC_TRANSFORM(BOOST_PP_INC(NumParams)) + +#define VTK_M_DO_STATIC_TRANSFORM_NAME DoStaticTransformCont +#define VTK_M_DO_STATIC_TRANSFORM_EXPORT VTKM_CONT_EXPORT +BOOST_PP_REPEAT(BOOST_PP_INC(VTKM_MAX_FUNCTION_PARAMETERS), + VTK_M_DO_STATIC_TRANSFORM_REPEAT,) +#undef VTK_M_DO_STATIC_TRANSFORM_EXPORT +#undef VTK_M_DO_STATIC_TRANSFORM_NAME + +#define VTK_M_DO_STATIC_TRANSFORM_NAME DoStaticTransformExec +#define VTK_M_DO_STATIC_TRANSFORM_EXPORT VTKM_EXEC_EXPORT +BOOST_PP_REPEAT(BOOST_PP_INC(VTKM_MAX_FUNCTION_PARAMETERS), + VTK_M_DO_STATIC_TRANSFORM_REPEAT,) +#undef VTK_M_DO_STATIC_TRANSFORM_EXPORT +#undef VTK_M_DO_STATIC_TRANSFORM_NAME + template + struct StaticTransformType { + typedef FunctionInterface< + typename detail::FunctionInterfaceStaticTransformType< + FunctionSignature,Transform>::type> type; + }; + + /// \brief Transforms the \c FunctionInterface based on compile-time + /// information. + /// + /// The \c StaticTransform method transforms all the parameters of this \c + /// FunctionInterface to different types and values based on compile-time + /// information. It operates by accepting a functor that defines a unary + /// function whose argument is the parameter to transform and the return + /// value is the transformed value. The functor must also contain a templated + /// struct name ReturnType with an internal type named \c type that defines + /// the return type of the transform for a given input type. + /// + /// The transformation is only applied to the parameters of the function. The + /// return argument is uneffected. + /// + /// The return type can be determined with the \c StaticTransformType + /// template. + /// + /// Here is an example of a transformation that converts a \c + /// FunctionInterface to another \c FunctionInterface containing pointers to + /// all of the parameters. + /// + /// \code + /// struct MyTransformFunctor { + /// template + /// struct ReturnType { + /// typedef const T *type; + /// }; + /// + /// template + /// DAX_CONT_EXPORT + /// const T *operator()(const T &x) const { + /// return &x; + /// } + /// }; + /// + /// template + /// typename vtkm::internal::FunctionInterface::template StaticTransformType::type + /// ImportantStuff(const vtkm::internal::FunctionInterface &funcInterface) + /// { + /// return funcInterface.StaticTransformCont(MyTransformFunctor()); + /// } + /// \endcode + /// + template + VTKM_CONT_EXPORT + typename StaticTransformType::type + StaticTransformCont(const Transform &transform) + { + typename StaticTransformType::type newFuncInterface; + detail::DoStaticTransformCont(transform, + this->Parameters, + newFuncInterface.Parameters); + return newFuncInterface; + } + template + VTKM_EXEC_EXPORT + typename StaticTransformType::type + StaticTransformExec(const Transform &transform) + { + typename StaticTransformType::type newFuncInterface; + detail::DoStaticTransformExec(transform, + this->Parameters, + newFuncInterface.Parameters); + return newFuncInterface; + } + /// \brief Transforms the \c FunctionInterface based on run-time information. /// /// The \c DynamicTransform method transforms all the parameters of this \c @@ -655,6 +791,46 @@ private: namespace detail { +// The following code uses the Boost preprocessor utilities to create +// definitions of FunctionInterfaceStaticTransformType for all supported number +// of arguments. The created classes are conceptually defined as follows: +// +// template +// struct FunctionInterfaceStaticTransformType { +// typedef P0(type)(typename Transform::template ReturnType::type, +// typename Transform::template ReturnType::type, ...); +// }; + +#define VTK_M_STATIC_TRANSFORM_TPARAM(z, ParamIndex, data) \ + BOOST_PP_IF( \ + ParamIndex, \ + typename Transform::template ReturnType::type,) + +#define VTK_M_STATIC_TRANSFORM_TYPE(NumParamsPlusOne) \ + template \ + struct FunctionInterfaceStaticTransformType< \ + P0(BOOST_PP_ENUM_SHIFTED_PARAMS(NumParamsPlusOne, P)), \ + Transform> \ + { \ + typedef P0(type)( \ + BOOST_PP_ENUM_SHIFTED(NumParamsPlusOne, VTK_M_STATIC_TRANSFORM_TPARAM,) \ + ); \ + }; +#define VTK_M_STATIC_TRANSFORM_TYPE_REPEAT(z, NumParams, data) \ + VTK_M_STATIC_TRANSFORM_TYPE(BOOST_PP_INC(NumParams)) + +BOOST_PP_REPEAT(BOOST_PP_INC(VTKM_MAX_FUNCTION_PARAMETERS), + VTK_M_STATIC_TRANSFORM_TYPE_REPEAT,) + +#undef VTK_M_STATIC_TRANSFORM_TYPE_REPEAT +#undef VTK_M_STATIC_TRANSFORM_TYPE +#undef VTK_M_STATIC_TRANSFORM_TPARAM + + template + struct ReturnType { + typedef const T *type; + }; + + template + 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; + std::cout << "In 3 point arg functor." << std::endl; VTKM_TEST_ASSERT(*a1 == Arg1, "Arg 1 incorrect."); VTKM_TEST_ASSERT(*a2 == Arg2, "Arg 2 incorrect."); @@ -409,6 +421,35 @@ void TestTransformInvoke() "Got bad result from invoke."); } +void TestStaticTransform() +{ + std::cout << "Trying static transform." << std::endl; + typedef vtkm::internal::FunctionInterface + OriginalType; + OriginalType funcInterface = + vtkm::internal::make_FunctionInterface(Arg1, Arg2, Arg3); + + std::cout << "Transform with reported type." << std::endl; + typedef OriginalType::StaticTransformType::type + ReportedType; + ReportedType funcInterfaceTransform1 = + funcInterface.StaticTransformCont(PointerTransform()); + funcInterfaceTransform1.InvokeCont(ThreePointerArgFunctor()); + funcInterfaceTransform1 = + funcInterface.StaticTransformExec(PointerTransform()); + funcInterfaceTransform1.InvokeExec(ThreePointerArgFunctor()); + + std::cout << "Transform with expected type." << std::endl; + typedef vtkm::internal::FunctionInterface + ExpectedType; + ReportedType funcInterfaceTransform2 = + funcInterface.StaticTransformCont(PointerTransform()); + funcInterfaceTransform2.InvokeCont(ThreePointerArgFunctor()); + funcInterfaceTransform2 = + funcInterface.StaticTransformExec(PointerTransform()); + funcInterfaceTransform2.InvokeExec(ThreePointerArgFunctor()); +} + void TestDynamicTransform() { std::cout << "Trying dynamic transform." << std::endl; @@ -487,6 +528,7 @@ void TestFunctionInterface() TestInvokeResult(); TestAppend(); TestTransformInvoke(); + TestStaticTransform(); TestDynamicTransform(); TestInvokeTime(); } From d3134b70509181f6f32f757004174a1dc0692867 Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Tue, 13 May 2014 13:18:21 -0600 Subject: [PATCH 4/4] Add ForEach methods to FunctionInterface. Provides a convienient mechanism to do an in-place type-preserving transform or a check of each parameter. --- vtkm/internal/FunctionInterface.h | 136 ++++++++++++++++-- .../testing/UnitTestFunctionInterface.cxx | 36 +++++ 2 files changed, 161 insertions(+), 11 deletions(-) diff --git a/vtkm/internal/FunctionInterface.h b/vtkm/internal/FunctionInterface.h index 714e671d8..02a5a8bb8 100644 --- a/vtkm/internal/FunctionInterface.h +++ b/vtkm/internal/FunctionInterface.h @@ -192,11 +192,15 @@ struct IdentityFunctor { // definitions of DoInvoke functions for all supported number of arguments. // The created functions are conceptually defined as follows: // -// template +// template // VTKM_CONT_EXPORT // void DoInvokeCont(const Function &f, -// ParameterContainer ¶meters, -// FunctionInterfaceReturnContainer &result, +// ParameterContainer ¶meters, +// FunctionInterfaceReturnContainer &result, // const TransformFunctor &transform) // { // result.Value = transform(f(transform(parameters.Parameter1),...)); @@ -356,7 +360,7 @@ struct FunctionInterfaceStaticTransformType; VTK_M_DO_STATIC_TRANSFORM_EXPORT \ void VTK_M_DO_STATIC_TRANSFORM_NAME( \ const Transform &transform, \ - ParameterContainer &originalParameters, \ + const ParameterContainer &originalParameters, \ ParameterContainer &transformedParameters) \ { \ (void)transform; \ @@ -381,12 +385,96 @@ BOOST_PP_REPEAT(BOOST_PP_INC(VTKM_MAX_FUNCTION_PARAMETERS), #undef VTK_M_DO_STATIC_TRANSFORM_EXPORT #undef VTK_M_DO_STATIC_TRANSFORM_NAME +#undef VTK_M_DO_STATIC_TRANSFORM_REPEAT +#undef VTK_M_DO_STATIC_TRANSFORM +#undef VTK_M_DO_STATIC_TRANSFORM_ASSIGN + template class FunctionInterfaceDynamicTransformContContinue; +// The following code uses the Boost preprocessor utilities to create +// definitions of DoForEach functions for all supported number of arguments. +// The created functions are conceptually defined as follows: +// +// template +// VTKM_CONT_EXPORT +// void DoForEachCont(const Functor &f, +// ParameterContainer ¶meters) +// +// { +// f(parameters.Parameter1); +// f(parameters.Parameter2); +// ... +// } +// +// We define multiple DoForEachCont and DoForEachExec 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 functor +// to be invoked on each parameter may only be viable in one or the other. +// There are also separate versions that support a const FunctionInterface and +// a non-const FunctionInterface. + +#define VTK_M_DO_FOR_EACH_CALL_PARAM(z, count, data) \ + BOOST_PP_IF(count, f(BOOST_PP_CAT(parameters.Parameter, count));,) + +#define VTK_M_DO_FOR_EACH(NumParamsPlusOne) \ + template \ + VTK_M_DO_FOR_EACH_EXPORT \ + void VTK_M_DO_FOR_EACH_NAME( \ + const Functor &f, \ + VTK_M_DO_FOR_EACH_FI_CONST ParameterContainer ¶meters) \ + { \ + (void)f; \ + (void)parameters; \ + BOOST_PP_REPEAT(NumParamsPlusOne, VTK_M_DO_FOR_EACH_CALL_PARAM,) \ + } +#define VTK_M_DO_FOR_EACH_REPEAT(z, NumParams, data) \ + VTK_M_DO_FOR_EACH(BOOST_PP_INC(NumParams)) + +#define VTK_M_DO_FOR_EACH_EXPORT VTKM_CONT_EXPORT +#define VTK_M_DO_FOR_EACH_NAME DoForEachCont +#define VTK_M_DO_FOR_EACH_FI_CONST const +BOOST_PP_REPEAT(BOOST_PP_INC(VTKM_MAX_FUNCTION_PARAMETERS), + VTK_M_DO_FOR_EACH_REPEAT,) +#undef VTK_M_DO_FOR_EACH_FI_CONST +#undef VTK_M_DO_FOR_EACH_NAME +#undef VTK_M_DO_FOR_EACH_EXPORT + +#define VTK_M_DO_FOR_EACH_EXPORT VTKM_CONT_EXPORT +#define VTK_M_DO_FOR_EACH_NAME DoForEachCont +#define VTK_M_DO_FOR_EACH_FI_CONST +BOOST_PP_REPEAT(BOOST_PP_INC(VTKM_MAX_FUNCTION_PARAMETERS), + VTK_M_DO_FOR_EACH_REPEAT,) +#undef VTK_M_DO_FOR_EACH_FI_CONST +#undef VTK_M_DO_FOR_EACH_NAME +#undef VTK_M_DO_FOR_EACH_EXPORT + +#define VTK_M_DO_FOR_EACH_EXPORT VTKM_EXEC_EXPORT +#define VTK_M_DO_FOR_EACH_NAME DoForEachExec +#define VTK_M_DO_FOR_EACH_FI_CONST const +BOOST_PP_REPEAT(BOOST_PP_INC(VTKM_MAX_FUNCTION_PARAMETERS), + VTK_M_DO_FOR_EACH_REPEAT,) +#undef VTK_M_DO_FOR_EACH_FI_CONST +#undef VTK_M_DO_FOR_EACH_NAME +#undef VTK_M_DO_FOR_EACH_EXPORT + +#define VTK_M_DO_FOR_EACH_EXPORT VTKM_EXEC_EXPORT +#define VTK_M_DO_FOR_EACH_NAME DoForEachExec +#define VTK_M_DO_FOR_EACH_FI_CONST +BOOST_PP_REPEAT(BOOST_PP_INC(VTKM_MAX_FUNCTION_PARAMETERS), + VTK_M_DO_FOR_EACH_REPEAT,) +#undef VTK_M_DO_FOR_EACH_FI_CONST +#undef VTK_M_DO_FOR_EACH_NAME +#undef VTK_M_DO_FOR_EACH_EXPORT + +#undef VTK_M_DO_FOR_EACH_REPEAT +#undef VTK_M_DO_FOR_EACH +#undef VTK_M_DO_FOR_EACH_CALL_PARAM + } // namespace detail template @@ -461,7 +549,7 @@ public: template VTKM_EXEC_CONT_EXPORT typename ParameterType::type - GetParameter() { + GetParameter() const { return detail::GetParameter(this->Parameters); } @@ -632,7 +720,7 @@ public: /// \brief Transforms the \c FunctionInterface based on compile-time /// information. /// - /// The \c StaticTransform method transforms all the parameters of this \c + /// The \c StaticTransform methods transform all the parameters of this \c /// FunctionInterface to different types and values based on compile-time /// information. It operates by accepting a functor that defines a unary /// function whose argument is the parameter to transform and the return @@ -675,7 +763,7 @@ public: template VTKM_CONT_EXPORT typename StaticTransformType::type - StaticTransformCont(const Transform &transform) + StaticTransformCont(const Transform &transform) const { typename StaticTransformType::type newFuncInterface; detail::DoStaticTransformCont(transform, @@ -686,7 +774,7 @@ public: template VTKM_EXEC_EXPORT typename StaticTransformType::type - StaticTransformExec(const Transform &transform) + StaticTransformExec(const Transform &transform) const { typename StaticTransformType::type newFuncInterface; detail::DoStaticTransformExec(transform, @@ -767,9 +855,8 @@ public: /// template VTKM_CONT_EXPORT - void - DynamicTransformCont(const TransformFunctor &transform, - const FinishFunctor &finish) { + void DynamicTransformCont(const TransformFunctor &transform, + const FinishFunctor &finish) { typedef detail::FunctionInterfaceDynamicTransformContContinue< FunctionSignature, ResultType(), @@ -784,6 +871,33 @@ public: this->Result = emptyInterface.GetReturnValueSafe(); } + /// \brief Applies a function to all the parameters. + /// + /// The \c ForEach methods take a function and apply that function to each + /// of the parameters in the \c FunctionInterface. (Return values are not + /// effected.) + /// + template + VTKM_CONT_EXPORT + void ForEachCont(const Functor &f) const { + detail::DoForEachCont(f, this->Parameters); + } + template + VTKM_CONT_EXPORT + void ForEachCont(const Functor &f) { + detail::DoForEachCont(f, this->Parameters); + } + template + VTKM_EXEC_EXPORT + void ForEachExec(const Functor &f) const { + detail::DoForEachExec(f, this->Parameters); + } + template + VTKM_EXEC_EXPORT + void ForEachExec(const Functor &f) { + detail::DoForEachExec(f, this->Parameters); + } + private: vtkm::internal::FunctionInterfaceReturnContainer Result; detail::ParameterContainer Parameters; diff --git a/vtkm/internal/testing/UnitTestFunctionInterface.cxx b/vtkm/internal/testing/UnitTestFunctionInterface.cxx index 5a961a7cc..e41f124a0 100644 --- a/vtkm/internal/testing/UnitTestFunctionInterface.cxx +++ b/vtkm/internal/testing/UnitTestFunctionInterface.cxx @@ -298,6 +298,14 @@ struct DynamicTransformFinish } }; +struct ForEachFunctor +{ + template + void operator()(T &x) const { x = 2*x; } + + void operator()(std::string &x) const { x.append("*2"); } +}; + void TryFunctionInterface5( vtkm::internal::FunctionInterface funcInterface) { @@ -470,6 +478,33 @@ void TestDynamicTransform() "DynamicTransform did not call finish the right number of times."); } +void TestForEach() +{ + std::cout << "Checking running a function on each parameter." << std::endl; + vtkm::internal::FunctionInterface + funcInterface = vtkm::internal::make_FunctionInterface( + Arg1, Arg2, Arg3, Arg4, Arg5); + 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."); + + funcInterface.ForEachCont(ForEachFunctor()); + VTKM_TEST_ASSERT(funcInterface.GetParameter<1>() == 2*Arg1, "Arg 1 incorrect."); + VTKM_TEST_ASSERT(funcInterface.GetParameter<2>() == 2*Arg2, "Arg 2 incorrect."); + VTKM_TEST_ASSERT(funcInterface.GetParameter<3>() == Arg3+"*2", "Arg 3 incorrect."); + VTKM_TEST_ASSERT(funcInterface.GetParameter<4>() == 2*Arg4, "Arg 4 incorrect."); + VTKM_TEST_ASSERT(funcInterface.GetParameter<5>() == 2*Arg5, "Arg 5 incorrect."); + + funcInterface.ForEachExec(ForEachFunctor()); + VTKM_TEST_ASSERT(funcInterface.GetParameter<1>() == 4*Arg1, "Arg 1 incorrect."); + VTKM_TEST_ASSERT(funcInterface.GetParameter<2>() == 4*Arg2, "Arg 2 incorrect."); + VTKM_TEST_ASSERT(funcInterface.GetParameter<3>() == Arg3+"*2*2", "Arg 3 incorrect."); + VTKM_TEST_ASSERT(funcInterface.GetParameter<4>() == 4*Arg4, "Arg 4 incorrect."); + VTKM_TEST_ASSERT(funcInterface.GetParameter<5>() == 4*Arg5, "Arg 5 incorrect."); +} + void TestInvokeTime() { std::cout << "Checking time to call lots of args lots of times." << std::endl; @@ -530,6 +565,7 @@ void TestFunctionInterface() TestTransformInvoke(); TestStaticTransform(); TestDynamicTransform(); + TestForEach(); TestInvokeTime(); }