template specialization according to sizeof type

I would like to provide a templated function, that varies its implementation (->specialization) according to the sizeof the template type.

Something similar to this (omitted typecasts), but without the if/elseif:

template<class T>
T byteswap(T & swapIt)
{
    if(sizeof(T) == 2)
    {
        return _byteswap_ushort (swapIt);
    }
    else if(sizeof(T) == 4)
    {
        return _byteswap_ulong(swapIt);
    }
    else if(sizeof(T) == 8)
    {
        return _byteswap_uint64(swapIt);
    }
            throw std::exception();
}

I know there are many roads to reach my goal, but since I try to learn about SFINAE and type traits I'm particularly interested in solutions using those techniques to decide at compile time which specialization to choose and which calls are not admitted.

Perhaps implementing a class trait is_4ByteLong and using boost::enable_if...

I have to admit, I'm stuck right now, so I thank you for any help or advice

18
задан nabulke 20 August 2010 в 08:23
поделиться

4 ответа

Вам не нужны SFINAE или типажи. Специализации ванильного шаблона достаточно. Конечно, он должен быть специализирован на структурах, поскольку C ++ (98) не поддерживает частичную специализацию шаблона функции.

template <typename T, size_t n>
struct ByteswapImpl
/*
{
  T operator()(T& swapIt) const { throw std::exception(); }
}
*/    // remove the comments if you need run-time error instead of compile-time error.
;

template <typename T>
struct ByteswapImpl<T, 2> {
  T operator()(T& swapIt) const { return _byteswap_ushort (swapIt); }
};

// ...

template <typename T>
T byteswap(T& swapIt) { return ByteswapImpl<T, sizeof(T)>()(swapIt); }
20
ответ дан 30 November 2019 в 08:15
поделиться

Просто ради демонстрации enable_if в действии, раз уж вы об этом говорили:

template <class T>
typename boost::enable_if_c< sizeof(T) == 2, T >::type
swapIt(T& rhs) { return _byteswap_short(rhs); }

template <class T>
typename boost::enable_if_c< sizeof(T) == 4, T >::type
swapIt(T& rhs) { return _byteswap_long(rhs); }

и т.д ...

И, конечно, вместо того, чтобы бросать, просто не существует реализации, если тип не соответствует ни одному из требований и, следовательно, у вас есть ошибка времени компиляции.

Два примечания:

  • Использование typename и :: type является обязательным
  • Я использовал enable_if_c , потому что мое выражение вычисляет логическое значение напрямую, тогда как enable_if требует типа, содержащего член :: value , который является логическим.
3
ответ дан 30 November 2019 в 08:15
поделиться

Я могу предложить следующий метод: Его преимущество в том, что вам не нужно выбрасывать исключение, если операнд не имеет допустимого размера. Он просто не соединится. Таким образом, проверка ошибок происходит во время сборки.

template<int size>
void byteswapInPlace(void* p);

template<> void byteswapInPlace<1>(void* p) { /* do nothing */ }

template<> void byteswapInPlace<2>(void* p)
{
    _byteswap_ushort((ushort*) p);
}

template<> void byteswapInPlace<4>(void* p)
{
    _byteswap_ulong((ulong*) p);
}

template<> void byteswapInPlace<8>(void* p)
{
    _byteswap_uint64((uint64*) p);
}


template<class T>
T byteswap(T & swapIt)
{
    byteswapInPlace<sizeof(T)>(&swapIt);
    return swapIt;
}
1
ответ дан 30 November 2019 в 08:15
поделиться

Просто сделайте вспомогательный класс, который принимает размер как аргумент шаблона:

#include <cstddef>
#include <iostream>


template<std::size_t Size>
struct ByteSwapper { };

template<>
struct ByteSwapper<2> {
  static unsigned short swap(unsigned short a) {
    return 2 * a;
  }
};

template<typename T>
T byteswap(const T& a) {
  return ByteSwapper<sizeof(T)>::swap(a);
}


int main() {
  unsigned short s = 5;
  std::cout << byteswap(s) << std::endl;
  unsigned int i = 7;
  // std::cout << byteswap(i) << std::endl; // error
}
4
ответ дан 30 November 2019 в 08:15
поделиться
Другие вопросы по тегам:

Похожие вопросы: