gpt4 book ai didi

C++ 存储带有可变参数的回调

转载 作者:行者123 更新时间:2023-12-01 14:29:27 28 4
gpt4 key购买 nike

我真的需要你的帮助。由于我是初学者,所以我一直坚持使用可变参数存储回调。

目标是存储 x 个回调及其 n 个类型为 y 的参数。我尝试使用模板,但坚持他们的推论。回调需要稍后在我现有的静态库中由其他更改回调参数的事件触发。所有回调函数总是/必须返回 void。

在我的 main.cpp 中我需要:

void testFunc_i(int i){
//do something
}
void subscribe_i(){
myLib::registerCallback(
"something", "INT",
&testFunc_i
);
}

void testFunc_ivf(int i, std::vector<float> f){
//do something
}
void subscribe_ivf(){
myLib::registerCallback(
"another string", "INT",
"something different", "FLOAT[]",
&testFunc_ivf
);
}
void testFunc_ifs(int i, float f, std::string s){
//do something
}
void subscribe_ifs(){
myLib::registerCallback(
"foo", "INT",
"bar", "FLOAT",
"text", "STRING",
&testFunc_ifs
);
}
int main(int argc, char** argv)
{
subscribe_i();
subscribe_ivf();
subscribe_ifs();
}

我在 myLib 中的内容是这样的:

namespace myLib{

class Subscriber
{
public:
Subscriber();
~Subscriber();
void addString(const std::string &s)
{
v.push_back(s);
}
void addType(const std::string &s)
{
t.push_back(s);
}
void setCallback(callback_t callbackFn) //???
{
callback = callbackFn; // ???
}

void call( /* ??? */ )
{
// ???
callback(); // ???
}
private:
callback_t callback; // ???

std::vector<std::strings>v;
std::vector<std::strings>t;
};

// if argument is a string
void add(unsigned &cnt, auto* subscriber, const std::string &value)
{
if (cnt%2==0) {
subscriber->addString(value);
}else{
subscriber->addType(value);
}
cnt++;
}

// if argument is a function
template <typename... Args>
void add(unsigned &cnt, auto* subscriber, void(*_callbackFn)(Args...) )
{
subscriber->setCallback(_callbackFn);
}

template <typename... Ts>
void registerCallback(Ts&&... ts)
{
auto subscriber = new Subscriber();
unsigned cnt = 0;
auto dummy = {(add(cnt,subscriber,ts), 0)...};
Store::getInstance().addSubsriber(subscriber); //store the subscriber object in a Singleton (working)
}
}

最佳答案

你真的不能这样做。

如果您在函数中使用可变参数,最好的办法是将这些参数转换为元组。

但是,一个元组不同于具有任何不同参数列表的任何其他元组,因此您不能将其存储在容器中以供稍后调用。

如果元组派生自公共(public)基类,您可以将它们存储起来直到使用(必须通过 virtual call() 方法进行双重分派(dispatch)) .这写起来不是很方便,因为它意味着几乎要重写 std::tuple 类型。

如果我是你,我会做相反的事情,即制作一个 lambda 来调用传递的回调,并将其转换为 std::function 存储在容器中供以后使用打电话。

此伪代码中的一些内容(未测试):

   template <typename F, typename... Args>
void registerCallback(F f, Args&&... args)
{
// Make a function that's calling the callback and store that instead
std::function<void()> laterCB = [&] { f(args...); };
// Simply store in your std::vector<std::function<void()>> container
Store::getInstance().addSubscriber(laterCB);
}

void triggerCB()
{
for (auto i : Store::getInstance().getCB())
*i();
}

另外一句话:

这种代码总是非常不安全,因为参数的类型可以是引用(语法中没有任何内容可以阻止这种情况)。稍后回调时,引用的对象可能不再存在。如果你必须编写一个库,你最好避免这样做,而是让你的界面非常严格以防止这种情况发生。

不安全使用示例:

// DON'T DO THAT
struct A { ... };
int processA(A & a) {
a.foo();
}

if (something())
{
A a;
yourLib.registerCallback(processA, a);
}

// Calls processA with a reference on a
// destructed object 'a' => so it'll crash
yourLib.triggerCB();

关于C++ 存储带有可变参数的回调,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59897767/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com