Недавно я внедрил специализированный парсер для слегка модифицированного Абстрактная синтаксическая нотация. В спецификации говорится, что целые числа кодируются как массив октетов, которые должны интерпретироваться как двоичное целое число с дополнением до двух.
Итак, сначала я подумал, что лучший способ сделать сериализацию в настоящем C ++ int
было бы просто начать со значения 0, а затем ИЛИ каждый октет со значением вроде:
uint64_t value = 0;
int shift = 0;
std::vector<uint8_t> octets = { /* some values */ };
for (auto it = octets.rbegin(); it != octets.rend(); ++shift, ++it)
{
value |= uint64_t(*it) << (shift * 8);
}
Это оставило бы меня с небольшим паттерном, сохраненным в value
, который я мог бы затем интерпретировать как целое число со знаком (два в дополнение), приведя его:
int64_t signed_value = static_cast<int64_t>(value);
Но мне пришло в голову, что это действительно полагается на поведение, определяемое реализацией. C ++ не гарантирует, что целые числа со знаком представляются как дополнение к двум. Итак, чтобы получить фактическое значение закодированного целого числа как C ++ int64_t
Мне нужно было бы на самом деле рассчитать суммирование 2 ^ N для каждого N-го бита в битовой комбинации с учетом знакового бита. Это кажется глупым, когда я знаю, что кастинг должен работать большую часть времени.
Есть ли здесь лучшее решение, которое было бы одновременно портативным и эффективным?
Если ваше решение работает, я думаю, что вы можете использовать немного метапрограммирования, чтобы проверить, является ли ваша платформа дополнением одного или двух.
struct is_ones_complement {
static const bool value = ( (1 & -1) == 0);
}
И тогда вы можете написать встроенную функцию преобразования:
template<bool is_ones_complement>
uint64_t convert_impl(const std::vector<uint8_t>& vec);
template<>
uint64_t convert_impl<true>(const std::vector<uint8_t>& vec) {
// Your specialization for 1's-complement platforms
}
template<>
uint64_t convert_impl<false>(const std::vector<uint8_t>& vec) {
// Your specialization for 2's-complement platforms
}
inline uint64_t convert(const std::vector<uint8_t>& vec) {
return convert_impl<is_ones_complement::value>(vec);
}
Не проверено, но это должно работать.
Других решений пока нет …