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


Автор: OlegIT 7.7.2014, 07:56
В классе имеются несколько однотипных функций. Как сделать список из этих функций в QList или vector?

Автор: Lukkoye 7.7.2014, 08:57
 http://rextester.com/XFBRA12764

Код

#include <vector>
#include <functional>
#include <iostream>
using namespace std;

#ifdef __MINGW__
    #define dFUNCTION __PRETTY_FUNCTION__
#endif

#ifdef __MINGW32__
    #define dFUNCTION __PRETTY_FUNCTION__
#endif

#ifdef __GNUC__
    #define dFUNCTION __PRETTY_FUNCTION__
#endif
    
#ifdef _MSC_VER
    #define dFUNCTION __FUNCSIG__
#endif

struct example1
{
    typedef void(example1::*func)();
    
    void foo() { cout << dFUNCTION<<endl; }
    void bar() { cout << dFUNCTION<<endl; }
    void baz() { cout << dFUNCTION<<endl; }
    
    example1()
    {
        mfunc.emplace_back( &example1::foo );
        mfunc.emplace_back( &example1::bar );
        mfunc.emplace_back( &example1::baz );
    }
    
    void launch()
    {
        for(auto& i: mfunc)
            (this->*i)();
    }
    
private:
    
    vector<func> mfunc;
    
};

struct example2
{
    typedef function<void()> func;
    
    void foo() { cout << dFUNCTION<<endl; }
    void bar() { cout << dFUNCTION<<endl; }
    void baz() { cout << dFUNCTION<<endl; }
    
    example2()
    {
        mfunc.emplace_back( bind(&example2::foo,this) );
        mfunc.emplace_back( bind(&example2::bar,this) );
        mfunc.emplace_back( bind(&example2::baz,this) );
    }
    
    void launch()
    {
        for(auto& i: mfunc)
            i();
    }
    
private:
    
    vector<func> mfunc;
    
};

int main()
{
    std::cout << "Hello, world!\n";
    
    example1 exa1; exa1.launch();
    std::cout << "-----------------------\n";
    example2 exa2; exa2.launch();
}


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

Второй способ более мощный, гибкий - создание делегата.

Делегат - это особый класс функторов (std::function), способный осуществлять запуск свободной функции или функции члена, без необходимости "помнить" к какому классу относится функция-член.

Грубо говоря, std::function это что-то вроде кютешных слотов-сигналов, вот только защита времени компиляции и работает на порядок быстрее.


Автор: OlegIT 7.7.2014, 11:05
Спасибо.

Как я понимаю Ваша ссылка на ресурс, где можно запускать свой код, вставляя его в окно редактора. Так?

Автор: OlegIT 8.7.2014, 11:14
Протестировал Ваш код в Visual Studio и в Qt5.3. Ошибки такие, компилятор не знает, что есть такое emplace_back (хотя внутри vector он есть) и bind. И на функцию launch() по полной выругался. Поместил в буфер функции с помощью push_back, но вызвать их не получается.

У меня другой ещё вопрос. Это всё мне нужно для предотвращения зацикливания, мои однотипные функции могут вызываться последовательно и вернутся к первоначальной функции. Что бы этого избежать, я создаю буфер из этих функций, а потом, в основном цикле программы, выполняю вызов функций из этого буфера. Но при вызове может оказаться, что буфер функций пополнился. Не будет тут проблемы? Может по другому это можно сделать?

Автор: NoviceF 8.7.2014, 13:03
Цитата(OlegIT @  8.7.2014,  12:14 Найти цитируемый пост)
Протестировал Ваш код в Visual Studio и в Qt5.3. Ошибки такие, компилятор не знает, что есть такое emplace_back

В qt включал c++11 в pro файле? Что-то в таком духе
Код

QMAKE_CXXFLAGS += -std=c++0x

Автор: OlegIT 8.7.2014, 14:24
Цитата(NoviceF @  8.7.2014,  13:03 Найти цитируемый пост)
В qt включал c++11 в pro файле? Что-то в таком духе

Включу.

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

Автор: NoviceF 8.7.2014, 14:57
Цитата(OlegIT @  8.7.2014,  15:24 Найти цитируемый пост)
тут ещё вопрос как/где запустить отработку созданного буфера

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

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

Автор: Lukkoye 8.7.2014, 19:25
Цитата(OlegIT @  8.7.2014,  11:14 Найти цитируемый пост)
Протестировал Ваш код в Visual Studio и в Qt5.3. Ошибки такие, компилятор не знает, что есть такое emplace_back (хотя внутри vector он есть) и bind. И на функцию launch() по полной выругался. Поместил в буфер функции с помощью push_back, но вызвать их не получается.


Вижал студия из коробки умеет std::function начиная с версии 10

Насчет Qt:

1. Если вы используете систему сборки QMake, пропишите в файле сценария сборки  имя_вашего_проекта.pro следующий ключ:

QMAKE_CXXFLAGS += -std=gnu++1y
либо 
QMAKE_CXXFLAGS += -std=c++0x


1. Если вы используете систему сборки Сmake, пропишите в файле сценария сборки CMakeLists.txt
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++1y ")
либо 
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")

Данный ключ активирует поддержку нововведений в стандарте языка.

По поводу "зацикливаний":


Цитата(OlegIT @  8.7.2014,  11:14 Найти цитируемый пост)
Но при вызове может оказаться, что буфер функций пополнился. Не будет тут проблемы? Может по другому это можно сделать?


Разумеется будут.

Каноничную реализацию можно подглядеть рассматривая устройство какой нибудь "EventSystem".

Если вкратце: есть отдельный класс, называемый "Eventsystem ", который хранит внутри себя несколько списков:
1. Желающие добавиться.
2. Желающие удалиться. Этот список представляет собой пару: <делегат, вкл/выкл>

Eventsystem в цикле бежит по рабочему циклу, и запускает одного за другим делегаты.

Допустим, запуск делегата приводит в действие функцию, которая пытается добавить в Eventsystem новый делегат - он добавляется в список "желающих добавиться". Рабочий же список остается без изменений.

Если же запуск делегата приводит в действие функцию, которая напротив, пытается удалить делегат - он просто помечается как "выкл", и Eventsystem знает, что его использовать нельзя. Больше он уже не будет запущен.
Если же претендент на удаление обнаружится в списке "желающих добавиться" - можно удалить оттуда сразу же.

После того, как весь рабочий цикл завершился, только тогда Eventsystem добавляет в рабочий список новеньких, а так же выбрасывает всех тех, кто помечен, как "выкл".

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

То бишь, легко отловить факт рекурсивных вызовов, и зацикливания.


Автор: OlegIT 9.7.2014, 11:16
Спасибо за разъяснения. С делегатами и двумя очередями понятно. Не понятно где выполнять вызовы, обработку списка и где "Eventsystem добавляет в рабочий список новеньких"?

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