提问人:Default 提问时间:9/28/2023 最后编辑:Default 更新时间:9/28/2023 访问量:79
将不同的函数存储在一个函数指针下
Storing different functions under one function pointer
问:
请允许我为我的问题提供一些背景信息:
我有一个大学作业,为我提供了一个名为 StringList 的类。这个类负责很多字符串操作,如插入、删除等。这些方法的函数签名的一些示例如下所示:
// Must be undoable
void insert_before(int index, const std::string &s)
{
//...
}
// Must be undoable
void remove_bofore(int index)
{
//...
}
void undo()
{
// Attempts to revert back to the last operation
}
该任务要求我实现一个堆栈,该堆栈能够提供 undo() 操作,该操作基本上可以反转之前发生的功能。例如,如果某些代码决定调用名为 insert_before() 的类方法,则 StringList 类必须使用堆栈来提供通过删除插入的字符串操作来恢复的功能。
实现堆栈似乎是一项相当微不足道的任务,但主要问题是试图弄清楚如何以最简单的方式实现 undo() 操作。我最初的想法是创建一个 Node 结构,除了允许它引用其他节点的标准下一个指针之外,还将包含一个函数指针变量,如下所示:
// Used in singly linked list stack
struct Node
{
Node* next;
void(*function)();
};
这有助于存储需要调用的所需函数,以便撤消()之前执行的操作。这是通过保存上次执行的完全相反的操作来完成的。例如,如果调用 insert_before() 方法,则其行为如下:
void insert_before(int index, const std::string &s)
{
// Perform the insertion...
stack.push(
remove_before(index) // Storing the function that needs to be called by undo()...
);
}
我在这种方法中面临的主要问题是,我不确定是否允许我们在一个通用函数指针下存储不同的函数签名。函数指针显然需要一个函数,该函数的返回类型签名为 void,参数为 void 类型。但是,需要存储在其中的每个函数都有不同的签名,而这些只是 2 种方法,还有更多。
我可以继续为每个方法类型创建一个函数指针,但我认为为每个方法类型创建这么多不同的函数指针不是一个好主意。
那么,我该怎么做呢?我的意思是。。。是否可以将它们全部存储在一个函数指针下?或者我还能用它做些什么吗?
答:
您不需要不同的签名。签名将始终是 ,或类似的东西。 不带任何参数,因此需要撤消的任何其他参数都需要烘焙到要存储的函数中。void(StringList &)
undo()
使用捕获 lambda:
stack.push([index](StringList &list)
{
list.remove_before(index);
});
由于函数指针不能指向捕获 lambda,因此请改用。std::function
评论
std::function
void (*function)(StringList &)
std::function
将不同的函数签名存储在一个通用函数指针下
会使解决方案变得复杂。 你听说过命令设计模式吗? 当调用insert_before或删除函数时,生成命令并将命令缓存在动作向量中。 因此,当调用 Undo 时,只需pop_back向量并执行它即可。 示例代码如下:
struct StringList
{
std::vector<Action> actions;
void insert_before(int index, const std::string &s)
{
//...
collections.insert(collections.begin() + index, s);
Action a{.ac = ActionType::remove};
if (index == 0)
{
a.index = 0;
}
else
{
a.index = index - 1;
}
actions.push_back(a);
}
// Must be undoable
void remove_bofore(int index)
{
//...
Action a{.ac = ActionType::insert, .s = collections[index], .index = index};
collections.erase(collections.begin() + index);
actions.push_back(a);
}
void undo()
{
// Attempts to revert back to the last operation
if (actions.empty())
{
return;
}
auto action = actions.back();
actions.pop_back();
if (action.ac == ActionType::remove)
{
collections.erase(collections.begin() + action.index);
}
else if (action.ac == ActionType::insert)
{
collections.insert(collections.begin() + action.index, action.s);
}
}
void print() const
{
std::cout << "size:" << collections.size() << " action size:" << actions.size() << std::endl;
}
std::vector<std::string> collections;
};
int main()
{
StringList sl;
sl.insert_before(0, "a");
sl.print();
sl.undo();
sl.insert_before(0, "b");
sl.print();
sl.undo();
sl.insert_before(0, "c");
sl.print();
sl.undo();
sl.insert_before(0, "d");
sl.insert_before(0, "e");
sl.print();
sl.undo();
sl.undo();
sl.print();
return 0;
}
评论
clear()
clear()
std::function
std::variant
评论
std::function<void (UndoContext&)>
)std::stack<std::function<void()>>
stack.push([=]{ remove_before(index); })
std::stack