[VC11][Bug] Функция принимающая функцию
От: Alexander G Украина  
Дата: 08.12.15 08:01
Оценка:
При передаче фукнции в другую функцию, может быть соблазн использовать не тип указателя на функцию, а тип функции. Должен же приводиться.

Так вот, в некоторых сценариях студия (проверено на MSVC 2012) падает, пытаясь разыменовать инструкции функции.

Воспроизводится как с объявлением функции-параметра напрямую, так и через typedef.

Был способ и без лябмды, но не получается его легко рафинировать.

Решение — не выделываться, объявлять через указатель.

#include <algorithm>
#include <iostream>

static void c(int v) { std::cout << v; };

//void f(int* a, size_t count, void (*callback)(int))  //- так не падает
void f(int* a, size_t count, void callback(int))
{
    std::for_each(
        a,
        a + count,
        [callback] (int v) { callback(v); } );
}

int main()
{
    int x[5] = {};
    f(x, 5, c);
    return 0;
}
Русский военный корабль идёт ко дну!
Re: [VC11][Bug] Функция принимающая функцию
От: T4r4sB Россия  
Дата: 08.12.15 08:13
Оценка: 8 (1)
Хм, проверил в студии-2003.

Так падает:


  Скрытый текст
template <typename F>
void Test (const F& f)
{
    int i=5;
    f(i);
}

void Foo(int i)
{
    printf("%i\n", i);
}

void Bar(void callback(int))
{
    Test(callback);
}

int main ()
{
    
    Bar(Foo);

    getchar();

    return 0;
}



а так не падает:
  Скрытый текст
template <typename F>
void Test (F f)
{
    int i=5;
    f(i);
}

void Foo(int i)
{
    printf("%i\n", i);
}

void Bar(void callback(int))
{
    Test(callback);
}

int main ()
{
    
    Bar(Foo);

    getchar();

    return 0;
}
Отредактировано 08.12.2015 8:16 T4r4sB . Предыдущая версия .
Re[2]: [VC11][Bug] Функция принимающая функцию
От: Alexander G Украина  
Дата: 08.12.15 08:19
Оценка:
Здравствуйте, T4r4sB, Вы писали:

TB>Хм, проверил в студии-2003.


Да, в 2012 то же самое на этих примерах.
Русский военный корабль идёт ко дну!
Re[3]: [VC11][Bug] Функция принимающая функцию
От: CEMb  
Дата: 08.12.15 08:30
Оценка:
Здравствуйте, Alexander G, T4r4sB, Вы писали:

TB>>Хм, проверил в студии-2003.


AG>Да, в 2012 то же самое на этих примерах.


оно честно пытается разыменовать ссылку, как указатель:

ms cl said:
00411BFB  mov         ecx,dword ptr [f] 
00411BFE  mov         edx,dword ptr [ecx] 
00411C00  call        edx

что-то тут недодумано?
Re[3]: [VC11][Bug] Функция принимающая функцию
От: T4r4sB Россия  
Дата: 08.12.15 08:31
Оценка:
А так не падает:
  Скрытый текст

template <typename F>
void Test (const F& f)
{
    int i=5;
    f(i);
}

void Foo(int i)
{
    printf("%i\n", i);
}

void Bar(void callback(int))
{
    Test(&callback);
}

int main ()
{
    
    Bar(Foo);

    getchar();

    return 0;
}


Короче, попробуй так:
void f(int* a, size_t count, void callback(int))
{
    std::for_each(a, a + count, &callback);
}


Блин, боюсь на таких вопросах завалить теорию. Все эти неявные касты "сущность==указатель на неё" — такая муть...
Re[4]: [VC11][Bug] Функция принимающая функцию
От: CEMb  
Дата: 08.12.15 09:31
Оценка:
Здравствуйте, T4r4sB, Вы писали:

TB>А так не падает:


TB>void Bar(void callback(int))

TB>{
TB> Test(&callback);
TB>}

потому что ты адрес передаёшь, там он там разыменовывается нормально в адрес функции.

в общем, странно, объясните кто-нибудь:

ms cl said:
    int i = 0;
00411ECE  mov         dword ptr [i],0 
    AA(i);
00411ED5  lea         eax,[i] 
00411ED8  push        eax  
00411ED9  call        AA (4112A3h) 
00411EDE  add         esp,4 
    Test(&callback);
00411EE1  lea         eax,[callback] 
00411EE4  push        eax  
00411EE5  call        Test<void (__cdecl*)(int)> (41129Eh) 
00411EEA  add         esp,4 
    Test(callback);
00411EED  mov         eax,dword ptr [callback] 
00411EF0  push        eax  
00411EF1  call        Test<void (__cdecl*)(int)> (41129Eh) 
00411EF6  add         esp,4


в случае вызова с передачей параметра по ссылке, пушится указатель на переменную (00411ED8) потом внутри раскручивается.
во втором вызове мы самы пушим указатель, он внутри раскручивается, всё ок.
в третьем случае мы передаём ссылку на функцию, но компилятор её тут передаёт, как есть (00411EED), а в вызове почему-то раскручивает, как указатель. ЧМДНТ? Почему в третьем вызове компилятор не заворачивает параметр в указатель, хотя знает, что передача по ссылке?
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.