Организация вызова функции с вектором СТД функции


Цель этот кусок кода является организация вызова функций в различных временных периодах. Каждый кадр может быть идентифицирован по основной номер кадра и номер кадра:

enter image description here

Код

Моя стратегия заключалась в разработке диспетчере задач, что позволяет зарегистрировать функции следующим образом:

FrameTaskManager.ч

#include <array>
#include <map>
#include <vector>
#include <functional>

class FrameTaskManager {
public:
    using task_t = std::function<void()>;
    static const int frameSize = 16;

    struct Frame {
        Frame() = default;
        Frame(int major, int minor) {
            this->major = major;
            this->minor = minor;
        }
        int major = 0;
        int minor = 0;
        Frame operator+(int increment) {
            Frame tmp(*this);
            tmp.major = (tmp.major + (tmp.minor + increment) / frameSize);
            tmp.minor = (tmp.minor + increment) % frameSize;
            return tmp;
        }
    };
    void step();
    Frame currentFrame() { return _frameCounter.frame; }
    void addTaskAtFrame(Frame frame, task_t task);
    void addRecurrentTask(int minor, task_t task);

private:
    struct FrameCounter {
        Frame frame;
        void next() { frame = frame + 1; }
    } _frameCounter;
    std::array<std::vector<task_t>, frameSize> _recurrenTasks;
    std::map<int, std::map<int, std::vector<task_t>>>_tasks;
}

FrameTaskManager.cpp

#include "frameTaskManager.hpp"

using task_t = std::function<void()>;

void FrameTaskManager::step() {
    const int minor = _frameCounter.frame.minor;
    const int major = _frameCounter.frame.major;
    // Run recurrent tasks
    for (auto& task : _recurrenTasks[minor]) {
        task();
    }
    // Run and pop scheduled tasks
    while (!_tasks[major][minor].empty()){
        auto task = _tasks[major][minor].back();
        task();
        _tasks[major][minor].pop_back();
    }
    _frameCounter.next();
}

void FrameTaskManager::addTaskAtFrame(Frame frame, task_t task) {
    _tasks[frame.major][frame.minor].push_back(task);
}

void FrameTaskManager::addRecurrentTask(int minor, task_t task) {
    _recurrenTasks[minor].push_back(task);
}

Пример использования main.cpp

#include <iostream>

void task1(){
    std::cout << "task1!\n";
};
void task2(int a){
    std::cout << "task2 says: " << a << "\n";
};
void task3() {
    std::cout << "task3!\n";
};

int main() {
    FrameTaskManager frameTaskManager;
    frameTaskManager.addRecurrentTask(2,task1);
    frameTaskManager.addRecurrentTask(4,[a=int(4)](){task2(a);});
    frameTaskManager.addTaskAtFrame(FrameTaskManager::Frame(1,1), task3);

    for (int step=0; step<30; ++step) {
        std::cout << "[" << frameTaskManager.currentFrame().major 
                  << "," << frameTaskManager.currentFrame().minor << "]\n";

        frameTaskManager.step();
    }
}

выход:

[0,0]
[0,1]
[0,2]
task1!
[0,3]
[0,4]
task2 says: 4
[0,5]
[0,6]
[0,7]
[0,8]
[0,9]
[0,10]
[0,11]
[0,12]
[0,13]
[0,14]
[0,15]
[1,0]
[1,1]
task3!
[1,2]
task1!
[1,3]
[1,4]
task2 says: 4
[1,5]
[1,6]
[1,7]
[1,8]
[1,9]
[1,10]
[1,11]
[1,12]
[1,13]

Вопросы и советы

Это мой первый раз, играя вокруг с std::function любые советы или идеи? Что вам нравится в реализации? Что вам не нравится? Любые советы по стилю? Большое спасибо за ваши отзывы!

Я тоже не совсем понял, почему я не могу сделать следующее в FrameTaskManager.ч:

Frame(int major, int minor) : major(major), minor(minor) {}

Когда я делаю, что я получаю следующее компиляции ошибка:

prog.cc: In constructor 'FrameTaskManager::Frame::Frame(int, int)':
prog.cc:13:37: error: class 'FrameTaskManager::Frame' does not have any field named 'gnu_dev_major'
       Frame(int major, int minor) : major(major), minor(minor) {}
                                     ^~~~~
prog.cc:13:51: error: class 'FrameTaskManager::Frame' does not have any field named 'gnu_dev_minor'
       Frame(int major, int minor) : major(major), minor(minor) {}
                                                   ^~~~~

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



176
1
c++
задан 27 февраля 2018 в 08:02 Источник Поделиться
Комментарии
1 ответ

смешанная


  • FrameTaskManager::currentFrame() должно быть const.

  • Нет необходимости using task_t = std::function<void()>; в FrameTaskManager.cpp.


класс Frame


  • Я не думаю, что есть много пользы для предоставления по умолчанию-конструктор и значения по умолчанию для Frame. Это позволяет пользователю не думать о том, что значения должны быть... что на самом деле не очень хорошая вещь. (И в этом случае он дает ошибку, как описано ниже).

  • Frame(int major, int minor) могли бы использовать некоторые ошибки проверки (assert и кинуть), чтобы обеспечить небольшие рамки действителен.

  • major и minor вероятно, должны быть беззнаковые типы, с (minor по крайней мере) никогда не может быть отрицательным. minor можно также использовать меньшие интегрального типа при желании. Это не требует больше мысли, когда занимались арифметикой, но это не обязательно плохо...

  • В любом случае, эти типы должны были using заявления или typedefв FrameManager класс, который может быть использован для передачи аргументов и т. д.

  • (Ошибка:) auto f = FrameTaskManager::Frame(0, 0) + (-5) дает Кадр (0, -5), который, вероятно, не желаемое поведение. Используя беззнаковые типы, как предложено выше, делает его более очевидным, что мы должны подумать / проверить эти вещи с самого начала (даже если это не реальная арифметическая легче).

  • operator+ текущая operator+ кажется, немного особый случай компромисса. Пре-инкремент оператор (или лучше, функция называется advance()) обеспечит необходимую функциональность. В advance(int major, int minor) версия может также быть добавлены для обработки изменения значений в случае необходимости.

  • В FrameCounter класс не нужен - нам просто нужен Frame _currentFrame член.


планирование задач


  • addTaskAtFrame() и addRecurrentTask() могли бы использовать некоторые проверки ошибок, чтобы гарантировать, что мы не добавить задачу на предыдущем кадре.

  • (Ошибка:) в std::map::operator[] на самом деле добавляет элемент на карту, если нет записи с таким ключом. Это делает текущий код для запуска запланированных заданий очень плохо, потому что размер _tasks и / или внутренней карте будет увеличиваться с каждым звонком step()!

  • Было бы намного чище, чтобы использовать std::multimap<Frame, task_t>. С соответствующим operator< для Frame.

  • (Еще одна вещь, чтобы быть в курсе с контейнерами функций такой: одно задание может попытаться добавить еще одну запланированную задачу в диспетчере рамки. Это повлечет за собой проблемы путем изменения _tasks или _recurrentTasks члены пока мы не переборем их в step().

    Мы могли бы защититься от этого во время выполнения путем добавления булевский элемент, который устанавливается в true для продолжительности step функции и утверждая против него в addTaskAtFrame() и addRecurrentTask(). Более надежным решением было бы копировать / перемещать задачи текущего кадра во временный контейнер перед их выполнением, поэтому мы можем свободно изменять самих контейнеров члена.

    Это все, что осталось в качестве упражнения для читателя ;) )



измененный код

#include <array>
#include <map>
#include <vector>
#include <functional>
#include <cassert>
#include <utility>

class FrameTaskManager {
public:
using task_t = std::function<void()>;

using frame_major_t = std::uint32_t;
using frame_minor_t = std::uint8_t;

static const auto frameSize = frame_minor_t{ 16 };

struct Frame {
Frame(frame_major_t major, frame_minor_t minor):
major(major), minor(minor) {
assert(minor < frameSize);
}

int major;
int minor;

void advance() {
assert(minor < frameSize);
++minor;
if (minor == frameSize) {
++major;
minor = frame_minor_t{ 0 };
}
}

friend bool operator<(Frame const& f1, Frame const& f2) {
return std::tie(f1.major, f1.minor) < std::tie(f2.major, f2.minor);
}
friend bool operator<=(Frame const& f1, Frame const& f2) {
return std::tie(f1.major, f1.minor) <= std::tie(f2.major, f2.minor);
}
};
void step();
Frame currentFrame() const { return _frame; }
void addTaskAtFrame(Frame frame, task_t task);
void addRecurrentTask(frame_minor_t minor, task_t task);

private:
Frame _frame = Frame{ 0, 0 };
std::array<std::vector<task_t>, frameSize> _recurrenTasks;
std::multimap<Frame, task_t> _tasks;
};

void FrameTaskManager::step() {
// Run recurrent tasks
for (auto& task : _recurrenTasks[_frame.minor]) {
task();
}

// Run and pop scheduled tasks
auto range = _tasks.equal_range(_frame);
for (auto i = range.first; i != range.second; ++i) {
i->second();
}
_tasks.erase(range.first, range.second);

_frame.advance();
}

void FrameTaskManager::addTaskAtFrame(Frame frame, task_t task) {
assert(_frame <= frame);
_tasks.emplace(frame, std::move(task));
}

void FrameTaskManager::addRecurrentTask(frame_minor_t minor, task_t task) {
assert(minor < frameSize);
_recurrenTasks[minor].push_back(std::move(task));
}


небольшой функции в заголовках

(Мнение)

Я думаю, что лучше сделать заголовок как можно более чистым (т. е. декларации) и ставить как можно больше в файле cpp. Определения классов в заголовочных файлах должны быть читаемы резюме коде / интерфейсах. Если определения функций необходимы в заголовке (шаблоны, inline-функции), я поставил их вне определения класса, чтобы держать его минимальным.

Однако, я не использовать IDE, который можно добавить определения функции в cpp файл, и сочетания клавиш, которые позволяют пропуск между файлами, а непосредственно между определениями функций и объявления. Без этого было бы намного больше печатать накладные делать

1
ответ дан 28 февраля 2018 в 01:02 Источник Поделиться