Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Передача функции или метода в кач-ве параметра


Автор: nerezus 30.4.2007, 18:51
Есть некоторый класс. В одном из его методов он должен принимать функцию, в другом вызывать ее.
1) Функция. Она всегда имеет параметры определенного типа. Допустим структуру st.
Один из методов моего класса получает эту функцию(как?) и запоминает в свойство(с каким типом?), а другой метод вызывает ее(как?) с параметром в виде структуры.

2) То же самое, но с методом объекта, а не с функцией. Класс объекта заранее не известен.

Автор: vinter 30.4.2007, 19:03
Цитата(nerezus @  30.4.2007,  18:51 Найти цитируемый пост)
1) Функция. Она всегда имеет параметры определенного типа. Допустим структуру st.Один из методов моего класса получает эту функцию(как?) и запоминает в свойство(с каким типом?), а другой метод вызывает ее(как?) с параметром в виде структуры.

Код

typedef int (*pFunction)(int);
int SomeFunc(int nVar)
{
     return nVar;
}
void function(pFunction pF);
........................
function(SomeFunc);

тебе это надо??

Автор: archimed7592 30.4.2007, 19:13
для метода:
Код
struct A
{
    void Fun (int, int) {}
};
typedef void (A::* pFun)(int, int);
pFun f = &A::Fun;
A a;
a.*f (1, 2);

совет тебе добрый: многим лучше использовать обощённые функторы...

Автор: Greeen 30.4.2007, 19:15
Код

#include "stdafx.h"

struct MyStruct
{
    int number;
    char ch;

    MyStruct(int Number, char Char) : number(Number), ch(Char) {}
};

typedef void(*MyFunc)(const MyStruct& Structure);

class MyClass
{
private:
    MyFunc m_func;

public:
    MyClass() {}
    void SetFunc(MyFunc Function) { m_func = Function; }
    void CallFunc(MyStruct Structure) { m_func(Structure); }
};

void TestFunction1(const MyStruct& Structure)
{
    std::cout << Structure.number << ' ' << Structure.ch;
}

void TestFunction2(const MyStruct& Structure)
{
    std::cout << Structure.ch << ' ' << Structure.number;
}

int _tmain(int argc, _TCHAR* argv[])
{
    MyStruct my_struct(10, 'A');
    MyClass my_class;
    my_class.SetFunc(TestFunction1);
    my_class.CallFunc(my_struct);
    my_class.SetFunc(TestFunction2);
    my_class.CallFunc(my_struct);
    return 0;
}

Автор: Mayk 30.4.2007, 19:17
Цитата(archimed7592 @  30.4.2007,  23:13 Найти цитируемый пост)

совет тебе добрый: многим лучше использовать обощённые функторы... 

А учитывая
Цитата(nerezus @  30.4.2007,  22:51 Найти цитируемый пост)

2) То же самое, но с методом объекта, а не с функцией. Класс объекта заранее не известен.

то другого приемлимого выхода собственно и не остаётся.

Автор: nerezus 30.4.2007, 19:24
Спасибо, заработало: 
Код

typedef void (*pFunction)(SDL_KeyboardEvent *key);

void function(pFunction pF, SDL_KeyboardEvent *key) {
    pF(key);
}

//PrintKeyInfo(&e->event.key);
function(PrintKeyInfo, &e->event.key);


А про методы ничего нету? )

Код

this.button1.Click += new System.EventHandler(this.button1_Click);
 А вот такого ничего нету? )

Добавлено через 2 минуты и 55 секунд
оо, сколько ответов.

Цитата

многим лучше использовать обощённые функторы...
 мм, это как?

Добавлено через 6 минут и 11 секунд
Цитата

pFun f = &A::Fun;
 А если класс неизвестный?

Автор: archimed7592 30.4.2007, 19:41
Цитата(nerezus @  30.4.2007,  19:24 Найти цитируемый пост)
мм, это как?
boost::bind, boost::lambda, Loki::Function...
а вообще читай Александрску, Современное проектирование на С++ smile
Цитата(nerezus @  30.4.2007,  19:24 Найти цитируемый пост)
А если класс неизвестный? 
функторы smile
в двух словах - это объекты у которых перегружен operator ()

Автор: nerezus 30.4.2007, 19:44
хм, тогда остается 2 варианта:
1) наследование нужного класса, из которого я хотел делать вызовы. Но тут проблемы, ибо он у меня Singleton
2) Strategy паттерн, но как-то хреново я его тут представляю....

Автор: archimed7592 30.4.2007, 19:57
3) использовать функторы smile 
скажи, а в чём проблема их использовать? smile
компилировать boost для них не нужно, если не ошибаюсь...
пример...вот те простейший пример:
Код
std::for_each (v.begin (), v.end (), std::cout << boost::lambda::_1 << '\n');
если контейнер map, то так:
Код
typedef std::map < int, std::string > container;
std::for_each (m.begin (), m.end (),
    std::cout << boost::bind<int> (&std::pair<int, std::string>::first, boost::lambda::_1)
    << '\t'
    << boost::bind<std::string> (&std::pair<int, std::string>::second, boost::lambda::_1)
    << '\n');


Добавлено через 3 минуты и 2 секунды
кстати, для map ты видишь как производится доступ к членам (полям/методам)... функтор возвращённый boost::bind можно засунуть, скажем, в boost::function (или Loki::Function) и вызывать не зная ни класса, ни метода... ничего...

Автор: archimed7592 30.4.2007, 21:09
про возможности http://boost.org/libs/bind/bind.html
про возможности http://boost.org/doc/html/lambda/le_in_details.html (не встречавшим конструкций if_then/try_catch и т.п. рекомендую - сильно впечатляет в первый раз smile)
про возможности http://boost.org/doc/html/function/tutorial.html

nerezus, конкретно твоя задача решается примерно так:
Код
class A
{
    boost::function1< void, const st & > func_;
public:
    set_action (boost::function1< void, const st & > func) {func_ = func;}
    do_action (const st &arg) {func (arg);}
};

void simple_fun (const st &arg) {};
void simple_fun_many_args (const st &arg, int, double) {};

class B
{
public:
    void member_fun (const st &arg) {};
};

int main ()
{
    A a;
    B b;
    a.set_action (simple_fun);
    a.do_action ();
    a.set_action (boost::bind<void> (simple_fun_many_args, boost::lambda::_1, 5, 67.85));
    a.do_action ();
    a.set_action (boost::bind<void> (&B::member_fun, boost::lambda::ref(x), boost::lambda::_1));
    a.do_action ();

    return 0;
}

Автор: nerezus 30.4.2007, 21:20
Блин, как-то некрасиво получается =\
Я вот думаю, может стоит попытаться заюзать готовый фреймворк? Просто ща я пытался структурный код SDL переделать в ООП(при практически нулевых знаниях С++) и столкнулся с сабжем.

Добавлено через 1 минуту и 2 секунды
archimed7592, за код спасибо. А можешь выложить либы буста для этого примера?

Автор: Любитель 30.4.2007, 23:04
Цитата(nerezus @  30.4.2007,  21:20 Найти цитируемый пост)
Блин, как-то некрасиво получается =\

nerezus, ты просто не почувствовал красоты в понятии плюсов smile

На мой взгляд, буст (и особенно бустовское ФП) - это самое красивое в плюсах smile

Автор: nerezus 30.4.2007, 23:13
Цитата

nerezus, ты просто не почувствовал красоты в понятии плюсов
 Вероятно. Да и ща я не от хорошей жизни в плюсы залез. Надо будет PyGame проверить.. )

Цитата

буст (и особенно бустовское ФП) - это самое красивое в плюсах
 ФП? Оно заключается в названии lambda и map? В других языках на порядок получше )

Автор: archimed7592 30.4.2007, 23:15
Любитель, всецело и полностью согласен smile с++ вообще очень красивый язык smile

nerezus,
Код
#include <boost/bind.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/function.hpp>
#include <boost/ref.hpp>
если ты об этом... smile
если нужен сам буст, то на http://boost.org тебе дорога...

Добавлено через 2 минуты и 26 секунд
Цитата(nerezus @  30.4.2007,  23:13 Найти цитируемый пост)
Оно заключается в названии lambda и map?
нет, map к функциональному программированию отношения не имеет smile
скажи для начала в каких языках есть такие же мощные средства для ФП smile я имею ввиду ООП/императивные языки т. к. на чисто-функциональных языках писать, имхо, невозможно smile

Автор: Любитель 30.4.2007, 23:23
Цитата(archimed7592 @  30.4.2007,  23:15 Найти цитируемый пост)
 на чисто-функциональных языках писать, имхо, невозможно

Всё-таки позволю не согласиться...

Прикол плюсов в достаточно виртуозной реализации многих вещей (не только ФП) без явной поддержки этого со стороны языка. Это красиво. В определённом роде.

Добавлено через 1 минуту и 34 секунды
Питон - это хороши, интересно... Но красоты я лично не вижу. Не привлекает. А програмерить на плюсах - приятно, блин!

Автор: archimed7592 30.4.2007, 23:27
Цитата(Любитель @  30.4.2007,  23:23 Найти цитируемый пост)
Всё-таки позволю не согласиться...
с чем несогласен то?
насчёт прикола плюсов: это не прикол... это само совершенство... ;)

Добавлено через 57 секунд
Цитата(Любитель @  30.4.2007,  23:23 Найти цитируемый пост)
А програмерить на плюсах - приятно, блин! 
ага... люблю этот язык  smile 

Автор: Любитель 30.4.2007, 23:29
Не соглашусь, что на языках с нэтивной поддержкой ФП невозможно писать. Можно и очень хорошо. На некоторых даже интересно. Но плюсы - не знаю, лично мне всё-таки нравиться дизайн и философия языка. Недоделан он, конечно, но, блин, хорош...

Автор: Mayk 30.4.2007, 23:38
Уж если буст тянуть, то можно черезhttp://www.boost.org/doc/html/signals/tutorial.html#id2731274 связь с SDL сделать.

Примерно так:
Код

#include <boost/signal.hpp>
#include <boost/bind.hpp>

using namespace std;
using namespace boost;



// это относится к SDL
typedef int* Key;
struct kbd_handler{
    static signal<void (Key)> key_pressed;
};

signal<void (Key)> kbd_handler::key_pressed;


// это уже нет
class MyClass
{
    public:
    void pressed_key( Key key_code ){
        printf("pressed %p\n", key_code );
    }
};


int main()
{
    MyClass kr;
    kbd_handler::key_pressed.connect( bind(&MyClass::pressed_key, &kr, _1)  );


    // это происходит в окресностях  SDL_PollEvent'а
    kbd_handler::key_pressed((Key)1);
}


Цитата

03:48:dvl:~/src/pol$ ./a.out 
pressed 0x1

Автор: archimed7592 1.5.2007, 00:05
Цитата(Любитель @  30.4.2007,  23:29 Найти цитируемый пост)
Не соглашусь, что на языках с нэтивной поддержкой ФП невозможно писать. Можно и очень хорошо. На некоторых даже интересно.
соддержкой - да... поддержка это хорошо... я про чистые ФЯП говорил... хаскел к примеру smile от лукавого, имхо...

Автор: Mayk 1.5.2007, 00:09
Цитата(archimed7592 @  1.5.2007,  04:05 Найти цитируемый пост)
)
Не соглашусь, что на языках с нэтивной поддержкой ФП невозможно писать. Можно и очень хорошо. На некоторых даже интересно.
соддержкой - да... поддержка это хорошо... я про чистые ФЯП говорил... хаскел к примеру smile от лукавого, имхо... 

smile  Господа, к порядку. smile 

Автор: Любитель 1.5.2007, 00:11
Цитата(archimed7592 @  1.5.2007,  00:05 Найти цитируемый пост)
от лукавого, имхо...

 smile 

Насчёт boost::signals - немного недоделана либа:
1. Хорошо бы иметь возможность паблик-доступа на коннект и прайват - на вызов. Или типа того.
2. Очень неприятно упоминание о потонебезопасности в доках по либе smile Впрочем, libsigc++ тоже вроде бы особым потокосейфити не обладает...

Автор: Mayk 1.5.2007, 00:26
Цитата(Любитель @  1.5.2007,  04:11 Найти цитируемый пост)

1. Хорошо бы иметь возможность паблик-доступа на коннект и прайват - на вызов. Или типа того.

набросочно это можно решить  так: 
Код

class private_signal : public signal<void (Key)>
{
    friend class kbd_handler; //kbd_handler наш друг
    private:
    using signal<void (Key)>::operator(); //вызов запрещён
};

struct kbd_handler{
    static private_signal key_pressed;

    static void emit(){
        key_pressed(0); //друзья вызывают private'но
    }
};

private_signal kbd_handler::key_pressed;

class MyClass
{
    public:
    void pressed_key( Key key_code ){
        printf("pressed %p\n", key_code );
    }
};
int main()
{
    MyClass kr;
    kbd_handler::key_pressed.connect( bind(&MyClass::pressed_key, &kr, _1)  );
    kbd_handler::emit(); 
//  kbd_handler::key_pressed(0); // Compile time error
}



Автор: Ken 1.5.2007, 00:51
Цитата(nerezus @ 30.4.2007,  18:51)
2) То же самое, но с методом объекта, а не с функцией. Класс объекта заранее не известен.

Nerezus, почему не хотите использовать event listener-ы в стиле Java? Это проще и не требует темплейтов и доп. библиотек. Например, допустим, вы создаете компонент Button. Хотите от него получить событие onClick (). Просто объявляем новый абстрактный класс (interface):
Код

class Button;
class ButtonListener
{
    public:
        virtual void onClick (Button* sender) = 0; // sender, чтобы узнать от какого Button это событие, можете убрать
};

А к классу Button добавляем метод и член:
Код

class ButtonListener;
class Button
{
    public:
        Button ():
            _listener (NULL)
        {
        }
...
        void setListener (ButtonListener* listener)
        {
            _listener = listener;
        }
...
    private:
...
        ButtonListener* _listener;
};

Класс который хочет ловить событие onClick () должен наследовать от ButtonListener и реализовать метод onClick (). А внутри одного из методов Button генерация onClick будет выглядеть так:
Код

void Button::doClick ()
{
    if (_listener != NULL)
        _listener->onClick (this);
}

Регистрация обработчика уже будет выглядеть примерно как вы хотите:
Код

Button* closeButton = new Button ();
closeButton->setListener (closeButtonController);


Автор: Любитель 1.5.2007, 08:49
Mayk, ну я немного по другому делаю - у меня шаблон для сигналов (юзающий бустовские, конечно) с дополнительным параметром - классом, для которого разрешён вызов сигнала.

Насчёт ивент-листенеров - в яве есть анонимные классы, в плюсах нет. Потому юзать ивент-листенеры наглой копией - не очень приятно. К тому же пропадает возможность биндинга и пр. приятных вещей.

Автор: Ken 1.5.2007, 09:07
Цитата(Любитель @ 1.5.2007,  08:49)
Насчёт ивент-листенеров - в яве есть анонимные классы, в плюсах нет. Потому юзать ивент-листенеры наглой копией - не очень приятно. К тому же пропадает возможность биндинга и пр. приятных вещей.


Что означает "юзать ивент-листенеры наглой копией"?

Не обязательно использовать анонимных классов. Если количество объектов от которых можем получать событий не очень много, то один класс-контроллер отвечающий за логику приложения бывает достаточным, если много, то можно создавать разные контроллеры обрабатывающие события. Мне нравится простота и эффективность этой схемы, она работает без дополнительных затрат (зачем boost, особенно в встроенных системах только из за этого).

Автор: nerezus 1.5.2007, 10:30
Цитата

скажи для начала в каких языках есть такие же мощные средства для ФП
 Насчет "такие же" я не знаю, но вот в питоне ФП намного более развито. Пример:
Код

# -*- coding: windows-1251 -*-

# Использование лябмда-функций в языке Python.
# Практическое применение

import time

# Часть первая: объявление простой функции
mult = lambda x, y: x * y
print "mult(1, 3) =", mult(1, 3)


# Часть вторая: Объявление функции в зависимости от внешних параметров.
def inc(n): return lambda x: x + n
inc6 = inc(6)
print "inc6(5) =", inc6(5)


# Часть третья: объявление функции, основанной на другой лямбда-функции
square = lambda x: mult(x, x)
print "square(7) =", square(7)


# Часть четвертая: создаем массив функций.
print "mult_arr[i](5)"
mult_arr = [] # список(массив) функций
for i in range(0, 10):
    mult_arr.append(lambda x: x * i) # добавляем в список функцию
# Демонстрация массива функций    
for i in range(0, 10):
    print mult_arr[i](5)


# Часть пятая: обход всех значений списка:
list = [0, 1, 2,3, 6, 10, 100, 777]
# использование спискового включения
print [x*2 for x in list]

# Часть шестая: вычисление факториала
# reduce() - обход списка, range() - создание списка
# Для вычисления факториала используются 2 lambda-функции
fact = lambda n: reduce(lambda x, y: x * y, range(1, n+1), 1)
print "fact(6) =", fact(6)

# Часть седьмая: полином Ньютона
x1 = 0.3; x2 = 0.4; x3 = 0.6
y1 = -2;  y2 = 1.2; y3 = 7.3;
L0 = y1 / ((x1-x2) * (x1-x3))
L1 = y2 / ((x2-x1) * (x2-x3))
L2 = y3 / ((x3-x1) * (x3-x2))
# Создаем функцию, основываясь на входных значениях
newt = lambda x: L0*(x-x2)*(x-x3) + L1*(x-x1)*(x-x3) + L2*(x-x1)*(x-x2)
print "newt(0.5) =", newt(0.5)


time.sleep(300)



Цитата

Но красоты я лично не вижу. Не привлекает.
 А у меня к плюсам неприязнь. В стиле "зачем усложнять, если можно проще". Но это оффтоп, продолжать не надо )


Ken, оо, самый лучший совет )  Просто это со стороны структурного кода работать не будет, ну да ладно, ну первую часть нафиг. Спасибо.
В C++ можно же при наследовании от 2х классов тип указателя на Child сделать указателем на Parent1(лисенер)? Ну вот и лучший вариант )

Автор: Любитель 1.5.2007, 11:35
Цитата(nerezus @  1.5.2007,  10:30 Найти цитируемый пост)
но вот в питоне ФП намного более развито.

В пиноте нэтив-роддержка. А в плюсах красивая эффектная реализация. smile

Цитата(Ken @  1.5.2007,  09:07 Найти цитируемый пост)
Не обязательно использовать анонимных классов.

Я не говорю, что обязательно, но без них очень неудобно. Бустовские вещи в данном случае гораздо более гибкое и красивое решение (boost::function, boost::signal, boost::bind, boost::lambda). + действительно в стиле плюсов.

Автор: Ken 1.5.2007, 12:29
Цитата

Бустовские вещи в данном случае гораздо более гибкое и красивое решение (boost::function, boost::signal, boost::bind, boost::lambda). + действительно в стиле плюсов.


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

Цитата

Просто это со стороны структурного кода работать не будет.


Nerezus для струрного кода используйте указателей на функций как в начале показали товарищи.
Цитата

В C++ можно же при наследовании от 2х классов тип указателя на Child сделать указателем на Parent1(лисенер)?

Да. Это то же самое что реализация интерфейсов в Java (если все методы второго класса абстракные и класс не содержит свои поля). Можете наследовать не только от 2х ;)

Автор: Любитель 3.5.2007, 18:25
Цитата(Ken @  1.5.2007,  12:29 Найти цитируемый пост)
Согласен с вами.

[offtop]Почему с нами?[/offtop]

Цитата(Ken @  1.5.2007,  12:29 Найти цитируемый пост)
Но программируя часто для встроенных систем и микроконтроллеров совсем неприятно увидеть как твой продукт становиться раздутым из за разных библиотек, поэтому если можно обойтись без них, то лучше использовать простые средства которые решают задачу.

Не знаю. Встроенные системы, микроконтроллеры - ничем этим не занимаюсь smile

+ для многих ситуаций хватит STL-вского ФП. Впрочем, дело вкуса, конечно.

Автор: archimed7592 4.5.2007, 14:39
Цитата(Любитель @  3.5.2007,  18:25 Найти цитируемый пост)
для многих ситуаций хватит STL-вского ФП
что за ситуации такие? smile 
я так понимаю речь о bind1st/2nd smile если память не изменяет - у функтора должны быть result_type и прочие грабли smile

Автор: Любитель 7.5.2007, 12:14
Прод стл-вским ФП (в отличие от бустовского - его развития) я понимаю:

1. std::unary_function и пр. вместо boost::function.

2. std::bind1st и пр. вместо boost::bind.

3. std::map + первый пункт вместо boost::signals  smile 

4. Повеситься вместо boost::lambda  smile 

Автор: archimed7592 7.5.2007, 13:06
Цитата(Любитель @  7.5.2007,  12:14 Найти цитируемый пост)
. std::map + первый пункт вместо boost::signals  smile 
м. б. std::deque? smile

Цитата(Любитель @  7.5.2007,  12:14 Найти цитируемый пост)
4. Повеситься
то то и оно  smile  smile 

Автор: Любитель 7.5.2007, 13:17
Цитата(archimed7592 @  7.5.2007,  13:06 Найти цитируемый пост)
м. б. std::deque

Туплю. Думал нужна привяка от строк - не нужна  smile 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)