Есть ли в C++ "стандартная" htonl-подобная функция для 64-битных чисел?
Я работаю над реализацией протокола memcache, который в некоторых моментах использует 64-битные целочисленные значения. Эти значения должны храниться в "сетевом порядке байтов".
Я хотел бы было немного uint64_t htonll(uint64_t value)
функция для внесения изменений, но, к сожалению, если она существует, я не смог ее найти.
Итак, у меня есть 1 или 2 вопроса:
- Существует ли какая-либо переносимая (Windows, Linux, AIX) стандартная функция для этого?
- Если такой функции нет, как бы вы ее реализовали?
Я имею в виду базовую реализацию, но я не знаю, как проверить порядок байтов во время компиляции, чтобы сделать код переносимым. Так что ваша помощь здесь приветствуется;)
Спасибо.
Вот окончательное решение, которое я написал, благодаря решению Брайана.
uint64_t htonll(uint64_t value)
{
// The answer is 42
static const int num = 42;
// Check the endianness
if (*reinterpret_cast<const char*>(&num) == num)
{
const uint32_t high_part = htonl(static_cast<uint32_t>(value >> 32));
const uint32_t low_part = htonl(static_cast<uint32_t>(value & 0xFFFFFFFFLL));
return (static_cast<uint64_t>(low_part) << 32) | high_part;
} else
{
return value;
}
}
6 ответов
Вы, вероятно, ищете bswap_64
Я думаю, что это поддерживается почти везде, но я бы не назвал это стандартным.
Вы можете легко проверить порядковый номер, создав int со значением 1, приведя адрес вашего int как char*
и проверка значения первого байта.
Например:
int num = 42;
if(*(char *)&num == 42)
{
//Little Endian
}
else
{
//Big Endian
}
Зная это, вы также можете сделать простую функцию, которая выполняет обмен.
Вы также можете всегда использовать boost, который содержит макросы с прямым порядком байтов, которые являются переносимыми кроссплатформенными.
#define htonll(x) ((1==htonl(1)) ? (x) : ((uint64_t)htonl((x) & 0xFFFFFFFF) << 32) | htonl((x) >> 32))
#define ntohll(x) ((1==ntohl(1)) ? (x) : ((uint64_t)ntohl((x) & 0xFFFFFFFF) << 32) | ntohl((x) >> 32))
Тест (1==htonl(1)) просто определяет (к сожалению, во время выполнения), требует ли аппаратная архитектура замены байтов. Во время компиляции не существует каких-либо переносимых способов определения архитектуры, поэтому мы прибегаем к использованию "htonl", который настолько переносим, насколько это возможно в этой ситуации. Если требуется замена байтов, то мы заменяем 32 бита за раз, используя htonl (не забывая также поменять местами два 32-битных слова).
Вы можете попробовать с uint64_t htobe64(uint64_t host_64bits)
&uint64_t be64toh(uint64_t big_endian_64bits)
для наоборот.
Это похоже на работу в C; я сделал что-то не так?
uint64_t htonll(uint64_t value) {
int num = 42;
if (*(char *)&num == 42) {
uint32_t high_part = htonl((uint32_t)(value >> 32));
uint32_t low_part = htonl((uint32_t)(value & 0xFFFFFFFFLL));
return (((uint64_t)low_part) << 32) | high_part;
} else {
return value;
}
}
Чтобы уменьшить накладные расходы на "if num == ...", используйте препроцессор:
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#else
#endif
Что ж, я думаю, что лучше использовать переключение endian во время компиляции, насколько это возможно, но я предпочитаю использовать функцию вместо макроса, потому что в макросе параметры просто заменяются аргументами; поэтому аргументы можно оценивать несколько раз и создавать странный результат, если они присутствуют в макросе несколько раз (как это сделано в некоторых из предыдущих предоставленных решений).
uint64_t htonll(uint64_t x)
{
#if __BIG_ENDIAN__
return x;
#else
return ((uint64_t)htonl((x) & 0xFFFFFFFFLL) << 32) | htonl((x) >> 32);
#endif
}
uint64_t ntohll(uint64_t x)
{
#if __BIG_ENDIAN__
return x;
#else
return ((uint64_t)ntohl((x) & 0xFFFFFFFFLL) << 32) | ntohl((x) >> 32);
#endif
}
Таким образом, это позволяет вызывать htonll(x++) без увеличения x несколько раз, как это будет сделано с использованием предыдущих макросов.
РЕДАКТИРОВАТЬ: объединение двух (используется код Брайана):
uint64_t htonll(uint64_t value)
{
int num = 42;
if(*(char *)&num == 42)
return (htonl(value & 0xFFFFFFFF) << 32LL) | htonl(value >> 32);
else
return value;
}
Предупреждение: непроверенный код! Пожалуйста, проверьте перед использованием.