Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
 
Рейтинг 4.67/6: Рейтинг темы: голосов - 6, средняя оценка - 4.67
8 / 8 / 0
Регистрация: 13.06.2018
Сообщений: 19
1

Отбросить виртуальность колбэка

30.08.2019, 23:47. Показов 1110. Ответов 8
Метки нет (Все метки)

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

Есть указатель на базовый класс и указатель на виртуальную функцию в этом классе, которая используется как колбэк.
Интересно можно ли отбросить виртуальность и вызвать самый базовый метод у объекта внутри некоего
C++
1
Invoker
. При этом клиент должен делать простой вызов типа
C++
1
Invoker(this, &SomeClass::SomeFunction
или
C++
1
Invoker(Functor(this, &SomeClass::SomeFunction)
--- функтор в этом случае делает что-то похоже что и лямбда (см. код). Смысл в том чтобы избавить клиента от необходимости явно создавать лямбду.

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
#include <iostream>
 
class A
{
public:
    virtual void Foobar() { std::cout << "A" << std::endl; }
};
 
class B : public A
{
public:
    virtual void Foobar() { std::cout << "B" << std::endl; }
};
 
 
template< class T, class F >
void Invoker(T* target, F&& caller)
{
    std::invoke(std::forward< F >(caller), *target);
}
 
int main()
{
    B b;
 
        // выведет B - срабатывает виртуальность, для клиента выглядит красиво
    Invoker(&b, &A::Foobar); 
 
        // виртуальность не срабатывает, но для клиента выглядит ужасно
    Invoker(&b, [](A& a)
                { 
                    a.A::Foobar();  
                });
 
return 0;
}

Спасибо.
0

Помощь в написании контрольных, курсовых и дипломных работ здесь.

Лучшие ответы (1)
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
30.08.2019, 23:47
Ответы с готовыми решениями:

Виртуальность и наследование
Очень интересный вопрос! #include &lt;iostream&gt; class Super { public: virtual void print()...

Перегрузка Виртуальность
Чем отличается перегрузка от виртуальности?

Виртуальность friend-метода в базовом классе
Привет всем. Прошу посмотрите данный код. В производном классе реализована перегрузка оператора...

Возвращение результата колбэка обратно в вызвавшую его функцию
WebClient wc = new WebClient(); wc.BaseAddress = urlFile; ...

8
С чаем беда...
Эксперт CЭксперт С++
9074 / 4606 / 1250
Регистрация: 18.10.2014
Сообщений: 10,304
31.08.2019, 00:13 2
Цитата Сообщение от sendless Посмотреть сообщение
Есть указатель на базовый класс и указатель на виртуальную функцию в этом классе, которая используется как колбэк. Интересно можно ли отбросить виртуальность и вызвать самый базовый метод у объекта
В С++ такой возможности нет. Указатель на виртуальную функцию не может быть привязан к конкретной версии виртуальной функции в иерархии. Либо реализуйте это все через невиртуальные функции, либо придется как-то выцарапывать целевой адрес функции нестандартными методами.
0
8 / 8 / 0
Регистрация: 13.06.2018
Сообщений: 19
31.08.2019, 02:02  [ТС] 3
Работающий пример кода.

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
#include <iostream>
#include <functional>
#include <utility>
 
template< class T, class F >
void Invoker(T* target, F&& caller)
{
    std::invoke(std::forward< F >(caller), *target);
}
 
class Super
{
public:
    void Subscribe() {
        Invoker(this, &Super::Foobar);
    }
 
private:
    virtual void Foobar() { 
        std::cout << " S U P E R " << std::endl; 
    };
};
 
class Derived : public Super
{
private:
    virtual void Foobar() { 
        std::cout << " D E R I V E D" << std::endl; 
    };
};
 
int main()
{
    Derived d;
    d.Subscribe();
}
Добавлено через 1 минуту
TheCalligrapher, ни шаблоны, ни макросы не смогут решить эту проблему?
0
15039 / 8058 / 1940
Регистрация: 30.01.2014
Сообщений: 13,642
31.08.2019, 03:31 4
sendless, по-моему тут не эту проблему надо решать, а пересматривать дизайн из-за которого она появилась. Почему мы вдруг вообще хотим от виртуального метода невиртуального поведения?

Может быть сделать так?
Кликните здесь для просмотра всего текста
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
#include <iostream>
#include <functional>
#include <utility>
 
template< class T, class F >
void Invoker(T* target, F&& caller)
{
    std::invoke(std::forward< F >(caller), *target);
}
 
class Super
{
public:
    void Subscribe() {
        Invoker(this, &Super::FoobarS);
    }
 
private:
    void FoobarS() {
        std::cout << " S U P E R " << std::endl; 
    }
    
    virtual void Foobar() { 
        FoobarS(); 
    }
};
 
class Derived : public Super
{
private:
    virtual void Foobar() { 
        std::cout << " D E R I V E D" << std::endl; 
    }
};
 
int main()
{
    Derived d;
    d.Subscribe();
}
1
Комп_Оратор)
Эксперт по математике/физике
8719 / 4425 / 598
Регистрация: 04.12.2011
Сообщений: 13,256
Записей в блоге: 16
31.08.2019, 10:45 5
sendless, если я правильно понял вопрос, то согласен с DrOffset. Желание сделать внутри механизма - анти-механизм неконструктивно. Хотя трудно придумать что-то такое, чего нельзя исполнить. Поэтому вопрос (в литературных выражениях) "А на кой это нужно?" лучше всего ставить вначале. Потому что в конце он всё равно встанет.
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
#include <iostream>
 
using namespace std;
 
struct Base
{
    virtual ~Base(){};
    bool is_virtualCall;
    Base(bool b=true):is_virtualCall(b){}
    virtual void foo(){cout<<"\nfoo from base";}
};
 
struct Child :public Base
{
     ~Child(){};;
   Child(bool b=true):Base(b){}
   void foo()
   {
       if(is_virtualCall){
       cout<<"\nfoo from child";
       }
       else
            {
        Base::foo();
        }
       }
};
 
void cb_caller(Base &b, void(Base::*fun)(void))
{
    (b.*fun)();
}
 
int main()
{
Base base;
Child child_virt, child_novirt(false);
cb_caller(base, &Base::foo);
cb_caller(child_virt, &Base::foo);
cb_caller(child_novirt, &Base::foo);
 
    return 0;
}
0
8 / 8 / 0
Регистрация: 13.06.2018
Сообщений: 19
31.08.2019, 14:25  [ТС] 6
DrOffset, IGPIGP,
Мне интересно, а можно ли это сделать в принципе, если да то как. Как таковой задачи нет, просто исследование just for fun. Также интересно - можно ли статически определить что функция виртуальная, типа
C++
1
std::is_polymorphic
только для функции.
0
Комп_Оратор)
Эксперт по математике/физике
8719 / 4425 / 598
Регистрация: 04.12.2011
Сообщений: 13,256
Записей в блоге: 16
31.08.2019, 14:50 7
Цитата Сообщение от sendless Посмотреть сообщение
DrOffset, IGPIGP,
Мне интересно, а можно ли это сделать в принципе, если да то как. Как таковой задачи нет, просто исследование just for fun. Также интересно - можно ли статически определить что функция виртуальная, типа
C++
Не понятно что значит это. Именно поэтому и идет разговор в стиле телешоу об этом.
Если использовать RTTI - (dynamic_cast например) то можно выяснить реальный тип и сделать на вызывающей стороне достаточно много вещей. Вопрос: "Зачем?". Полиморфизм наследования, это способ разгрузки вызывающей стороны от вопроса "кто там и что он вызовет?". Сломать этот механизм можно легко. Один из способов я показал, но можно ещё нарыть.
0
15039 / 8058 / 1940
Регистрация: 30.01.2014
Сообщений: 13,642
31.08.2019, 15:39 8
Лучший ответ Сообщение было отмечено sendless как решение

Решение

Цитата Сообщение от sendless Посмотреть сообщение
можно ли это сделать в принцип
В "принципе" - можно, но это будет уже не С++.

В стандартном С++ - сделать это нельзя. Можно обернуть вашу лямбду в специальный макрос, что даст требуемый эффект.
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
#include <iostream>
#include <functional>
#include <utility>
 
template< class T, class F >
void Invoker(T* target, F&& caller)
{
    std::invoke(std::forward< F >(caller), *target);
}
 
template <typename T>
struct member_traits;
 
template <typename F, typename C>
struct member_traits<F (C::*)> {
    using class_type = C;
};
 
#define NON_POLYMORPHIC_MEMBER(F) \
    [](typename member_traits<decltype(&F)>::class_type & a) { a.F(); }
 
class Super
{
public:
    void Subscribe() {
        Invoker(this, NON_POLYMORPHIC_MEMBER(Super::Foobar));
    }
 
private:
    virtual void Foobar() { 
        std::cout << " S U P E R " << std::endl; 
    };
};
 
class Derived : public Super
{
private:
    virtual void Foobar() { 
        std::cout << " D E R I V E D" << std::endl; 
    };
};
 
int main()
{
    Derived d;
    d.Subscribe();
}
Но о "принципе" речь не идет уже, понятно.
3
749 / 352 / 72
Регистрация: 10.06.2014
Сообщений: 2,371
31.08.2019, 19:03 9
Del...
Не так понял
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
31.08.2019, 19:03

Помощь в написании контрольных, курсовых и дипломных работ здесь.

Результат колбэка вернуть как результат функции
Добрый день, подскажите плииииз, такое в принципе возможно? (потери в продуктивности не важны) ...

Отбросить нули
Ребят помогите отбросить нули. Например я ввожу 0000000000000005,45 а он должен принимать 5,45. И...

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

Отбросить все теги.
Есть строка $eader, содержащая html теги. Например, &quot;&lt;h2&gt;&lt;center&gt;Теория&lt;/center&gt;&lt;/h2&gt;&quot;, или...


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

Или воспользуйтесь поиском по форуму:
9
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2021, vBulletin Solutions, Inc.