#c #compile-time #stdtuple
#c #время компиляции #stdtuple
Вопрос:
У меня есть фрагмент кода, который я пытался автоматически декодировать в буфере с учетом ожидаемых типов данных. Данные представлены в виде кортежей:
std::tuple<uint8_t, int32_t> data;
size_t bufferIndex;
IOBuffer::ConstSPtr buffer( std::make_shared<IOBuffer>(5) );
У меня также есть хэплеры кортежей для перебора кортежей и выполнения функтора для каждого из них:
//-------------------------------------------------------------------------
//
template <typename Function, typename ...Tuples, typename ...Args>
void IterateOverTuple( Functionamp; f, std::tuple<Tuples...>amp; t,
Argsamp;... args )
{
impl::IterateOverTupleImpl<0, sizeof...(Tuples),
std::tuple<Tuples...>>()( f, t, args... );
}
//---------------------------------------------------------------------
//
template <int I, size_t TSize, typename Tuple>
struct IterateOverTupleImpl
: public IterateOverTupleImpl<I 1, TSize, Tuple>
{
template <typename Function, typename ...Args>
void operator()( Functionamp; f, Tupleamp; t, Argsamp;... args )
{
f( std::get<I>(t), args... );
IterateOverTupleImpl<I 1, TSize, Tuple>::operator()( f, t,
args... );
}
};
//---------------------------------------------------------------------
//
template <int I, typename Tuple>
struct IterateOverTupleImpl<I, I, Tuple>
{
template <typename Function, typename ...Args>
void operator()( Functionamp; f, Tupleamp; t, Argsamp;... )
{
cl::Ignore(f);
cl::Ignore(t);
}
};
И вот мой функтор декодера:
struct DecoderFunctor
{
template <typename X>
void DecodeIntegral( Xamp; x, const IOBuffer::ConstSPtramp; buffer, size_tamp; index )
{
if( std::is_same<X, uint8_t>::value )
{
x = buffer->At(index);
}
else if( std::is_same<X, int8_t>::value )
{
x = static_cast<int8_t>( buffer->At(index) );
}
else if( std::is_same<X, uint16_t>::value )
{
x = cl::ByteConversion::UbytesToUInt16( UByteArray<2>{{
buffer->At(index 0),
buffer->At(index 1) }}
);
}
else if( std::is_same<X, int16_t>::value )
{
x = cl::ByteConversion::UbytesToInt16( UByteArray<2>{{
buffer->At(index 0),
buffer->At(index 1) }}
);
}
else if( std::is_same<X, uint32_t>::value )
{
x = cl::ByteConversion::UbytesToUInt32( UByteArray<4>{{
buffer->At(index 0),
buffer->At(index 1),
buffer->At(index 2),
buffer->At(index 3) }}
);
}
else if( std::is_same<X, int32_t>::value )
{
x = cl::ByteConversion::UbytesToInt32( UByteArray<4>{{
buffer->At(index 0),
buffer->At(index 1),
buffer->At(index 2),
buffer->At(index 3) }}
);
}
else if( std::is_same<X, uint64_t>::value )
{
x = cl::ByteConversion::UbytesToUInt64( UByteArray<8>{{
buffer->At(index 0),
buffer->At(index 1),
buffer->At(index 2),
buffer->At(index 3),
buffer->At(index 4),
buffer->At(index 5),
buffer->At(index 6),
buffer->At(index 7) }}
);
}
else if( std::is_same<X, int64_t>::value )
{
x = cl::ByteConversion::UbytesToInt64( UByteArray<8>{{
buffer->At(index 0),
buffer->At(index 1),
buffer->At(index 2),
buffer->At(index 3),
buffer->At(index 4),
buffer->At(index 5),
buffer->At(index 6),
buffer->At(index 7) }}
);
}
// Increment the index in the buffer
index = sizeof(X);
}
template <typename X>
void operator()( Xamp; x, const IOBuffer::ConstSPtramp; buffer, size_tamp; index )
{
if( std::is_integral<X>::value )
{
DecodeIntegral( x, buffer, index );
}
}
};
И именно здесь вызывается код:
DecoderFunctor func;
IterateOverTuple( func, data, buffer, index );
Таким образом, все отлично работает с целыми типами, и они отлично декодируются. Однако, когда я хотел попытаться реализовать новый метод декодирования (для массивов), он не компилировался:
std::tuple<std::array<uint16_t, 100>, std::array<uint8_t, 100>> data;
Вот ошибка (gcc-4.9).
Поэтому я не понимаю, почему я получаю эту ошибку. Из-за теста std::is_integral<X>::value
данные не должны оцениваться DecodeIntegral( x, buffer, index );
правильно?
Пожалуйста, не указывайте, что это незавершенная работа, поэтому, безусловно, есть несколько ошибок и улучшений. И спасибо вам за вашу помощь!
Ответ №1:
Я признаю, что я не прошел весь код, но я считаю, что ваша проблема связана с условиями выполнения и времени компиляции. Вы не можете использовать условие времени выполнения (например if (std::is_integral<:::>::value>)
, для предотвращения ошибок во время компиляции.
Я понимаю DecodeIntegral
, что он компилируется только тогда, когда X
действительно является целым. Поэтому вы должны убедиться, что вызов DecodeIntegral
с нецелым X
значением никогда не будет замечен компилятором (т. Е. Создан экземпляр), а не только в том, что он никогда не происходит во время выполнения.
Поскольку функция DecodeIntegral
может легко быть статическим членом без каких-либо изменений в семантике, вы можете использовать трюк «делегировать классу» для достижения этой цели. Перейдите DecodeIntegral
во вспомогательный класс:
template <bool Integral>
struct Helper;
template <>
struct Helper<true>
{
template <class X>
static void Decode( Xamp; x, const IOBuffer::ConstSPtramp; buffer, size_tamp; index )
{
// Old code of DecodeIntegral() goes here
}
};
template <>
struct Helper<false>
{
template <class X>
static void Decode( Xamp; x, const IOBuffer::ConstSPtramp; buffer, size_tamp; index )
{
// Code for non-integral decoding goes here
}
};
struct DecoderFunctor
{
template <typename X>
void operator()( Xamp; x, const IOBuffer::ConstSPtramp; buffer, size_tamp; index )
{
Helper<std::is_integral<X>::value>::Decode(x, buffer, index);
}
};
Добавлено на основе запроса в комментарии
Если вам нужно более одного дискриминатора, добавьте дополнительные bool
параметры шаблона в помощник. Если для нужного вам дискриминатора нет стандартного предиката, вы можете написать свой собственный.
(В приведенном ниже примере предполагается, что дискриминаторы являются исключительными — не более одного истинно):
// Two-discriminator helper
template <bool Integral, bool Array>
struct Helper;
template <>
struct Helper<true, false>
{
void Decode() { /* integral decode */ }
};
template <>
struct Helper<false, true>
{
void Decode() { /* array decode */ }
};
// Custom predicate
template <class T>
struct IsStdArray : std::false_type
{};
template <class T, size_t N>
struct IsStdArray<std::array<T, N>> : std::true_type
{};
// Usage
struct DecoderFunctor
{
template <typename X>
void operator()( Xamp; x, const IOBuffer::ConstSPtramp; buffer, size_tamp; index )
{
Helper<
std::is_integral<X>::value,
IsStdArray<X>::value
>::Decode(x, buffer, index);
}
};
Комментарии:
1. Вы правы, в этих случаях я должен научиться больше думать о времени компиляции, а не о времени выполнения. Кстати, есть ли способ определить во время компиляции, что данные являются std::array ? (std::is_array в моем случае не работает)
2. @Athanase я расширил ответ
3. Методы шаблонов действительно невероятны. Это чудесно :).
Ответ №2:
std::is_integral<X>::value
Вычисляется false
, но это не означает, что приведенный ниже код ( DecodeIntegral( x, buffer, index );
) «пропущен». Компилятор тоже видит эту строку. Итак, в настоящее время у вас есть условие времени выполнения, но на самом деле вам нужно условие времени компиляции.
Редактировать: я просто хотел отредактировать свой ответ с помощью короткого примера, но Angew был быстрее. Смотрите его ответ 🙂
Комментарии:
1. Спасибо за ваш ответ!