我正在尝试实现一个表示从基类继承的 TimedCallback 对象列表的 vector 。 它们包含一些基本变量,加上一个函数指针,这是主要特征。 该函数应该能够返回任何类型并具有任何参数。 我将它们作为 lambda 传递给函数,到目前为止没有任何问题。
这是相关代码:
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(); 指定任何模板;方法。 请帮助我了解这怎么可能。
编辑:
我的意思是,我想完全有可能沿着
定义一堆 typedefusing 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;
};
任务分配器.h:
void ScheduleJob(const std::string& name, const std::string& time, const Callback& func, int repeatCount = 0);
void RunScheduledJobs();
std::vector<TimedCallback> m_TimerCallbackList;
任务分配器.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)和自动的错误。解决方案包括键入签名而不是自动签名,或者删除/添加适当的 const(?)。不确定这是同一个错误还是我的实现仍然不正确。 (漏洞:https://stackoverflow.com/a/30344737/8263197)
我不确定如何使用此 typedef 使 RetVal 支持任何值。 我试过了
template<typename T>
struct CallbackReturnValue {
CallbackReturnValue(T v) : value(v) {}
T value;
};
但之后我仍需要对其他支持方法进行模板化。我在这里弄错了什么?
最佳答案
看来您正在尝试重新发明 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;
};
如果您需要回调返回的值,则需要更复杂的东西。但是,请尝试一次迈出这一步。
经过一番澄清后,其意图似乎是将返回值存储在某种容器中。为了使这项工作有效,需要有一种所有返回值都可以转换为的类型(例如让所有返回值都是从公共(public)基类派生的类)。我将继续提供一种可行的方法。
第一步是定义通用返回类型。通过将它设为 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();
}
关于c++ - 包含具有不同类型函数指针的对象的 vector ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53241666/