2015-10-19 1 views
0

У меня есть такой код в C++Реализовать функцию из C++ в C# (MAKE_HRESULT - функция Windows)

#define dppHRESULT(Code) \ 
    MAKE_HRESULT(1, 138, (Code)) 

     long x = dppHRESULT(101); 

результата является й = -2138439579.

MAKE_HRESULT является функцией окна и определяется как

#define MAKE_HRESULT(sev,fac,code) \ 
    ((HRESULT) (((unsigned long)(sev)<<31) | ((unsigned long)(fac)<<16) | ((unsigned long)(code)))) 

мне нужно повторить это в C#. Так что я написал этот код:

public static long MakeHResult(uint facility, uint errorNo) 
     { 
      // Make HR 
      uint result = (uint)1 << 31; 
      result |= (uint)facility << 16; 
      result |= (uint)errorNo; 

      return (long) result; 
     } 

И позвони, как:

// Should return type be long actually?? 
    long test = DppUtilities.MakeHResult(138, 101); 

Но я получаю другой результат, test = 2156527717.

Почему? Может кто-то, пожалуйста, помогите мне повторить эту функцию C++ также в C#? Так что я получаю аналогичный вывод на аналогичных входах?


Альтернативная реализация.

Если я использую эту реализацию

public static long MakeHResult(ulong facility, ulong errorNo) 
     { 
      // Make HR 
      long result = (long)1 << 31; 
      result |= (long)facility << 16; 
      result |= (long)errorNo; 

      return (long) result; 
     } 

это работает на входе 101. Но если я вход -1, то C++ возвращает -1 как результат в то время как C# возвращает 4294967295. Почему?

Я бы очень признателен за помощь, поскольку я застрял с ней.

ответ

1

Я переписал функцию как эквивалент C#.

static int MakeHResult(uint facility, uint errorNo) 
{ 
    // Make HR 
    uint result = 1U << 31; 
    result |= facility << 16; 
    result |= errorNo; 

    return unchecked((int)result); 
} 

C# является более строгим о подписанном/без знака преобразования, в то время как исходный код C не обращал внимания на это. Смешение подписи и неподписанных типов обычно приводит к головным болям.

Как Бен Вейгт упоминает в своем ответе, существует различие в названии типов между двумя языками. long в C на самом деле int в C#. Оба они относятся к 32-битным типам.

U в 1U означает «это целое число без знака». (Краткое обновление: подписанные типы могут хранить отрицательные числа, неподписанные типы не могут.) Вся арифметика в этой функции выполняется без знака, а окончательное значение просто переносится на знаковое значение в конце. Это ближайшая аппроксимация исходного макроса C.

unchecked требуется здесь, потому что в противном случае C# не позволит вам преобразовать значение, если оно выходит за пределы целевого типа, даже если бит идентичны. Переключение между подписанным и unsigned обычно требует этого, если вы не возражаете, что значения отличаются, когда вы имеете дело с отрицательными числами.

+0

Не могли бы вы уточнить, что и почему вы изменили? Кажется, эта функция работает, но я не понимаю, почему .. Я буду продолжать ее тестировать. Но на -1 он также возвратил -1 как C++ один –

+0

Конечно. Я решил ради простоты, чтобы вся арифметика оставалась без знака. 'var result = 1U << 31' дает нам тип' uint' из-за суффикса U после значения. Мы только бросаем в конце функции. Поскольку C# будет бросать подгонку, если вы попытаетесь преобразовать целое число без знака в подписанное, если значение вне диапазона (даже если бит идентичны), мы должны использовать 'unchecked', когда мы это делаем. Оба типа ввода - 'uint', потому что они соответствуют типу C' unsigned long'. – SaxxonPike

+1

Спасибо, пожалуйста, просто добавьте это объяснение (возможно, другое также потребуется) вместо ответа. Также, если вы еще раз подчеркнете, почему мой предыдущий подход не работал, это также будет приятно. Как и для новичков, которые не понимают этих сдвигающихся и подписанных против неподписанных проблем. Затем через несколько раз я буду принимать его также, если он не получает плохих голосов –

0

Вы рассчитываете его в неподписанном типе (uint). Поэтому сдвиги будут вести себя соответственно. Попробуйте вместо этого использовать int и посмотрите, что произойдет.

Ключ здесь в том, что 2156527717 как неподписанный int такой же, как -2138439579 как подписанный int. Они буквально одни и те же.

+0

Да, но C++ также делает бросок на 'unsigned long' внутри макроса, не так ли? например здесь (unsigned long) (fac) << 16)? Так почему я должен использовать int? –

+0

Потому что вы никогда не получите отрицательное число, используя неподписанные типы. Однако вы не получаете 'unsigned long' из макроса. Он передается в 'HRESULT', который на самом деле является 32-битным типом. Когда вы затем передаете его на 'long long', он интерпретирует это 32-битное значение как подписанное, а затем делает преобразование, давая вам отрицательное число. – SaxxonPike

+0

Не могли бы вы увидеть мой обновленный вопрос (вторая часть). Я не могу заставить его соответствовать на входе -1 тоже, после вашего предложения. Где я должен использовать int? Не могли бы вы рассказать? и посмотреть мое обновление? –

1

В компиляторах Windows C++ long - 32 бит. В C# long - 64-разрядный. Ваше преобразование C# этого кода не должно содержать ключевое слово типа long.

SaxxonPike предоставил правильный перевод, но в его пояснении (-ях) отсутствует эта важная информация.

Ваш промежуточный результат - это 32-разрядное целое без знака. В версии C++ приведение выполняется под 32-битовое значение, что приводит к тому, что высокий бит интерпретируется как знаковый бит. Код SaxxonPike делает это также. Результат отрицательный, если промежуточное значение имеет самый старший бит.

В исходном коде в вопросе листинг относится к 64-битной подписанной версии, которая сохраняет старый старший бит как нормальную двоичную цифру и добавляет новый бит знака (всегда ноль). Таким образом, результат всегда положителен. Несмотря на то, что низкие 32-биты точно соответствуют 32-битовому результату в C++, в версии C#, возвращающей long, то, что будет битом знака в C++, не рассматривается как бит знака.

В новой попытке в вопросе происходит то же самое (бит знака в 64-битном номере всегда равен нулю), но это происходит в промежуточных вычислениях, а не в конце.

+0

Да, я бы оценил в целом, если кто-то может объяснить, что там происходит и почему мои предыдущие попытки не работали –

+0

@Quser: Я разработал, это помогает? –

+0

Я прочитаю его позже. спасибо, что я не могу повышать свою репутацию. Я отвечу на другой ответ, так как вы говорите также, что все правильно. Благодарю. Я не уверен, теперь я получаю объяснение, но надеюсь, что когда я прочитаю это лучше (если вы можете упростить, было бы хорошо, иначе это так) –

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