我正在使用模板元编程来创建 Variant 和 Functor(通用仿函数)数据类型。我有一个有趣的问题,需要以某种方式处理特定参数类型的参数。理想情况下,我想使用某种编译时条件运算符来处理给定参数,如果条件满足,则使用方法 A,如果条件失败,则使用 B。
高级问题总结:
- 我需要通过变体的内部值将变体传递给函数指针的调用,或者将变体本身传递给调用,具体取决于预期的参数类型是否为 Variant 类型。
详情:
调用 Functor 时,会使用一组 Variants 来模拟函数参数。这是我的 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(多功能指针)的 union 中。当调用 Functor 时,联合被类型转换为适当的签名类型,如上所示。传递给 Functor 的每个 Variant 都通过 GetValue 方法转换为 Variant 中保存的值。这意味着我将在调用期间传递给 Functor 的每个变体的内部数据转换为它们各自的值。要转换为的值的类型是从将模板化的 StaticFnCall 与 MultiFnPtr 的签名匹配推导出来的。
下面是 GetValue 的实现:
template <typename TYPE>
const TYPE& VariantBase::GetValue( void ) const
{
return *reinterpret_cast<TYPE *>(data);
}
问题是我试图将一个函数签名包装在一个将 Variant 作为其参数类型之一的 Functor 中。只要在调用 Functor 时将 Variant 传递给采用 Variant 的参数,这就很好。但是,我需要将任意类型传递给采用 Variant 的参数。然后 GetValue 将用于将任意类型转换为 Variant *,当我希望改为使用 Variant 的构造函数创建一个 Variant 以传递给被调用的函数指针时,这会导致该任意类型的数据被解释为字面上的 Variant函子内。
我一直在尝试想出一种将值直接传递给 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 ) ) );
}