简体   繁体   中英

C++ Template MetaProgramming: Compile Time Conditional Operator on Template Type

I'm using template metaprogramming to create a Variant and Functor (a generic functor) data type. I have an interesting problem with needing to handle arguments a certain way for particular argument types. Ideally I'd like to use some sort of compile-time conditional operator to handle a given argument with method A if condition is met, and B if condition fails.

High level problem summary :

  • I need to pass a variant to a call on a function pointer by either the variant's internal value, or pass the variant itself to the call depending on whether or not the argument type expected is of a Variant type.

Details :

When calling a Functor an array of Variants is used to simulate function arguments. Here's an example of one of the overloaded constructors for my Functor :

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

The Variant can be constructed with any type of data I pass to it. This is all fine until I get to this piece of code (this is a specific functor call helper class for a signature requiring 3 arguments):

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>( ) );
}

Each Functor stores a function pointer, and the function pointer is stored in a union called MultiFnPtr (multi function pointer). The union is typecasted to the appropriate signature type when the Functor is called, as seen above. Each Variant passed to the Functor is converted to the value held within the Variant by the GetValue method. This means I'm converting each Variants' internal data that had been passed to the Functor during the call into their respective values. The value's type to convert to is deduced from matching a templated StaticFnCall to the MultiFnPtr's signature.

Here is the implementation of GetValue:

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

The problem is that I'm trying to wrap a function signature within a Functor that takes a Variant as one of its parameter types. This is fine as long as when the Functor is called a Variant is passed to the argument that takes a Variant. However, I'm in need of passing an arbitrary type to the argument taking a Variant. GetValue would then be used to convert an arbitrary type to a Variant *, which causes that arbitrary type's data to be interpreted as a Variant literally, when I desire to instead use the Variant's constructor to create a Variant to pass to the function pointer being called within the Functor.

I've been trying to come up with a way of passing a value to the StaticFnCall's function pointer directly, instead of using GetValue when the corresponding template type is a Variant. I've looked up std::enable_if and sfinae but am struggling to get a solution together. Here's an example in pseudo code of what I'm trying to achieve:

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>( ) );
}

EDIT :

So I figured out that I can use a templated global function and use template specialization to handle an argument in one of two ways. However this isn't a compile-time solution as a global function will cause branching, unless the function is inlined.

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

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

Since the function ArgHandle has overload resolution at compile-time I imagine there might be some sort of way to achieve the behavior I'd like without a function call. Use:

#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 ) ) );
}

I don't understand why you don't just stop after this part of your question:

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

and add a template specialization for Variant :

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

and be done with it. Does something not work with this? It seems like you were circling around to this solution later on in your question, but by then you'd introduced the pointless ArgHandle function, and macros, and helper functions, and it was just a mess.

Personally, I'd get rid of the GetValue function altogether, and just provide implicit type-conversion operators so you could write fn(arg0, arg1, arg2) . But I guess this depends on what the rest of your code looks like.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM