mingw - cdecl необходим для правильного запуска функций - PullRequest
0 голосов
/ 19 июня 2020

Приведенный ниже код работает правильно с любым онлайн-компилятором g cc, который я нашел (g cc 9.2.0), он также правильно работает с компилятором CYGWIN g cc, но, к сожалению, он не работает с Компилятор MINGW g cc - похоже, он передает недопустимый параметр как "this" методам "methodA" и "methodB", когда они вызываются, вместо ожидаемых результатов (56,58) я получаю несколько случайных больших чисел.

#include <iostream>
using namespace std;

class CallbackBase
{
    public:
    using METHOD_TYPE = int  (CallbackBase::*)(...);
};  

class CallbackProvider : public CallbackBase
{
public:
    int methodA(int a,int b,int c)
    {
        return a+b+c+d;
    }
    int methodB(double a,double b,double c)
    {
       return a+b+c+d;
    }
    private:
    int d=8;

};

class CallbackRunner
{
public:

               CallbackBase::METHOD_TYPE m_method;
               CallbackBase* m_this;

               void install (CallbackBase* _this, CallbackBase::METHOD_TYPE _method)
               {
                              m_method =_method;
                              m_this =_this;
               }
               int Run1()
               {
                   return (m_this->*m_method)(15L,16L,17L);
               }
               int Run2()
               {
                   return (m_this->*m_method)(15.6,16.7,17.8);
               }
};

int main()
{
    CallbackProvider    cp;
    CallbackRunner      cr;

    cr.install(&cp,(CallbackBase::METHOD_TYPE)&CallbackProvider::methodA);
    cout << "result " << cr.Run1() << endl;
    cr.install(&cp,(CallbackBase::METHOD_TYPE)&CallbackProvider::methodB);
    cout << "result " << cr.Run2() << endl;
               return 0;
}

Проблема решается, если я добавляю к этим методам атрибут __cdecl:

int __cdecl methodA(int a,int b,int c)
int __cdecl methodB(double a,double b,double c) 

Я не использую -mrtd флаг компиляции . Согласно this , __cdecl должен быть соглашением о вызовах по умолчанию для компиляторов g cc, но похоже, что это не так для MINGW.

Возможно ли установить __cdecl в качестве соглашения о вызовах по умолчанию для моего проекта? или в качестве альтернативы, есть ли способ установить атрибут «по умолчанию» для всех методов?

Я использую Windows 10 с 64-битной архитектурой.

Ответы [ 2 ]

0 голосов
/ 19 июня 2020

Вам не разрешается использовать указатель на функцию с параметром C -style variable c для вызова функций с обычными параметрами. Есть причина, по которой ваши cr.install вызовы не работают без приведения указателя.

Самое простое решение - привести указатель обратного вызова к правильному типу перед его вызовом на основе аргументов, с которыми вы хотите его вызвать. . Вы можете написать шаблон, который будет выполнять приведение за вас.

Однако это очень небезопасно, поскольку легко привести к неправильному типу. (Ваш код был бы столь же небезопасным, если бы он не был определен с самого начала.)

Более безопасный подход - сохранить указатель функции в std::any. Затем попытка обратного вызова с недопустимыми параметрами вызовет ошибку времени выполнения (то есть std::any_cast обнаружит несоответствие типа параметра).

0 голосов
/ 19 июня 2020

В вашем коде есть ошибка - поведение не определено. Приведение от int (Class::*)(int, int, int) к int (CallbackBase::*)(...) запускает его. Эти два типа не совпадают, и вы не можете волей-неволей выполнять приведение между ними.

Это фрагмент вашего кода, в котором вы пытаетесь выполнить это недопустимое преобразование:

cr.install(&cp,(CallbackBase::METHOD_TYPE)&CallbackProvider::methodA);

You вы можете легко увидеть диагностическое сообщение c самостоятельно, если удалите приведение:

ошибка: невозможно преобразовать 'int (CallbackProvider :: *) (int, int, int)' в 'CallbackBase :: METHOD_TYPE '{aka' int (CallbackBase :: *) (...) '}

Тот факт, что он работает на одних компиляторах, а не на других, не имеет значения, это просто проявление неопределенного поведения.

Вы можете вернуться к исходному типу функции перед ее вызовом, но тогда все это станет еще более уродливым.

Другой вариант - сделать ваши конкретные функции также varadi c и доступ к параметрам через VA_ARGS. Это отбросит всю безопасность типов C ++ за пределы окна, и мне тоже не нравится этот подход. на самом деле довольно устаревший) для вариативных c функций, в то время как другие компиляторы будут использовать современный AMD ABI.

...