2012-01-09 6 views
1

У меня недавно возникли проблемы с указателями функций CUSTBACK custm, и это сводилось к использованию соглашений о вызовах, которые решают проблему temporaly, funy CALLBACK действительно хорошо работает, но сигнатура вызывающей функции все еще ошибочна !! и я потратил много времени на то, чтобы найти этот BUG. понял, что caling соглашение позволит вам сделать что-то, что не в порядке, иногда ...Существуют ли стандартные соглашения о вызовах?

Хорошо, что прошлое теперь ... Теперь, я хочу знать, Лил больше о соглашениях о вызове: Visual Studio имеет собственный __cdecl, __thiscall, и т.д. (IIRC).

Является ли стандартный C++ регламентирующим некоторые соглашения о вызовах и как я могу их использовать, если так?


EDIT: Некоторый код, на который я не смог найти ошибку:

class Object; 
    class EventArgs; 

    typedef void(__cdecl Object::*MethodHandler)(Object* sender, EventArgs args); 

///..... this is how I call it..(snapshot) 
(iter->second.sender->*iter->second.memberFunct)(sender, args); 
///... 
    void __cdecl Triger(EventArgs args) //missing "Object* sender" here!!! but it works! 
    { 
     if(args == "test") 
     cout << "test args received" << endl; 
    } 

(. Кстати, имена типов моих пользовательских классов) Это работало просто отлично! Функция была вызвана, но без __cdecl Я получил ошибки регистра ESP.

+0

Какая ошибка? Как правило, жестко закодированный указатель функции для регистрации обработчика/обратного вызова без ошибки компилятора является мертвой датой совершения чего-то неправильного. В приведенном выше коде «Триггер», безусловно, не относится к типу MethodHandler, поскольку аргументы функции различны. – selbie

+0

спасибо, нет, это не та же подпись, но она отлично работает с помощью __cdelc, если я не использую __cdecl, тогда ошибка регистра ESP и прогрм chrush .. очень интересно для меня .. – codekiddy

+0

Это работает, потому что вам повезло с уважением к порядку, в котором аргументы попадают в стек. Трюк cdecl, вероятно, просто массирует стек, чтобы он не разбивался. Где-то в вашем коде вы делаете «(MethodHandler *) Trigger» (или void * cast), чтобы зарегистрировать эту функцию с помощью службы обратного вызова без ошибки компиляции.Если вы исправите свой код таким образом, чтобы код компилировался без приведения, ваш сбой исчезнет. Это означает, что во всем мире одинаковое соглашение о вызове и список одинаковых аргументов. – selbie

ответ

1

Причина, по которой вы столкнулись без __cdecl, заключается в том, что по умолчанию для компилятора Windows используется __stdcall. Последнее заставляет функцию вызова очищать стек. Следовательно, ваш вызывающий пользователь нажимает на два аргумента в стек для вызова «Триггера», но триггер только выдает один аргумент из стека при выходе из функции. Следовательно, вы терпите крах. __cdecl, вызывающий конвенциональный вид, обходит это.

Как я уже говорил в своих комментариях выше, использование __cdecl для исправления сбоя скрывает настоящую ошибку. Список аргументов триггера не соответствует списку аргументов, ожидаемому от «MethodHandler». Вероятно, это причина вашей аварии.

Это, вероятно, лучше исправить:

typedef void(Object::*MethodHandler)(Object* sender, EventArgs args); 

void Triger(Object* sender, EventArgs args) 
{ 
    if(args == "test") 
    cout << "test args received" << endl; 
} 

Вы не разделял код, который показывает, как «Trigger» регистрируется для последующего обратного вызова. Но я сильно подозреваю, вы поставили слепок что-то вроде этого:

MethodHandler handler = (MethodHandler)Trigger; 

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

+0

Хе-хе, да, вы точно знаете, в чем была моя проблема, однако требуется кастинг, я задал вопрос о stackoverflow на этом, но everone сказал, что невозможно сделать то же самое без кастинга ... перепроверьте, что вы отвечаете как самое лучшее! пожалуйста, ознакомьтесь с этим http://stackoverflow.com/questions/8756993/how-to-force-implicit-conversion-in-polymorphism – codekiddy

+0

Если вам нужно направить указатель на элемент из производного класса в базовый класс, используйте static_cast, который позволит вам сохранить некоторую проверку типов. – servn

3

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

+0

На самом деле, C++ * знает * соглашения о вызовах: extern «C» и extern «C++» (последний по умолчанию, и поэтому обычно явно не написан). Однако обычно эти вызывающие соглашения различаются только при изменении имени. – celtschk

+0

благодарю вас за ответ! Я попытаюсь использовать extern «C» и посмотреть, есть ли у него какая-то польза :) – codekiddy

+0

@celtschk: Спасибо, хорошая точка, хотя они называются «спецификациями привязки», а в то время как стандарт оставляет его полностью открытым, что это делает * (и действительно позволяет платформе поддерживать произвольные спецификации), «соглашения о вызовах» (очистка стека, порядок аргументов, регистр и стек и т. д.), которые, по-видимому, заинтересованы в OP, остаются деталями реализации. –

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