Безопасное преобразование между беззнаковым и подписанным int

У меня есть интерфейс между клиентом и сервером, где клиент отправляет (1) значение без знака и (2) флаг, который указывает, подписано ли значение / нет. Затем сервер будет статически приводить беззнаковое значение к соответствующему типу.

Позже я узнал, что это поведение, определяемое реализацией, и я читал об этом, но не мог найти подходящего решения, которое было бы полностью безопасным? Я читал о каламбурах типов, преобразованиях указателей и memcpy.

Сработает ли просто использование профсоюзного типа? UnionType, содержащий подписанные и неподписанные int, а также флаг подписанный / беззнаковый. Для подписанных значений клиент устанавливает подписанную часть объединения, а сервер читает подписанную часть. То же самое для беззнаковой части. Или я что-то совсем не понимаю?

Дополнительный вопрос: как мне узнать конкретное поведение в этом случае для конкретного сценария, например. windriver diab на ппц? Я немного не понимаю, как найти такую ​​документацию.


person polemic    schedule 13.06.2014    source источник
comment
Если вы не столкнетесь с системой, использующей что-то вроде дополнения или знаковой величины, я думаю, что все, что вы используете, будет определять преобразование без знака в ›знак таким же образом.   -  person user2357112 supports Monica    schedule 13.06.2014
comment
Что должно произойти, когда клиент отправляет беззнаковое значение, но отправляет подписанный флаг?   -  person M.M    schedule 13.06.2014
comment
Также как ваш код обрабатывает подписанный флаг? (поскольку C ++ имеет систему статических типов, вам необходимо назначить свои типы во время компиляции)   -  person M.M    schedule 13.06.2014
comment
@ user2357112, означает ли это, что для систем с двумя дополнениями static_cast ‹int› (static_cast ‹unsigned int› (x)) == x всегда будет верным?   -  person polemic    schedule 13.06.2014
comment
@MattMcNabb, в идеале сервер будет static_cast ‹int› (unsigned_value) и получит ожидаемое значение со знаком. или вы имели в виду случай, когда отправляется тип объединения? подписанный флаг - это просто булево, не уверен, правильно ли я понял ваш второй вопрос - полемика только сейчас отредактируйте   -  person polemic    schedule 13.06.2014
comment
@polemic, который вызывает именно ту проблему, которую вы пытаетесь избежать (поведение, определяемое реализацией), выполняя static_cast ‹int› для беззнакового значения, выходящего за пределы диапазона int. Вы контролируете и клиента, и сервер?   -  person M.M    schedule 13.06.2014
comment
@polemic: я был бы очень удивлен, если бы увидел, что он ведет себя иначе (при условии, что x - это int). Однако у меня нет большого опыта работы с разными системами и компиляторами, и иногда операции определяются странным образом. Величина знака и дополнение до одного - это случаи, когда есть очевидная причина, по которой преобразование ведет себя по-другому, но, возможно, вы столкнетесь с системой дополнения до двух, которая должна имитировать поведение старой системы дополнения или что-то в этом роде.   -  person user2357112 supports Monica    schedule 13.06.2014
comment
Интерфейс @MattMcNabb все еще обсуждается с командой клиентов, так что да, у меня есть какой-то контроль. Я имел в виду, что static_cast из unsigned в signed было поведением по умолчанию, которое мы пытались изменить. Предлагаемое решение (я отредактировал свой пост выше) не будет использовать такие приведения, только если условия   -  person polemic    schedule 13.06.2014


Ответы (2)


Решение union потребует, чтобы и клиент, и сервер использовали одно и то же представление, что не является хорошей идеей. (например, обе системы с прямым порядком байтов и прямым порядком байтов являются общими).

Вы должны каким-то образом сериализовать значение. Но вы просто делаете это отдельно для каждого типа.

Например (псевдокод)

void send_signed(int32_t x)
{
    send(1, "s");
    send(sizeof x, serialized x);
}

void send_unsigned(uint32_t x)
{
     send(1, "u");
     send(sizeof x, serialized x);
}

И на ресивере; вы сначала читаете шрифт. Если это "s", то вы десериализуете подписанный int и что-то с ним; и если это "u", вы десериализуете беззнаковое int (в другую переменную) и что-то с ним делаете.

person M.M    schedule 13.06.2014
comment
Есть ли что-нибудь, что нужно сделать пользователю с порядком байтов на стороне клиента и сервера? - person R Sahu; 13.06.2014
comment
Если бы мы предположили, что порядок байтов не является проблемой, сработало бы решение объединения? - person polemic; 13.06.2014
comment
@ Мэтт Макнабб, что требуется для serialized x? Пока отправляются буквы 's' или 'u', единственное требование, которое я мог найти, - это преобразование int x в (unsigned int) x, скажем unsigned int xmit = int whatever и отправка. Затем на принимающей стороне, если установлено 's', int rcvd = (int)xmit позаботится о подписи. Пример: int xmit = -5678 отправляется как: `11111111111111111110100111010010` после получения с установленным 's' приведение int rcvd = (int)xmit восстанавливает правильный знак на другом конце: -5678. Мне что-то еще не хватает? (случаи x > 2,147,483,647 не будут работать, но? - person David C. Rankin; 13.06.2014
comment
сериализованный означает сохранение его в данных без потерь и с возможностью восстановления; например Я бы сохранил 4-байтовое целое число в качестве наиболее значимого байта первым на обоих концах, но это не имеет особого значения, если вы можете восстановить его на другом конце. - person M.M; 14.06.2014

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


Взгляните на статью в Википедии о UNIVAC, чтобы увидеть пример платформы, в которой они дополняют представление был использован, например для 12- и 36-битных слов.

person Cheers and hth. - Alf    schedule 13.06.2014