Модераторы: Daevaorn
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Указатель на функцию класса, Указатель на функцию класса  
:(
    Опции темы
Merlin_ua
Дата 13.8.2014, 22:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 44
Регистрация: 22.11.2006

Репутация: нет
Всего: нет



Пример:
Код

class a {
public:
    a(int n);
    ~a();
    void (*f1)();
    void f2();
    void f3();
};
a::~a(){};
a::a(int n)
{
    f1 = 0;
    if(n == 1)
        f1 = а2;
    if (n == 2)
        f1 = f3;
}
void a::f2()
{
    cout << "f2\n";
}
void a::f3()
{
    cout << "f3\n";
}


Пример сильно упрощен. Суть - реализовать динамическую подмену метода. Нужно в зависимости от значения, передаваемого в конструкторе, присоить f1 указатель на одну из функцию-метод этого же класса. Естественно код не работает. Как решить подобную задачу?
PM MAIL   Вверх
Lukkoye
Дата 13.8.2014, 23:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 86
Регистрация: 23.3.2013

Репутация: 1
Всего: 1



http://rextester.com/KGK12631

Код

//Title of this code

#include <iostream>
using namespace std;

struct a {
    a(int n)
        :f(nullptr)
    {
        if(n == 1)
            f = &a::f2;
        else if (n == 2)
            f = &a::f3;
    }
    
    void launch() { (this->*f)(); }
    
    void f2() { cout << "f2\n"; }
    void f3() { cout << "f3\n"; }
    
    void(a::*f)();
};


int main()
{
    std::cout << "Hello, world!\n";
    
    a foo(1);
    foo.launch();    //<---- внутреннее связывание
    (foo.*foo.f)();  //<---- внешнее связывание
    
    std::cout << "----------------\n";
    
    a baz(2);
    baz.launch();
    (baz.*baz.f)();
}


Так же рекомендую ознакомиться:
http://forum.vingrad.ru/forum/topic-378684.html

Это сообщение отредактировал(а) Lukkoye - 13.8.2014, 23:29
PM MAIL   Вверх
Cheloveck
Дата 14.8.2014, 00:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1578
Регистрация: 26.7.2008
Где: Тула

Репутация: 3
Всего: 32



Можно юзать паттарн strategy, если не пугает небольшой оверхед с использованием виртуальных вызовов.

Ещё есть кашерный способ с использованием шаблонных функторов. Правда, не всегда можно использовать.

Код

#include <iostream>

struct PrintHello
{
    void operator () ()
    {
        std::cout << "Hello";
    }
};

struct PrintWorld
{
    void operator () ()
    {
        std::cout << "World";
    }
};

template<typename Func>
class FooBar
{
public:
    void print()
    {
        func();
    }

private:
    Func func;
};

int main()
{
    FooBar<PrintHello> hello;
    FooBar<PrintWorld> world;
    hello.print();
    std::cout << std::endl;
    world.print();
    std::cout << std::endl;
    return 0;
}



--------------------
user posted image
PM Jabber   Вверх
Lukkoye
Дата 14.8.2014, 23:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 86
Регистрация: 23.3.2013

Репутация: 1
Всего: 1



Цитата(Cheloveck @  14.8.2014,  00:35 Найти цитируемый пост)
Ещё есть кашерный способ с использованием шаблонных функторов. Правда, не всегда можно использовать.


Ваш способ не кошерный по ряду причин:

1. Нет ни какой необходимости увеличивать размер объекта ---> мембер не нужен.
2. Оверхед ---> агрегат запускает функцию, которая запускает функцию мембера ---> это избыточный оверхед в данном случае.

3. функция мембера никак не связанна с данными агрегата ---> должна быть const ---> можно заменить на свободную функцию.

итого, вот вам более эффективная модель, лишенная этих досадных недостатков:

http://rextester.com/KYRE18162

Код

#include <iostream>
using namespace std;

void foo(){ cout << "Free\n"; }


struct PrintHello{ static void foo(){ cout << "Hello\n"; }};
struct PrintWorld{ static void foo(){ cout << "World\n"; }};

template<class Func, Func func >struct FooBar
{
    void print()const{ func();} 
};

#define deduce( f ) decltype(&f), &f

int main()
{
    
    FooBar<      deduce(foo)        >   Free;         Free.print();
    FooBar< deduce(PrintHello::foo) >   Hello;        Hello.print();
    FooBar< deduce(PrintWorld::foo) >   World;        World.print();
 
    return 0;
}


По аналогичной схеме можно работать и с нестатическими функциями-членами классов.

Смысл идеи: передавать функцию, которую нужно запускать через параметр шаблона, а не через аргументы функций, или мемберы классов.

Передаваемые через параметр шаблона функции 100% inline без каких бы то ни было штрафов,  и оверхеда в рантайме. 



Это сообщение отредактировал(а) Lukkoye - 14.8.2014, 23:56
PM MAIL   Вверх
Cheloveck
Дата 15.8.2014, 00:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1578
Регистрация: 26.7.2008
Где: Тула

Репутация: 3
Всего: 32



Цитата(Lukkoye @  15.8.2014,  00:53 Найти цитируемый пост)
1. Нет ни какой необходимости увеличивать размер объекта ---> мембер не нужен.


Это 1 байт
Цитата(Cheloveck @  14.8.2014,  01:35 Найти цитируемый пост)
Func func;

против Ваших 8-ми (на 64-разрядных машинах):
Цитата(Lukkoye @  14.8.2014,  00:28 Найти цитируемый пост)
void(a::*f)();

http://codepad.org/lj2HBuRV


Цитата(Lukkoye @  15.8.2014,  00:53 Найти цитируемый пост)
2. Оверхед ---> агрегат запускает функцию, которая запускает функцию мембера ---> это избыточный оверхед в данном случае.


Смотрим на асм, который выдаёт g++ и не наблюдаем оверхеда
Код

    .file    "test.cpp"
    .section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string    "Hello"
.LC1:
    .string    "World"
    .section    .text.unlikely,"ax",@progbits
.LCOLDB2:
    .section    .text.startup,"ax",@progbits
.LHOTB2:
    .p2align 4,,15
    .globl    main
    .type    main, @function
main:
.LFB1027:
    .cfi_startproc
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    movl    $.LC0, %esi
    movl    $_ZSt4cout, %edi
    call    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
    movl    $_ZSt4cout, %edi
    call    _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
    movl    $.LC1, %esi
    movl    $_ZSt4cout, %edi
    call    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
    movl    $_ZSt4cout, %edi
    call    _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
    xorl    %eax, %eax
    addq    $8, %rsp
    .cfi_def_cfa_offset 8
    ret
    .cfi_endproc
.LFE1027:
    .size    main, .-main
    .section    .text.unlikely
.LCOLDE2:
    .section    .text.startup
.LHOTE2:
    .section    .text.unlikely
.LCOLDB3:
    .section    .text.startup
.LHOTB3:
    .p2align 4,,15
    .type    _GLOBAL__sub_I_main, @function
_GLOBAL__sub_I_main:
.LFB1039:
    .cfi_startproc
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    movl    $_ZStL8__ioinit, %edi
    call    _ZNSt8ios_base4InitC1Ev
    movl    $__dso_handle, %edx
    movl    $_ZStL8__ioinit, %esi
    movl    $_ZNSt8ios_base4InitD1Ev, %edi
    addq    $8, %rsp
    .cfi_def_cfa_offset 8
    jmp    __cxa_atexit
    .cfi_endproc
.LFE1039:
    .size    _GLOBAL__sub_I_main, .-_GLOBAL__sub_I_main
    .section    .text.unlikely
.LCOLDE3:
    .section    .text.startup
.LHOTE3:
    .section    .init_array,"aw"
    .align 8
    .quad    _GLOBAL__sub_I_main
    .local    _ZStL8__ioinit
    .comm    _ZStL8__ioinit,1,1
    .hidden    __dso_handle
    .ident    "GCC: (Debian 4.9.1-1) 4.9.1"
    .section    .note.GNU-stack,"",@progbits


А всё потому, что всё волшебным способом заинлайнилось.


Цитата(Lukkoye @  15.8.2014,  00:53 Найти цитируемый пост)

3. функция мембера никак не связанна с данными агрегата ---> должна быть const ---> можно заменить на свободную функцию.

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

И да
Код

#include <iostream>
#include <functional>

struct PrintHello
{
    void operator () ()
    {
        std::cout << "Hello";
    }
};

struct PrintWorld
{
    void operator () ()
    {
        std::cout << "World";
    }
};

template<typename Func>
class FooBar
{
public:
    FooBar() 
    {
    }
    
    FooBar(Func _func) : func(_func) 
    {
    }
    
    void print()
    {
        func();
    }

private:
    Func func;
};

void freeFunction()
{
    std::cout << "I'm free";
}

int main()
{
    FooBar<PrintHello> hello;
    FooBar<PrintWorld> world;
    FooBar<std::function<void()>> free_func(freeFunction);
    hello.print();
    std::cout << std::endl;
    world.print();
    std::cout << std::endl;
    free_func.print();
    std::cout << std::endl;
    return 0;
}

http://ideone.com/Pn1xQ2


Это сообщение отредактировал(а) Cheloveck - 15.8.2014, 00:50


--------------------
user posted image
PM Jabber   Вверх
Lukkoye
Дата 15.8.2014, 01:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 86
Регистрация: 23.3.2013

Репутация: 1
Всего: 1



Цитата(Cheloveck @  15.8.2014,  00:25 Найти цитируемый пост)
Это 1 байт


Который можно было бы и не тратить совершенно зазря и на пустом месте.

Цитата(Cheloveck @  15.8.2014,  00:25 Найти цитируемый пост)
против Ваших 8-ми (на 64-разрядных машинах):


Против каких моих 8ми?
В моем примере вообще нет мемберов. 


Цитата(Cheloveck @  15.8.2014,  00:25 Найти цитируемый пост)
Смотрим на асм, который выдаёт g++ и не наблюдаем оверхеда


inline это конечно хорошо, только не факт в вашем случае. в случае с параметром шаблона - факт.


Цитата(Cheloveck @  15.8.2014,  00:25 Найти цитируемый пост)
Не совсем понятно предложение. Но исходя из вышеперечисленных соображения и возможность оборачивать свободные функции в структуры макросом, не считаю этот пункт хоть сколько-нибудь значащим.


boost::function/boost::bind с вами не согласны.


Было время, я рассматривал различные варианты реализаций делегатов на плюсах (с++03).
И собственный велосипед так же имеется.

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

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

Вместо этого делалась статическая связка, путем передачи указатель на функцию-член через параметр шаблона.
Что и делало его столь быстрым в работе.

Цитата(Cheloveck @  15.8.2014,  00:25 Найти цитируемый пост)
И да


Ваше "и да" ---> std::function итак уже является тонкой оберткой над функторами. Высоко технологичное, черезвычайно быстрое средство запуска удаленных функций.

Факт его использования делает ненужной вашу собственную обертку в принципе.
Она получается избыточным велосипедом с квадратными колесами.


Другое дело сам std::function.

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

Ваша обертка в этом смысле прибита гвоздями к operator() с определенной сигнатурой.


Теперь попробуйте нарисовать модель, которую подобно std::function можно нацеливать на самые разные функции, и биндить аргументы.

Только имейте ввиду - если для классов, вы будете отдельно сохранять объект, и отдельно указатель на функцию-член, и делать динамическую связку - вы просадите рантайм, и ваш велосипед никогда по скорости не догонет std::function/std::bind.




Это сообщение отредактировал(а) Lukkoye - 15.8.2014, 01:20
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0485 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.