2016-04-29 2 views
15

Как написать функцию constexpr, чтобы обменивать константу целого числа, не полагаясь на расширения компилятора, и можете ли вы привести пример о том, как это сделать?Как написать функцию constexpr swap для изменения endianess целого числа?

+6

Что такое «континент целого»? В чем смысл 15? –

+0

@KerrekSB Что бы это ни было. Я не задавал этот вопрос. Мой вопрос заключается в том, как поменять big-endian на little-endian и наоборот. – user1095108

+3

@KerrekSB: В контексте C++ (и большинства программ в целом), когда говорят целое число, они обычно ссылаются на целочисленный объект. То есть область памяти, используемая для хранения целочисленных данных, обычно является одним из основных целых типов (char, short, int, long и long long вместе с их неподписанными вариантами). Вы действительно никогда не сталкивались с этим использованием? –

ответ

31

Да, это довольно легко; здесь рекурсивный (C++ 11-совместимый) реализация (только неподписанные интегральные типы):

#include <climits> 
#include <cstdint> 
#include <type_traits> 

template<class T> 
constexpr typename std::enable_if<std::is_unsigned<T>::value, T>::type 
bswap(T i, T j = 0u, std::size_t n = 0u) { 
    return n == sizeof(T) ? j : 
    bswap<T>(i >> CHAR_BIT, (j << CHAR_BIT) | (i & (T)(unsigned char)(-1)), n + 1); 
} 

Example.

Здесь я использую j в качестве аккумулятора и n в качестве счетчика цикла (индексирование байт) ,

Если у вас есть компилятор, поддерживающий C++17 fold expressions, что можно написать что-то, что расширяющийся вне в точности то, что вы хотите написать вручную:

template<class T, std::size_t... N> 
constexpr T bswap_impl(T i, std::index_sequence<N...>) { 
    return ((((i >> (N * CHAR_BIT)) & (T)(unsigned char)(-1)) << 
      ((sizeof(T) - 1 - N) * CHAR_BIT)) | ...); 
}; //          ^~~~~ fold expression 
template<class T, class U = typename std::make_unsigned<T>::type> 
constexpr U bswap(T i) { 
    return bswap_impl<U>(i, std::make_index_sequence<sizeof(T)>{}); 
} 

Преимущество этой формы в том, что, поскольку он не использует циклы или рекурсии, вы в значительной степени гарантированно получаете оптимальную сборку - на x86-64, clang даже управляет work out to use the bswap instruction.

2

Вдохновленный ecatmur Я предлагаю следующее решение, которое имеет потенциально лучшую производительность, когда компилятор не обнаруживает bswap (O (log (n)) vs O (N)). Учитывая, что N обычно < = 8 это, вероятно, не имеет значения, до сих пор:

template <typename T> 
typename std::enable_if<std::is_unsigned<T>::value,T>::type 
constexpr alternating_bitmask(const size_t step){ 
    T mask(0); 
    for (size_t i=0;i<digits<T>();i+=2*step){ 
    mask|=(~T(0)>>(digits<T>()-step))<<i; 
    } 
    return mask; 
} 

template <typename T> 
typename std::enable_if<std::is_unsigned<T>::value,T>::type 
constexpr bswap(T n){ 
    for (size_t i=digits<unsigned char>();i<digits<T>();i*=2){ 
    n = ((n&(~(alternating_bitmask<T>(i))))>>i)| 
     ((n&((alternating_bitmask<T>(i))))<<i); 
    } 
    return n; 
} 

Поскольку эта форма является более сложным, чем решение ecatmur в компилятор имеет более жесткую оптимизацию рабочих мест, но лязг до сих пор считает, что мы имеем в виду BSWAP.

+1

Это решение на самом деле имеет временную сложность Θ (N), так как внутренний цикл (не считая оптимизаций) имеет амортизированную сложность Θ (N/log N) (за итерацию внешнего цикла). Чтобы получить фактический Θ (log N), битмаски должны быть сохранены в памяти, например. предварительно вычисляется в массив. –

+0

@ArneVogel это правда, я просто предположил, что битмаски будут компилировать константы времени, поскольку функция, которая их генерирует, является constexpr. – Lykos

Смежные вопросы