Форум программистов, компьютерный форум, киберфорум
Наши страницы
IGPIGP
Войти
Регистрация
Восстановить пароль
Оценить эту запись

Указатель на функцию и функтор

Запись от IGPIGP размещена 05.07.2016 в 22:30
Обновил(-а) SatanaXIII 06.07.2016 в 13:00

Недавно потребовалось передать функтор туда, где ожидается указатель на функцию. Удивительно, но сходу не получается. Оказывается, именем объекта функтора нельзя инициализировать указатель на функцию, несмотря на полностью совпадающие типы возврата и списки формальных параметров.
Прямых указаний в интернете по запросам содержащим "функтор" и "указатель на функцию" найти не удалось, хотя информации и по функторам и по указателям достаточно много. Наиболее близкой для решения задачи, мне показалась информация от AlenaCpp:
http://alenacpp.blogspot.com/2007/04/blog-post.html
Там при упоминании функторов есть вот такой шаблончик
Цитата:
C++
1
2
3
4
5
6
7
template <class Fun>
void foo(Fun f)
{
  //...
  f();
  //...
}
Продемонстрировать результат моих размышлений на простом и искусственном (надуманном) примере можно бы так:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <iostream>
using namespace std;
 
struct Call_back_class
{
 
int a;
int b;
 
Call_back_class(int a_, int b_)
:a(a_), b(b_)
{}
 
Call_back_class(const Call_back_class &rhs)
:a(rhs.a), b(rhs.b), calc_result(rhs.calc_result)
{}
 
int (*calc_result)(Call_back_class &);
 
int calc(){return (*calc_result)(*this);}
 
};
 
template< typename Arg, typename T>
int foo(Arg &n)
{
T f;
return f(n);
}
 
int main(int argc, char* argv[])
{
 
Call_back_class cb(123, 456);
 
struct Plus
{
int operator()(const Call_back_class &cbck_){ return cbck_.a + cbck_.b ;}
};
 
int (*calc_result)(Call_back_class &) = foo<Call_back_class, Plus>;
cb.calc_result=calc_result;
cout<<cb.calc()<<endl;//579
cout<<endl;
 
struct Minus
{
int operator()(const Call_back_class &cbck_){ return cbck_.a - cbck_.b ;}
};
 
cb.calc_result= foo<Call_back_class, Minus>;
cout<<cb.calc()<<endl;//-ззз
cout<<endl;
 
struct Multiply
{
int operator()(const Call_back_class &cbck_){ return cbck_.a * cbck_.b ;}
};
 
cb.calc_result= foo<Call_back_class, Multiply>;
cout<<cb.calc()<<endl;//56088
cout<<endl;
 
system("pause");
return 0;
}

Указатели на функции, построенные как шаблонные обёртки функторов, могут пригодиться не только для реализации обратных вызовов. Вот что удалось выяснить, в случае использования функтора, как предиката (один из самых частых вариантов).
Вот тут, я нашёл интересный пример с размышлением о том, как дорого по производительности может обойтись функтор-предикат, переданный алгоритму сортировки.
https://habrahabr.ru/post/234215/
Речь идёт о конструировании объектов. Вывод о том, что на каждый акт сравнения запускается полтора конструктора не радует.
Однако применив фокус с указателем на функцию запускающую функтор можно сократить количество конструкторов на треть. А если усовершенствовать шаблон снабдив его статическим объектом функтора, то потребуется всего один конструктор независимо от количества сравнений(!!):
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include <iostream>
#include <vector>
#include <algorithm>
 
using namespace std;
 
class Compare
{
public:
    
    Compare()
    {
      cout<<"Compare::Compare()"<<endl;
    }
 
    Compare(const Compare& compare)
    {
      cout<<"Compare::Compare(const Compare& )"<<endl;
    }
 
    ~Compare()
    {
      cout<<"Compare::~Compare()"<<endl;
    }
  
    bool operator() (int v1, int v2)
    {
      cout<<"compare "<<v1<<" "<<v2<<endl;
      return v1 < v2;
    }
};
 
template< typename Arg, typename T>
bool foo(Arg m, Arg n )
{
T f;
return f(m, n);
}
 
template< typename Arg, typename T>
bool fo(Arg m, Arg n )
{
static T f=T();
return f(m, n);
}
 
int main()
{
    vector<int> mas;
 
    for( int k = 0; k <= 3; k++ )    
        mas.push_back( rand() );
    for( int k = 0; k <= 3; k++ )cout << mas[k]<<" ";    
    cout << endl;
 
    //sort( mas.begin(), mas.end(), Compare() );//раскомментируйте этот кошмар...
    //11 конструкторов и деструкторов
 
    //bool (*comp)(int, int)= foo<int, Compare>;//это немного лучше -
    //sort( mas.begin(), mas.end(), *comp );//8 шт. - на каждое сравнение по одному конструктору/деструктору 
 
    bool (*cmp)(int, int)= fo<int, Compare>;//а тут всего один конструктор! :)
    sort( mas.begin(), mas.end(), *cmp );
 
    for( int k = 0; k <= 3; k++ )cout << mas[k]<<" ";    
    cout << endl;
    system("pause");
    return 0;
}
раскомментируйте закоментированные фрагменты по очереди, закомментировав остальные и посмотрите разницу.
Размещено в Без категории
Просмотров 810 Комментарии 5
Всего комментариев 5
Комментарии
  1. Старый комментарий
    Аватар для IGPIGP
    Цитата:
    Потому, что вроде не учтен факт возможного наличия состояния у функтора.
    HighPredator , вопросы как раз острые. Конечно, ты прав, состояние у функтора не учитывается. Оно может быть зафиксировано статическим членом. Перечислением например. Установили ordinal или bysize и режим сравнения строк изменился.
    Тут внимание сконцентрировано на 2-х вопросах:
    1) Информации о создании механизма передачи кода функтора по указателю на функцию в сетке, действительно, не видно (невооруженным глазом). Я потратил достаточно много времени без каких-либо результатов, например. Может искал плохо? Однако, поиск не бывает бесполезен и, в частности, мне попалась статья о затратности применения функторов, имеющих сложные конструктора, при использовании в качестве предикат в алгоритмах сортировки. И отсюда второй вопрос:
    2) Как избежать конструирования (ненужного, обычно) объектов функтора? Я предложил самый простой вариант решения для демонстрации. Думаю, главное не в нём, а в том, чтобы показать проблему, а народ подхватит и решит гораздо лучше.
    Цитата:
    Потому, что я так понимаю, что в какое-нибудь сишное к примеру АПИ, так просто это не прокинуть.
    Дык в Сях же нет функторов. Но если прищуриться, то твой вопрос плодотворен, я думаю. И вот в каком смысле. Си-функции имеют указатели не совместимые с указателями плюсовых функций наличием директивы extern "C". То есть нельзя указателю на функцию С++ присвоить указатель на функцию С. Но можно обернуть вызов функции С через её указатель в функторе и передать указатель на функтор как тут показано.
    Хотя, проще обернуть такой вызов плюсовой функцией.
    Запись от IGPIGP размещена 06.07.2016 в 09:25 IGPIGP вне форума
    Обновил(-а) IGPIGP 06.07.2016 в 14:25
  2. Старый комментарий
    Аватар для IGPIGP
    Что касается передачи указателя на плюсовый метод в С код, то тут о таком речи не шло. Тут только о том, что касается указателя на функцию C++, как такового.
    Запись от IGPIGP размещена 06.07.2016 в 09:41 IGPIGP вне форума
    Обновил(-а) IGPIGP 06.07.2016 в 09:55
  3. Старый комментарий
    Аватар для IGPIGP

    Не по теме:

    Ответы плодятся как кош... конструкторы функторов. Но как удалить лишнее сообщение не понимаю. :wall:

    Запись от IGPIGP размещена 06.07.2016 в 14:01 IGPIGP вне форума
    Обновил(-а) IGPIGP 06.07.2016 в 14:09
  4. Старый комментарий
    Аватар для IGPIGP
    Цитата:
    Благодарю за пояснения
    HighPredator, ты преувеличиваешь мои возможности, но всё равно, спасибо.
    Запись от IGPIGP размещена 06.07.2016 в 21:34 IGPIGP вне форума
  5. Старый комментарий
    Аватар для IGPIGP
    Цитата:
    Сообщение от Somebody Просмотреть комментарий
    На дворе 2016-й год. Лямбды, лямбды...
    Somebody, не понял вашей реплики. Что на дворе? Вы не понимаете, что любой функциональный объект, независимо от того функтор это или лямбда, требует создания объекта? Или может быть вы несогласны с тестом производительности, который показывает преимущество (огромное) указателя на статический метод-предикат, по сравнению с передачей объекта? Тут ещё std::bind надо бы упомянуть, как отдельный случай "объекто-строения". Я всегда против того чтобы С++ программист думал как жава кодер. Если скорость, - прежде всего, то нужно заглядывать под капот. (имхо). То есть, ваша односложная реплика непонятна.
    Запись от IGPIGP размещена 13.03.2019 в 14:53 IGPIGP вне форума
    Обновил(-а) IGPIGP 14.03.2019 в 21:12
 
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2019, vBulletin Solutions, Inc.
Рейтинг@Mail.ru