LINUX.ORG.RU

Установить определённый байт в n байтном целом числе

 , ,


1

4

Можно как-то в c++ сделать сабж, чтоб прям красиво с обмазыванием типа как в boost?

Как на C то понятно сделать.

★★★★★

Последнее исправление: CYB3R (всего исправлений: 1)

Ответ на: комментарий от aureliano15

Ну типа синтаксический сахар там и прочее, может в boost что-то для этого есть? (с ходу не нашёл)

fornlr ★★★★★
() автор топика

Вспомни, как страшно выглядит в C++ аналог printf'а и узбагойся!

anonymous
()

чем не устраивает такой вариант?

#include <iostream>
#include <inttypes.h>

int main()
{
	uint16_t result;
	uint8_t byte1, byte2;

	byte1 = 42;
	byte2 = 146;

	result |= byte1 << 8;
	result |= byte2;

	std::cout << std::hex << result << std::endl;

	return 0;
}

WRG ★★★★
()
union MyInt {
  char bytes[4];
  long integer;
}

...

  MyInt test;
  test.integer = 123456;
  test.bytes[2] = 0;

Как-то так. И это в C тоже сработает.

ziemin ★★
()
Ответ на: комментарий от fornlr

С классами

#include <iostream>
#include <inttypes.h>

class Bytefucker {
public:
	static void setByte(uint16_t &num, uint8_t pos, uint8_t byte) {
		num |= (byte << (pos * 8));
	}
};

int main()
{
	uint16_t result;
	uint8_t byte1, byte2;

	byte1 = 42;
	byte2 = 146;

	Bytefucker::setByte(result, 0, byte1);
	Bytefucker::setByte(result, 1, byte2);


	std::cout << std::hex << result << std::endl;

	return 0;
}

WRG ★★★★
()
Последнее исправление: WRG (всего исправлений: 1)
Ответ на: комментарий от WRG

Класс из одной статической функции на фиг не нужен. К тому же, функция не работает, она не устанавливает байт в нужное значение, а делает OR старого и нового значения. Надо было сначала байт обнулить. Ну и автор хотел шаблонное решение, а не только для uint16_t, насколько я понял.

gentoo_root ★★★★★
()
Последнее исправление: gentoo_root (всего исправлений: 1)
Ответ на: комментарий от fornlr

А что мешает завернуть то же самое в функцию-член?

class my_byte_vector
{
  public:
    int len;
    unsigned char* v;
    // Разные конструкторы, деструкторы и пр. функции.
    inline void set_byte(int n, unsigned char val)
    { if(n>=0 && n<len) v[n]=val; };
};
aureliano15 ★★
()
Ответ на: комментарий от ziemin

Я к тому, что такой способ бесполезен, если стоит задача «установить определённый байт в числе». Как я этим способом установлю младший байт в ноль, например?

gentoo_root ★★★★★
()
template<typename T>
constexpr T setbyte(T number, size_t byte, unsigned char value)
{
    return byte < sizeof(T) ? (number & ~(T(0xff) << (8 * byte))) | T(value) << (8 * byte)
                            : throw std::out_of_range("Attempt to access invalid byte");
}

Пока сообразил что-то такое, возможно, есть недостатки.

gentoo_root ★★★★★
()
Ответ на: комментарий от anonymous

Это UB

Да, есть даже компилятор, в котором это не работает (Sun):

http://blog.qt.io/blog/2011/06/10/type-punning-and-strict-aliasing/

Но в GCC, например, задокументировано, что поведение такое же, как и для Си.

gentoo_root ★★★★★
()
Последнее исправление: gentoo_root (всего исправлений: 1)
Ответ на: комментарий от ziemin

x86 это big-endian

Вообще-то, little endian.

Массив байт отображается на long как есть.

А в big endian наоборот. И как мне тогда написать код с этим union, который младший байт установит в ноль? Он будет не работать либо на big endian, либо на little endian, либо придётся вставлять уродливых #ifdef'ов (в функцию изменения байта, например).

gentoo_root ★★★★★
()
uint64_t void set_nth_byte(uint64_t number, uint8_t byte_index, uint8_t byte_value) {
  uint64_t clear_mask = ~((uint64_t) 0xff << (byte_index * 8));
  uint64_t set_mask = (uint64_t) byte_value << (byte_index * 8);
  return number & clear_mask | set_mask;
}

как то так (не тестировал).

Legioner ★★★★★
()

Вместо того, что бы сделать красиво и просто, будем делать, как в C++, с объектами и классами?

beastie ★★★★★
()
Ответ на: комментарий от gentoo_root

либо придётся вставлять уродливых #ifdef'ов

Имхо, ничего уродливого в этом нет. Это одна из парадигм C/C++ - возможность делать максимально эффективный код для различных архитектур, но не всегда полностью машинно-независимый. А большую независимость от архитектуры обеспечивать директивами препроцессора.

Если такой подход не нравится, никто не мешает перейти на ту же яву. У неё свои недостатки, но есть и свои достоинства.

aureliano15 ★★
()
Ответ на: комментарий от ziemin

Тогда уж:

#include <arpa/inet.h>
#include <stdio.h>
#include <stdint.h>

uint32_t
setbyte(uint32_t in, uint8_t val, unsigned int n)
{
        const int bytes = sizeof(uint32_t) / sizeof(uint8_t);
        union {
                uint32_t l;
                uint8_t b[bytes];
        } d;

        if (n >= bytes)
                return in;

        d.l = htonl(in);
        d.b[bytes - n - 1] = val;

        return ntohl(d.l);
}

int
main()
{
        printf("0x%.8x\n", setbyte(0x6100, 0x41, 0));
        printf("0x%.8x\n", setbyte(0x6100, 0x41, 1));
        printf("0x%.8x\n", setbyte(0x6100, 0x41, 2));
        printf("0x%.8x\n", setbyte(0x6100, 0x41, 3));
        printf("0x%.8x\n", setbyte(0x6100, 0x41, 4));   // shall fail

        return 0;
}

0x00006141
0x00004100
0x00416100
0x41006100
0x00006100
beastie ★★★★★
()
Последнее исправление: beastie (всего исправлений: 2)
Ответ на: комментарий от aureliano15

Это одна из парадигм C/C++ - возможность делать максимально эффективный код для различных архитектур, но не всегда полностью машинно-независимый.

GCC довольно умный: когда встречает сдвиги и битовые операции, это не означает, что на выхлопе будут соответствующие ассемблерные команды. Например, код (a >> 16) | (a << 16) генерирует одну-единственную операцию циклического сдвига на 16, в то время как аналогичный swap слов в инте, реализованный с помощью union, будет поувесистее и будет работать с несколькими регистрами, а может и с памятью (насчёт памяти не помню, этот тест делал давно).

Т.к. моя реализация setbyte ещё и отмечена constexpr, то в случае compile-time-констант функция вообще не будет вызвана, а будет подставлена константа; если только некоторые параметры известны, то тоже будут проведены соответствующие оптимизации. Так что я считаю, что версия кода со сдвигами во многих случаях будет быстрее версии с mov'ами. Бенчмарков я не делал, тем более тупой бенчмарк, много раз запускающий функцию setbyte, будет далёк от реальных условий, поскольку многое ещё и зависит от кол-ва используемых регистров, а это скажется лишь на окружающем коде.

gentoo_root ★★★★★
()
Ответ на: комментарий от gentoo_root

код (a >> 16) | (a << 16) генерирует одну-единственную операцию циклического сдвига на 16

Да, скорее всего так и есть.

Бенчмарков я не делал, тем более тупой бенчмарк, много раз запускающий функцию setbyte, будет далёк от реальных условий, поскольку многое ещё и зависит от кол-ва используемых регистров, а это скажется лишь на окружающем коде.

Кстати, есть очень хорошая команда x68 rdtsc специально для бенчмарков, чтобы не вызывать много раз функцию для замеров времени. См., например, статью на хабре «Максимально точное измерение кода». А чтобы учесть влияние окружения/на окружение, надо просто включить это самое окружение в измерения, только не слишком большой участок, чтобы результаты для setbyte не попали в пределы погрешности. :-)

aureliano15 ★★
()
Ответ на: комментарий от aureliano15

Кстати, есть очень хорошая команда x68 rdtsc специально для бенчмарков, чтобы не вызывать много раз функцию для замеров времени.

Точно, что-то я и не подумал, что можно так замерять время, причём можно даже не выдумывать искусственные бенчмарки, а просто натыкать rdtsc в конкретную программу (хотя про эту команду я знал, но ни разу не пользовался). Тогда и естественное окружение как раз будет. Но даже так нужно делать аккуратно, rdtsc сама по себе не безобидна, она портит на x86_32 аж два регистра (ну и в любом случае сохранить её значение ещё где-то надо).

gentoo_root ★★★★★
()
Ответ на: комментарий от anonymous_coward

Если так уж не хочется делать маску, то есть std::bitset.

Тоже хотел битсет предложить, но автор хочет манипулировать байтами, а не битами.

DarkEld3r ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.