Форум программистов, компьютерный форум CyberForum.ru

Абстракция вызова функции - C++

Восстановить пароль Регистрация
 
Melg
416 / 152 / 62
Регистрация: 23.09.2013
Сообщений: 306
23.09.2013, 23:45     Абстракция вызова функции #1
Итак я хотел бы обсудить с участниками форума такую задачу. Но перед тем как перейти к сути - замечу, что вопросы морально-этического облика человека, поставившего эту задачу, и степень адекватности постановки задачи впринципе - не должны стать ключевыми в ходе обсуждения. К сожалению ввиду множества нюансов описание может затянуться и боюсь это вопрос не из серии "С++ для начинающих", но доступа к той ветке у меня нет. Итак преступим.
Предположим, что на стороне пользователя (другого программиста использующего мой код) имеется некоторая функция, это может быть метод класса, статическая функция, или функция определённая глобально. Эта функция может иметь любое число параметров ( от нуля т.е foo(); до N т.е foo(T1 o1, T2 o2, T3 o3,...TN oN). У этой функции может быть любой тип возвращаемого значения. При этом функция может использовать любой тип передачи параметров и возвращаемого значния - т.е по значению, по ссылке, по указателю и т.д.
Задача: Необходимо создать такой механизм, который бы позволил пользователю используя мой код, в runtime этапе по своему желанию сначала зарегестрировать свою функцию в некотором объекте, а после - вызвать её через этот объект. (Для условности мы можем назвать его Delegate delegate. В процессе регистрации (одной или несколькими операциями) - должна быть задана сама функция, все объекты - аргументы функции, а так же некоторый способ обработки возвращаемого значения функции.
Подробнее: пользователь на момент регистрации функции создает все необходимые объекты, соответствующие сигнатуре функции, и регистрирует их, после чего эти объекты должны будут использоваться при вызове зарегестрированной функции, пользователь определяет некоторый алгоритм (функцией или объектом класса) который умеет обработать возвращаемое значение.
Пара примеров для иллюстрации: Допустим у меня есть функция int PrintIntValue(int value_for_print); - которая выводит значение переданного парметра на экран, и если она отработала успешно - то возвращает 0, иначе -1. Допустим у меня есть уже готовое значение равное 42. И есть некоторая функция, void HandleErrorCode(int error_code); Которая принимает значение кода ошибки, и в зависимости от кода - пишет в лог файл ( на экран, в консоль - не суть важно) - некоторую информацию (время возникновения, код и т.д.). Я хочу Имея объект Delegate delegate; Зарегестрировать некоторым образом (каким угодно) три перечисленных выше парметра (функция, набор конкретных значений параметров для вызова, и обработчик возвращаемого значения), а после - где-то в другом месте кода - иметь возможность сделать вызов delegate(); (ну или delegate.CallFunction(); ) после которого будтет выполнена последовательность: вызов функции с переданным значением 42 в качестве параметра PrintIntValue(42); -а после возврата значения - вызов функции c переданным кодом ошибки HandleErrorCode(*);
Требования: вызов delegate.Callfunction(); - можно производить неограниченное число раз. После того как в delegate была зарегестрирована одна функция, ничто не должно мешать зарегестрировать в нем другую, например метод класса, который принимает массив указателей на объекты, и ничего не возвращает. И после этого - вызов delegate.CallFunction(); Будет вызывать уже этот метод, и его обработчик вызова ( например который подсчитывает число вызовов), а не предыдущую функцию.
Ограничения: для реализации нельзя использовать сторонние библиотеки, нельзя использовать boost, можно использовать любые решения из std. Можно использовать любые возможности языка включая стандарт с++11. Сам класс Delegate - не должен быть шаблонным, должна быть возможность объединения некоторого набора объектов в любые структуры - например в список, для последовательного вызова CallFunction - для всех объектов в списке. Можно использовать шаблоны, в том числе с переменным числом аргументов. При этом вызов delegate.CallFunction(); должен быть всегда валидным, и если не зарегестрирована никакая функция, и если внутри зарегестрированной фукнции кидается исключение произвольного типа, и если обработчик кидает исключения. При регистрации пользователем своего "супового набора" - ему должна либо сразу предоставляться готовая функциональность - либо любой набор промежуточных абстракций, но пользователь в конечном счете не должен писать дополнительный код для реализации какой-то из абстракций, только код, непосредственно конкретизирующий его намерение зарегестрировать функцию, но он так же может использовать любые возможности языка и стандартной библиотеки. При вызове функции через delegate.CallFunction(); должны соблюдаться способы передачи парметров и возвращаемого значения в точности, для каждого из элементов. Если была функция, которая принимала один аргумент по ссылке, другой по значению, а третий по указателю, при этом возвращая указатель, то после вызова, аргумент, зарегестрированный и передназначеный для передачи по ссылке - в пользовательском коде - реально должен измениться, а обработчик вызова должен получить указатель на адрес памяти, который вернула функция. Реалзация должна быть максимально быстрой, (как ориентир: если сравнивать прямой вызов функции, с переданными в неё параметрами, и вызов обработчика значения, с таким-же вызовом, реализованным через delegate.CallFunction(); - время вызова ( исключая время выполнения непосредственно тел функций) - должно быть не более чем в 10 раз больше чем прямой вызов).
Собственно вопрос: каким образом Вы бы предложили решать поставленную задачу. Интересует либо код, либо схема, либо словесное описание на русском языке, хотя-бы с приблизительным уровнем детализации. Заранее спасибо.
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
castaway
Эксперт С++
4848 / 2987 / 368
Регистрация: 10.11.2010
Сообщений: 11,028
Записей в блоге: 10
Завершенные тесты: 1
24.09.2013, 00:03     Абстракция вызова функции #2
Как ты говоришь тебя зовут?
Jupiter
Каратель
Эксперт C++
6543 / 3963 / 226
Регистрация: 26.03.2010
Сообщений: 9,273
Записей в блоге: 1
Завершенные тесты: 2
24.09.2013, 00:07     Абстракция вызова функции #3
Melg, много текста, лучше бы привели пример кода который будет писать этот самый пользователь, который программист
castaway
24.09.2013, 00:09
  #4

Не по теме:

Jupiter, ты реально все это читал??

OhMyGodSoLong
~ Эврика! ~
 Аватар для OhMyGodSoLong
1234 / 983 / 42
Регистрация: 24.07.2012
Сообщений: 2,002
24.09.2013, 00:13     Абстракция вызова функции #5
Цитата Сообщение от Melg Посмотреть сообщение
Собственно вопрос: каким образом Вы бы предложили решать поставленную задачу. Интересует либо код, либо схема, либо словесное описание на русском языке, хотя-бы с приблизительным уровнем детализации. Заранее спасибо.
Переходом на Common Lisp. Пожалуйста.
Melg
416 / 152 / 62
Регистрация: 23.09.2013
Сообщений: 306
24.09.2013, 00:23  [ТС]     Абстракция вызова функции #6
Jupiter, Изначально не стал приводить пример кода именно потому-что реализация может в значительной степени повлиять на него, а он в свою очередь - сильно ограничит реализацию. Что-бы был понятен смысл - сферический пример:
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
 
#include "delegate.h"
void Foo() {/*...*/;}
int Foo1(double &parameter) {/*...*/;}
class Dummy {
public:
    char* Method(float val){/*...*/;}
};
 
class DummyHandler {
public:
    void HandleDummyMethod(char *return_value) {/*...*/;}
};
 
void Foo1Handler(int return_value) {/*...*/;}
 
int main(int argc, char **argv) {
    Delegate delegate;
 
    delegate.RegisterFunction(&Foo);
    delegate.CallFunction();
 
    double parameter = 234.45d;
    delegate.RegisertFunction(&Foo1,&Foo1Handler,parameter);
    delegate.CallFunction();
 
    Dummy dummy;
    DummyHandler dummy_handler;
    float some_val = 456.324f;
    delegate.RegisterFunction(&dummy,&Dummy::Method,
                                        &dummy_handler,&DummyHandler::HandleDummyMethod,
                                        some_val);
    delegate.CallFunction();
    return 0;
}
Jupiter
Каратель
Эксперт C++
6543 / 3963 / 226
Регистрация: 26.03.2010
Сообщений: 9,273
Записей в блоге: 1
Завершенные тесты: 2
24.09.2013, 01:36     Абстракция вызова функции #7
ну например
Кликните здесь для просмотра всего текста
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#include <memory>
#include <iostream>
#include <functional>
 
template<class... Args>
class DelegateTemplate;
 
class Delegate
{
    std::unique_ptr<Delegate> mPtr;
public:
    virtual ~Delegate() { }
 
    template<class R, class T, class... Args>
    void RegisterFunction(R(*target)(Args...), T(*callback)(R), Args... args)
    {
        mPtr.reset(new DelegateTemplate<R(Args...), T(R)>(target, callback, args...));
    }
 
    template<class R, class S, class T, class... Args>
    void RegisterFunction(R(S::*target)(Args...), S* obj, T(*callback)(R), Args... args)
    {
        mPtr.reset(new DelegateTemplate<R(Args...), T(R)>(target, obj, callback, args...));
    }
 
    virtual void CallFunction()
    {
        mPtr->CallFunction();
    }
};
 
template<class R, class T, class... Args>
class DelegateTemplate<R(Args...), T(R)> : public Delegate
{
    std::function<R()> mTarget;
    std::function<T(R)> mCallback;
public:
    DelegateTemplate(R(*target)(Args...), T(*callback)(R), Args... args)
    : mTarget(std::bind(target, args...))
    , mCallback(callback)
    {
    }
 
    template<class S>
    DelegateTemplate(R(S::*target)(Args...), S* obj, T(*callback)(R), Args... args)
    : mTarget(std::bind(target, obj, args...))
    , mCallback(callback)
    {
    }
 
    ~DelegateTemplate() { }
 
    void CallFunction() override
    {
        mCallback(mTarget());
    }
};
 
int Foo1(int i)
{
    std::cout << "Foo1.i = " << i << std::endl;
    return i;
}
 
void Callback1(int i)
{
    std::cout << "Callback1.i = " << i << std::endl;
}
 
const char* Foo2(double r)
{
    std::cout << "Foo2.r = " << r << std::endl;
    return "hello world";
}
 
void Callback2(const char* str)
{
    std::cout << "Callback1.str = " << str << std::endl;
}
 
class Test
{
public:
    int Foo3(const char* str)
    {
        std::cout << "Test::Foo3.str = " << str << std::endl;
        return 4;
    }
};
 
int main()
{
    Delegate d;
    d.RegisterFunction(&Foo1, &Callback1, 42);
    d.CallFunction();
 
    
    double x = 500.100;
    d.RegisterFunction(&Foo2, &Callback2, x);
    d.CallFunction();
 
    Test test;
    d.RegisterFunction(&Test::Foo3, &test, Callback1, "hello from main");
    d.CallFunction();
}

http://ideone.com/wgsKY5
gray_fox
What a waste!
 Аватар для gray_fox
1244 / 1127 / 53
Регистрация: 21.04.2012
Сообщений: 2,350
Завершенные тесты: 3
24.09.2013, 02:08     Абстракция вызова функции #8
Melg, bind не подойдёт?
castaway
24.09.2013, 02:14
  #9

Не по теме:

gray_fox, наивно полагать, что ТС поймет о чем вы говорите..

gray_fox
What a waste!
 Аватар для gray_fox
1244 / 1127 / 53
Регистрация: 21.04.2012
Сообщений: 2,350
Завершенные тесты: 3
24.09.2013, 02:36     Абстракция вызова функции #10
Цитата Сообщение от castaway Посмотреть сообщение
gray_fox, наивно полагать, что ТС поймет о чем вы говорите..
Вроде как
Цитата Сообщение от Melg Посмотреть сообщение
можно использовать любые возможности языка включая стандарт с++11.
castaway
24.09.2013, 02:53
  #11

Не по теме:

gray_fox, упустил момент, признаю ошибку...

Melg
416 / 152 / 62
Регистрация: 23.09.2013
Сообщений: 306
24.09.2013, 20:47  [ТС]     Абстракция вызова функции #12
Цитата Сообщение от Jupiter Посмотреть сообщение
ну например
Кликните здесь для просмотра всего текста
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#include <memory>
#include <iostream>
#include <functional>
 
template<class... Args>
class DelegateTemplate;
 
class Delegate
{
    std::unique_ptr<Delegate> mPtr;
public:
    virtual ~Delegate() { }
 
    template<class R, class T, class... Args>
    void RegisterFunction(R(*target)(Args...), T(*callback)(R), Args... args)
    {
        mPtr.reset(new DelegateTemplate<R(Args...), T(R)>(target, callback, args...));
    }
 
    template<class R, class S, class T, class... Args>
    void RegisterFunction(R(S::*target)(Args...), S* obj, T(*callback)(R), Args... args)
    {
        mPtr.reset(new DelegateTemplate<R(Args...), T(R)>(target, obj, callback, args...));
    }
 
    virtual void CallFunction()
    {
        mPtr->CallFunction();
    }
};
 
template<class R, class T, class... Args>
class DelegateTemplate<R(Args...), T(R)> : public Delegate
{
    std::function<R()> mTarget;
    std::function<T(R)> mCallback;
public:
    DelegateTemplate(R(*target)(Args...), T(*callback)(R), Args... args)
    : mTarget(std::bind(target, args...))
    , mCallback(callback)
    {
    }
 
    template<class S>
    DelegateTemplate(R(S::*target)(Args...), S* obj, T(*callback)(R), Args... args)
    : mTarget(std::bind(target, obj, args...))
    , mCallback(callback)
    {
    }
 
    ~DelegateTemplate() { }
 
    void CallFunction() override
    {
        mCallback(mTarget());
    }
};
 
int Foo1(int i)
{
    std::cout << "Foo1.i = " << i << std::endl;
    return i;
}
 
void Callback1(int i)
{
    std::cout << "Callback1.i = " << i << std::endl;
}
 
const char* Foo2(double r)
{
    std::cout << "Foo2.r = " << r << std::endl;
    return "hello world";
}
 
void Callback2(const char* str)
{
    std::cout << "Callback1.str = " << str << std::endl;
}
 
class Test
{
public:
    int Foo3(const char* str)
    {
        std::cout << "Test::Foo3.str = " << str << std::endl;
        return 4;
    }
};
 
int main()
{
    Delegate d;
    d.RegisterFunction(&Foo1, &Callback1, 42);
    d.CallFunction();
 
    
    double x = 500.100;
    d.RegisterFunction(&Foo2, &Callback2, x);
    d.CallFunction();
 
    Test test;
    d.RegisterFunction(&Test::Foo3, &test, Callback1, "hello from main");
    d.CallFunction();
}

http://ideone.com/wgsKY5
Спасибо за столь конструктивный ответ. На мой взгляд остаются не решенными следующие насущные проблемы:
1) Передача параметра в функцию по ссылке, иллюстрация http://ideone.com/HA96zR.
2) Обработка вызова метода делегата, без зарегестрированной в нём функции приводит к ошибке выполнения: http://ideone.com/sFTPdh
3) При регистрации функции, которая кидает исключения - вызов при помощи делегата вызывает полное разрушение стека http://ideone.com/kWQqIk

Так-же возник вопрос, в чём заключался смысл написания DelegateTemplate - в отношении наследования от Delegate. При этом - наличия у реализации Delegate указателя на объект типа Delegate, притом как последует из кода RegisterFunction это жесткая композиция (Delegate знает о наследниеке DelegateTemplate).

Кроме того хочу уточнить еще раз, Delegate не обязательно должен принимать весь скоп исходных данных - он может принимать инстанс некоторого другого объекта, или указатель на интерфейс, и это может быть объект из std. Главное ограничение - пользователю не разрешается писать свои реализации этого "интерфейса", он должен иметь возможность сформировать всё небоходимое используя предоставленный код и стандартную библиотеку, без написания своих методов, функций, классов.

Добавлено через 3 минуты
Цитата Сообщение от gray_fox Посмотреть сообщение
Melg, bind не подойдёт?
Спасибо за ответ, но в чистом виде - bind не реализует всю заявленную функциональность. В часности возможность регистрации сущности - обработчика возвращаемого значения, имеет не заданный стандартом тип возвращаемого результата и не "глушит" кинутые эксепшны из зарегестрированных функий, кроме того при использовании порожденного объекта - может еще и свои исключения кинуть. Хотя и предоставляет отличную абстракцию над сигнатурой (списком параметров) функции/метода.
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
24.09.2013, 21:35     Абстракция вызова функции
Еще ссылки по теме:

Не понятный глюк вызова перегруженной функции C++
C++ Вызова функции
Упрощение вызова функции через #define C++

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
Jupiter
Каратель
Эксперт C++
6543 / 3963 / 226
Регистрация: 26.03.2010
Сообщений: 9,273
Записей в блоге: 1
Завершенные тесты: 2
24.09.2013, 21:35     Абстракция вызова функции #13
Цитата Сообщение от Melg Посмотреть сообщение
Спасибо за столь конструктивный ответ. На мой взгляд остаются не решенными следующие насущные проблемы:
1) Передача параметра в функцию по ссылке, иллюстрация http://ideone.com/HA96zR.
2) Обработка вызова метода делегата, без зарегестрированной в нём функции приводит к ошибке выполнения: http://ideone.com/sFTPdh
3) При регистрации функции, которая кидает исключения - вызов при помощи делегата вызывает полное разрушение стека http://ideone.com/kWQqIk
"пилите, Шура, пилите"

Цитата Сообщение от Melg Посмотреть сообщение
ак-же возник вопрос, в чём заключался смысл написания DelegateTemplate - в отношении наследования от Delegate. При этом - наличия у реализации Delegate указателя на объект типа Delegate, притом как последует из кода RegisterFunction это жесткая композиция (Delegate знает о наследниеке DelegateTemplate).
DelegateTemplate<A> и DelegateTemplate<B> это два разных класса, общий базовый класс позволяет заставить их "плясать под одну дудку" т.е. в рантайме менять: целевую функцию, функцию обратного вызова и параметры
Yandex
Объявления
24.09.2013, 21:35     Абстракция вызова функции
Ответ Создать тему
Опции темы

Текущее время: 06:14. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2016, vBulletin Solutions, Inc.
Рейтинг@Mail.ru