Я использую шаблонное метапрограммирование для создания типа данных Variant и Functor (универсальный функтор). У меня есть интересная проблема с необходимостью обрабатывать аргументы определенным образом для определенных типов аргументов. В идеале я хотел бы использовать какой-то условный оператор времени компиляции для обработки заданного аргумента с помощью метода A, если условие выполнено, и B, если условие не выполнено.
Краткое описание проблемы высокого уровня:
подробности:
При вызове Functor для имитации аргументов функции используется массив Variants. Вот пример одного из перегруженных конструкторов для моего Functor:
Variant operator()( Variant arg0, Variant arg1, Variant arg2 );
Вариант может быть создан с любым типом данных, которые я передаю ему. Это все нормально, пока я не доберусь до этого фрагмента кода (это специальный класс помощника вызова функтора для подписи, требующий 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, как показано выше. Каждый Variant, переданный в Functor, преобразуется в значение, содержащееся в Variant, методом GetValue. Это означает, что я конвертирую внутренние данные каждого варианта, которые были переданы в Functor во время вызова, в их соответствующие значения. Тип значения для преобразования выводится из сопоставления шаблонного StaticFnCall с подписью MultiFnPtr.
Вот реализация GetValue:
template <typename TYPE>
const TYPE& VariantBase::GetValue( void ) const
{
return *reinterpret_cast<TYPE *>(data);
}
Проблема в том, что я пытаюсь обернуть сигнатуру функции в Functor, который принимает Variant в качестве одного из типов параметров. Это хорошо до тех пор, пока когда Functor вызывается Variant, передается аргумент, который принимает Variant. Однако мне нужно передать произвольный тип аргументу, принимающему вариант. Затем GetValue будет использоваться для преобразования произвольного типа в Variant *, что приводит к буквальному интерпретации данных этого произвольного типа как Variant, когда я вместо этого хочу использовать конструктор Variant для создания Variant для передачи указателю на функцию, вызываемую внутри функтора.
Я пытался придумать способ передачи значения указателю функции StaticFnCall напрямую, вместо использования GetValue, когда соответствующий тип шаблона является Variant. Я посмотрел 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 ) ) );
}
Я не понимаю, почему вы не останавливаетесь после этой части вашего вопроса:
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)
, Но я думаю, это зависит от того, как выглядит остальная часть вашего кода.
Других решений пока нет …