C ++模板MetaProgramming:在模板类型上编译时间条件运算符

时间:2012-11-15 06:12:24

标签: c++ templates reflection metaprogramming variant

我正在使用模板元编程来创建Variant和Functor(通用仿函数)数据类型。我有一个有趣的问题,需要以某种方式处理特定参数类型的参数。理想情况下,如果满足条件,我想使用某种编译时条件运算符来处理方法A的给定参数,如果条件失败,则使用B.

高级别问题摘要

  • 我需要将一个变量传递给函数指针的调用 变量的内部值,或者将变量本身传递给调用,具体取决于期望的参数类型是否为Variant类型。

详情

调用Functor时,变量数组用于模拟函数参数。这是我的Functor的一个重载构造函数的示例:

Variant operator()( Variant arg0, Variant arg1, Variant arg2 );

可以使用我传递给它的任何类型的数据构造Variant。这一切都很好,直到我得到这段代码(这是一个特殊的函子调用助手类,用于需要3个参数的签名):

template <typename R, typename T0, typename T1, typename T2>
Variant StaticFnCall3( MultiFnPtr fn, Variant& arg0, Variant& arg1, Variant& arg2 )
{
  return reinterpret_cast<typename VoidToType<R>::type(*)(T0, T1, T2)>(fn.StaticFn)( arg0.GetValue<T0>( ), arg1.GetValue<T1>( ), arg2.GetValue<T2>( ) );
}

每个Functor都存储一个函数指针,函数指针存储在一个名为MultiFnPtr(多函数指针)的联合中。如上所示,当调用Functor时,联合被强制转换为适当的签名类型。传递给Functor的每个Variant都将通过GetValue方法转换为Variant中保存的值。这意味着我将在调用期间传递给Functor的每个Variants的内部数据转换为它们各自的值。要转换为的值类型是从模板化的StaticFnCall与MultiFnPtr的签名匹配推导出来的。

以下是GetValue的实现:

template <typename TYPE>
const TYPE& VariantBase::GetValue( void ) const
{
  return *reinterpret_cast<TYPE *>(data);
}

问题在于我正在尝试将函数签名包装在Functor中,该函数将Variant作为其参数类型之一。只要调用Functor时,就可以将Variant传递给带Variant的参数。但是,我需要将任意类型传递给使用Variant的参数。然后,GetValue将用于将任意类型转换为Variant *,这会导致任意类型的数据按字面解释为Variant,当我希望使用Variant的构造函数创建Variant以传递给被调用的函数指针时在Functor中。

我一直试图想出一种直接将值传递给StaticFnCall的函数指针的方法,而不是在相应的模板类型是Variant时使用GetValue。我已经查找了std :: enable_if和sfinae,但我正在努力寻求一个解决方案。以下是我正在尝试实现的伪代码示例:

template <typename R, typename T0, typename T1, typename T2>
Variant StaticFnCall3( MultiFnPtr fn, Variant& arg0, Variant& arg1, Variant& arg2 )
{
  return reinterpret_cast<typename VoidToType<R>::type(*)(T0, T1, T2)>(fn.StaticFn)( (IF_IS_VARIANT) ? arg0 : arg0.GetValue<T0>( ), (IF_IS_VARIANT) ? arg1 : arg1.GetValue<T1>( ), (IF_IS_VARIANT) ? arg2 : arg2.GetValue<T2>( ) );
}

修改

所以我发现我可以使用模板化的全局函数并使用模板特化来以两种方式之一处理参数。但是,这不是编译时解决方案,因为全局函数将导致分支,除非函数内联。

template<typename T>
const T ArgHandle( const RefVariant& arg )
{
  return arg.GetValue<T>( );
}

template<>
const Variant ArgHandle<Variant>( const RefVariant& arg )
{
  return Variant( arg );
}

由于函数ArgHandle在编译时具有重载解析,我想可能有某种方法来实现我想要的行为而不需要函数调用。使用:

#define ARG( NUM ) \
  ArgHandle<T##NUM>( arg##NUM )

template <typename R, typename T0, typename T1, typename T2>
Variant StaticFnCall3( MultiFnPtr fn, RefVariant& arg0, RefVariant& arg1, RefVariant& arg2 )
{
  return reinterpret_cast<typename VoidToType<R>::type(*)(T0, T1, T2)>(fn.StaticFn)( ARG( 0 ), ARG( 1 ), ARG( 2 ) ) );
}

1 个答案:

答案 0 :(得分:1)

我不明白你为什么不在这部分问题后停止:

template <typename TYPE>
const TYPE& VariantBase::GetValue( void ) const
{
  return *reinterpret_cast<TYPE *>(data);
}

并为Variant添加模板专精:

template <>
const VariantBase& VariantBase::GetValue<VariantBase>( void ) const
{
  return *this;
}

并完成它。有什么不适用于此?在您的问题中,您似乎正在绕过此解决方案,但到那时您已经介绍了无意义的ArgHandle函数,宏和辅助函数,这只是一团糟。

就个人而言,我完全摆脱了GetValue函数,只提供了隐式类型转换运算符,因此您可以编写fn(arg0, arg1, arg2)。但我想这取决于你的代码的其余部分。