В чем преимущество / использование __stdcall? - PullRequest
3 голосов
/ 10 октября 2019

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

Но я до сих пор не могу понять преимущества и случаи, когда я должен использовать __stdcall.

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

OpcUa_StatusCode __stdcall opcua_client::onShutdownMessage(OpcUa_Handle hApplication, OpcUa_Handle hSession, OpcUa_String strShutdownMessage, void* extraParam)
{
    opcua_client* pOpcClient = NULL;
    if (extraParam)
    {
        try
        {
            pOpcClient = qobject_cast <opcua_client*>((QObject*)extraParam);
            throw(55);
        }
        catch (int exeption)
        {

        }
    }

    OpcUa_StatusCode uStatus = OpcUa_Good;

    QString strShutDownMsg = QString::fromUtf8(OpcUa_String_GetRawString(&strShutdownMessage));
    bool bOK;
    OpcUa_StatusCode uClientLibStatus = uClientLibStatus = strShutDownMsg.toUInt(&bOK, 16);

    if (pOpcClient)
    {
        if (uClientLibStatus > 0)
            pOpcClient->process_onShutdownMessage(uClientLibStatus);
    }
    else
    {
        return uStatus;
    }

    return uStatus;
}

Ответы [ 2 ]

3 голосов
/ 10 октября 2019

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

Кроме этого, stdcall предназначен для уменьшения размера кода по сравнению с cdecl путем перемещения кода очистки аргумента от вызывающего к вызываемому (который может использоватьret imm16 на x86).

Это старая микрооптимизация для архитектуры IA-32. MSVC принимает ключевое слово __stdcall и на других архитектурах, но ничего не делает там .

Проверьте этот пример:

__declspec(noinline) int sum(int a, int b)
{
    return a + b;
}

int add1(int a)
{
    return sum(a, 1);
}

MSVC компилирует это в:

int sum(int,int) PROC
  mov eax, DWORD PTR _a$[esp-4]
  add eax, DWORD PTR _b$[esp-4]
  ret 0 ; this instruction will be replaced
int sum(int,int) ENDP

int add1(int) PROC
  push 1
  push DWORD PTR _a$[esp]
  call int sum(int,int)
  add esp, 8 ; this instruction will be removed
  ret 0
int add1(int) ENDP

И версия с stdcall:

__declspec(noinline) int __stdcall sum(int a, int b)
{
    return a + b;
}

int add1(int a)
{
    return sum(a, 1);
}

, которая компилируется в:

int sum(int,int) PROC
  mov eax, DWORD PTR _a$[esp-4]
  add eax, DWORD PTR _b$[esp-4]
  ret 8 ; 0 argument replaced with 8
int sum(int,int) ENDP

int add1(int) PROC
  push 1
  push DWORD PTR _a$[esp]
  call int sum(int,int)
  ret 0 ; extra instruction is missing here
int add1(int) ENDP

Обратите внимание, как во 2-м примере используется ret 8 и вызывающая сторонане имеет дополнительных add esp, 8.

2 голосов
/ 10 октября 2019

opcua_client::onShutdownMessage выглядит как функция обратного вызова. То есть функция, которая отправляется некоторому API, который вызывает функцию позднее. Функция обратного вызова должна тогда иметь соглашение о вызовах, ожидаемое API, в данном случае __stdcall. Поскольку __stdcall является соглашением о вызовах по умолчанию на платформе Win32, его не нужно указывать при сборке для Win32.

...