Вектор с объектами, которые имеют функциональные указатели различного типа - PullRequest
0 голосов
/ 10 ноября 2018

Я пытаюсь реализовать вектор, представляющий список объектов TimedCallback, которые наследуются от базового класса. Они содержат некоторые основные переменные, а также указатель на функцию, которая является основной функцией. Функция должна иметь возможность возвращать любой тип и иметь любые параметры. Я передаю их как лямбды в функции, никаких проблем пока нет.

Это соответствующий код:

std::vector<std::unique_ptr<TimedCallbackBase>> m_TimerCallbackList;

struct TimedCallbackBase {
    TimedCallbackBase() = default;
    virtual ~TimedCallbackBase() = default;

    template<typename T> T run()
    {
        return dynamic_cast< TimedCallback<T> & >(*this).Run();
    }
    template<typename T> std::string name()
    {
        return dynamic_cast< TimedCallback<T> & >(*this).Name;
    }
    template<typename T> TaskTimer time()
    {
        return dynamic_cast< TimedCallback<T> & >(*this).Time;
    }
    template<typename T> int repeatcount()
    {
        return dynamic_cast< TimedCallback<T> & >(*this).RepeatCount;
    }
};

template <typename Fu>
struct TimedCallback : TimedCallbackBase {
    TimedCallback(const std::string& name, const std::string& time, Fu f, int r) : Name(name), Run(f), Time(time), RepeatCount(r) {}
    std::string Name;
    Fu Run;
    TaskTimer Time;
    int RepeatCount;
};

template<typename Fu>
void Schedule(const std::string& name, const std::string& time, Fu f, int repeatCount = 0) {
    TimedCallback cb(name, time, f, repeatCount);
    if (!vec_contains(m_TimerCallbackList, cb)) {
        m_TimerCallbackList.push_back(cb);
    }
    else { Global->Log->Warning(title(), "Callback '"+name+"' already exists."); }
}

Моя проблема в этом методе. Я не могу правильно запустить указатели функций.

void _RunTimers() {
    if (m_TimerCallbackList.size() > 0) {
        for (auto &t : m_TimerCallbackList) {
            if (t != nullptr) {

                std::string _name = t.get()->name(); // wrong syntax, or signature?
                TaskTimer  _time = t.get()->time();
                int  _repeatcount = t.get()->repeatcount();
                //auto _run = t.get()->run(); ??

                Global->t("Callback name: " + _name);
                Global->t("Callback time: " + _time.GetRealTimeAsString());
                Global->t("Callback count: " + its(_repeatcount));
            }
        }
    }
    else { Global->Log->Warning(title(), "No timed tasks to run at this time."); }
}

Я собираюсь использовать код, подобный следующему:

Task->Schedule("testTimer", "10sec", [&]{ return Task->Test("I'm a 10sec timer."); });

_RunTimers();

Я чувствую, что довольно далеко не правильно делаю. Я не хочу указывать какие-либо шаблоны для _RunTimers (); метод. Пожалуйста, помогите мне понять, как это возможно.

Редактировать:

Я имею в виду, я думаю, что вполне возможно просто определить группу typedefs в соответствии с

using int_func = std::function<int()>;

для каждого возможного случая, а затем перегрузить мой объект-обертку, но я искал что-то более динамичное и защищенное от изменений.

Редактировать 2: После внесения предложенных изменений

Примечание: я переименовал методы ради неясности. (Метод Test () здесь не включен, он просто выполняет std :: cout строкового параметра)

главный:

Callback myCallback = make_callback(&TaskAssigner::Test, "I'm a 5sec timer.");

Task->ScheduleJob("timer1", "5sec", myCallback, -1);
Task->RunScheduledJobs();

Утилиты:

typedef double RetVal;
typedef std::function<RetVal()> Callback;

template <class F, class... Args>
Callback make_callback(F&& f, Args&&... args)
{
    auto callable = std::bind(f, args...);                  // Here we handle the parameters
    return [callable]() -> RetVal{ return callable(); };    // Here we handle the return type
}

struct TimedCallback  {
    TimedCallback(cstR name, cstR time, Callback f, int r)
        : Name(name), Run(f), Time(time), RepeatCount(r) {}

    RetVal operator()() const { return Run(); }

    bool operator==(const TimedCallback& other) {
        if (Name == other.Name && RepeatCount == other.RepeatCount) { return true; }
        return false;
    }

const bool operator==(const TimedCallback& other) const {
    if (Name == other.Name && RepeatCount == other.RepeatCount) { return true; }
    return false;
}

    std::string Name;
    Callback Run;
    TaskTimer Time;
    int RepeatCount;
};

TaskAssigner .h:

void ScheduleJob(const std::string& name, const std::string& time, const Callback& func, int repeatCount = 0);

void RunScheduledJobs();

std::vector<TimedCallback> m_TimerCallbackList;

TaskAssigner.cpp:

void TaskAssigner::ScheduleJob(const std::string& name, const std::string& time, const Callback& func, int repeatCount) {

    TimedCallback cb(name, time, func, repeatCount);

    if (!vec_contains(m_TimerCallbackList, cb)) {
        m_TimerCallbackList.emplace_back(cb);
    }
    else { Global->Log->Warning(title(), "Callback '" + name + "' already added."); }
}

void TaskAssigner::RunScheduledJobs() {
    if (m_TimerCallbackList.size() > 0) {
        for (auto &t : m_TimerCallbackList) 
        {
            RetVal value = t();
            //Global->t("Callback result: " + std::to_string(value));
            Global->t("Callback name: " + t.Name);
            Global->t("Callback time: " + t.Time.GetRealTimeAsString());
            Global->t("Callback count: " + its(t.RepeatCount));
        }
    }
    else { Global->Log->Warning(title(), "No timed tasks to run at this time."); }
}

Текущая проблема:

Компилятор говорит: C3848: выражение, имеющее тип 'const std :: _ Bind , const char (&) [18]>' потеряло бы некоторые const-volatile квалификаторы для вызова .....

Я пытался исследовать, и некоторые упоминали ошибку с VS 2013, связанную с bind и auto. Решения включают ввод подписи, а не ее автоматическое изменение, или удаление / добавление правильного const (?). Не уверен, что это та же ошибка или моя реализация все еще неверна. (Ошибка: https://stackoverflow.com/a/30344737/8263197)

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

    template<typename T>
struct CallbackReturnValue {
    CallbackReturnValue(T v) : value(v) {}
    T value;
};

но тогда мне все равно придется шаблонизировать другие методы поддержки. Что я здесь ошибаюсь?

1 Ответ

0 голосов
/ 11 ноября 2018

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

typedef std::function<void()> Callback;

struct TimedCallback {
    TimedCallback(const std::string& name, const std::string& time, const Callback & f, int r) :
        Name(name),
        Run(f),
        Time(time),
        RepeatCount(r)
    {}

    // Not wise to differentiate names by case (run vs. Run),
    // but this form might be instructive as to how this
    // setup would fit into your existing _RunTimers().
    // By the way, _RunTimers is a reserved identifier.
    void run()
    {
        Run();
    }

    std::string Name;
    Callback Run;
    TaskTimer Time;
    int RepeatCount;
};

Нужно что-то более сложное, если вам нужны значения, возвращаемые обратными вызовами. Тем не менее, попробуйте сделать один шаг за раз.


После некоторых разъяснений кажется, что цель состоит в том, чтобы хранить возвращаемые значения в каком-то контейнере. Чтобы это работало, необходим тип, в который могут быть преобразованы все возвращаемые значения (например, все возвращаемые значения должны быть классами, производными от общего базового класса). Я перейду к тому, как это можно сделать.

Первым шагом является определение общего типа возвращаемого значения. Сделав это typedef, я могу абстрагироваться от этого выбора в следующем коде.

typedef /* fill this in */ RetVal;

Далее мы пересматриваем определения Callback и run() для учета этого типа.

typedef std::function<RetVal()> Callback;

RetVal run()
{
    return Run();
}

В противном случае определение TimedCallback остается прежним, но я бы добавил удобный слой, чтобы упростить построение этих обратных вызовов. Вдохновлены такими впечатляющими именами, как "make_pair" и "make_tuple":

template <class F, class... Args>
Callback make_callback(F&& f, Args&&... args)
{
    auto callable = std::bind(f, args...);               // Here we handle the parameters
    return [callable]() -> RetVal{ return callable(); }; // Here we handle the return type
}

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

// Example function that takes a parameter.
std::string hello(const std::string & world)
{
    std::cout << "Hello " << world << "!\n";
    return world;
}

// Example main function to see that this can work.
int main()
{
    // Placeholder for m_TimerCallbackList.
    std::vector<TimedCallback> timer_list;

    // Instead of Task->Schedule(), I'll emplace on a vector.
    timer_list.emplace_back("testTimer", "10sec",
                            make_callback(hello, "Earth"),
                            1);

    // Instead of RunTimers(), I'll manually run the first callback.
    RetVal value = timer_list[0].run();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...