c ++如何将一个通用函数与多个地图对象绑定并将其称为清除函数
c++ how to bind a general function with multiple map object and call it clear function
我的系统定义了多个类似unordered_map
的数据,例如
std::unordered_map< int, int> int_map;
std::unordered_map< int, double> double_map;
std::unordered_map< string, string> string_map;
std::unordered_map< string, string> string_map_2;
...
std::unordered_map< string, vector<string> > string_string_map;
std::unordered_map< string, vector<string> > string_string_map_2;
每个地图包含不同的数据类型。这些与注册回调函数的框架(用 C 编写)一起使用。注册函数 (register_undo_handler
) 有两个参数:指向回调函数的指针和要传递给回调的数据。在我们的例子中,数据是一个映射指针。
//register our callback function to UNDO framework
register_undo_handler(clear_data, &int_map);
//or :
register_undo_handler(clear_data, &string_map);
// this is a sketch of the callback function, which frees the map resources
void clear_data(void *data)
{
data->clear();
}
如果UNDO发生,UNDO框架将调用每个定义的回调函数,例如clear_data
,并传递注册数据,转换为void*
,作为这样的参数
// For each registered callback function, run with registered parameter
(*registered_callback_func)(data);
我们想要一个可以释放任何地图的回调函数。
对实施有何建议?
编辑:
我只是意识到如果只是清除那些地图,它并不比 更容易(这仍然需要完成)...我将把它留在这里作为 general-propose 处理程序到 C 处理程序解决方案的参考. (顺便说一句,技术上它只需要一个回调函数(第一个参数))
首先,我强烈建议迁移到 c++
撤消框架
但是您实际上可以使用单个处理程序(第一个参数)将其归档。
重点是使用 type-erased 处理程序(下面代码中的std::function
)作为数据(第二个参数)传递,并在包装处理程序(第一个参数)中调用它
注意:你还没有添加 register_undo_handler
的签名所以我猜是
void register_undo_handler(void(*)(void), void*);
代码(Wandbox)
#include <functional>
#include <list>
#include <stack>
#include <iostream>
using namespace std;
void register_undo_handler(void(*callback)(void*), void* data){
callback(data); //I'd not gonna implement all undo in C so let me do this instead
}
void the_callback_wrapper(void* cb){
(*static_cast<function<void()>*>(cb))();
}
int main(){
list<function<void()>> undos;
//and your maps, they should have same lifetime
int a=2,b=3;
//do something...
undos.emplace_back([]{std::cout << "I'd like to undo this - 1\n";});
register_undo_handler(the_callback_wrapper, (void*)(&undos.back()));
//undo with local variables, no problem
undos.emplace_back([&]{std::cout << "I'd like to undo this - " << a << '\n';});
register_undo_handler(the_callback_wrapper, (void*)(&undos.back()));
undos.emplace_back([&]{std::cout << "I'd like to undo this - " << b << '\n';});
register_undo_handler(the_callback_wrapper, (void*)(&undos.back()));
//modify local variable, no problem
undos.emplace_back([&]{std::cout << "I'd like to modify a to 100\n"; a=100;});
register_undo_handler(the_callback_wrapper, (void*)(&undos.back()));
std::cout << "a should be 100 : a = " << a << '\n';
//use multiple local variable, no problem
undos.emplace_back([&]{std::cout << "I'd like to modify **both a and b** to 10000\n"; a=b=100000;});
register_undo_handler(the_callback_wrapper, (void*)(&undos.back()));
std::cout << a << ' ' << b;
}
旁注:如果您可以使用全局变量来存储其撤消仿函数,您也可以将它们的索引作为数据传递并让回调决定调用哪个仿函数。
如果您只是不想重复代码,您可以使用单个 模板 来完成。
#include <functional>
#include <unordered_map>
#include <iostream>
using namespace std;
void register_undo_handler(void(*callback)(void*), void* data){
callback(data); //I'd not gonna implement all undo in C so let me do this instead
}
template<typename clearable>
void Erase(void* data){
((clearable*)data)->clear();
}
int main(){
std::unordered_map<int, int> int_map;
std::unordered_map<int, double> double_map;
double_map[0] = int_map[0] = 0;
//you can wrap this into a function to avoid type types yourself
register_undo_handler(&Erase<decltype(int_map)>, (void*)&int_map);
register_undo_handler(&Erase<decltype(double_map)>, (void*)&double_map);
std::cout << int_map.size() << ' ' << double_map.size();
}
调用“撤消”回调时,有两条信息可用,即调用函数的地址和提供给函数的 pointer-sized 数据。您的要求是第一部分不发生变化,因此第二部分需要告诉我们所涉及的地图类型。一个大问题是,一旦将指针转换为 void*
,就无法再恢复其原始类型。因此,为了实现既定目标,您需要一种方法来使用原始指针以外的其他东西来识别映射。一种可能性是将指针包装在一个可以记住原始类型的对象中。这可能会变得混乱,尤其是当您需要动态分配这些包装器对象时。对此进行编码的开销比编写多个回调函数要多。同样,运行时开销也没有节省(时间和 space)。这可能不是你想要做的。
为每个类型写一个回调就简单多了。只需几行代码即可定义回调。每个函数编译成少量的目标代码;总而言之,编译函数几乎是任何解决方案所需的理论最小目标代码量。 (每个映射类型的 clear
函数都是一个不同的函数,因此它需要不同的函数调用才能调用它们中的每一个。我超过了最小值,因为我检查了 null。)
template <class T>
void call_clear(void * ptr)
{
if ( ptr )
static_cast<T*>(ptr)->clear();
}
注册其中一个回调在语法上有点麻烦(例如 register_undo_handler( call_clear<decltype(int_map)>, &int_map);
),因此可能需要编写一个包装器来处理注册。这个包装器可以推导模板参数,减少重复输入的数量。编译器应在任何优化级别内联此函数,从而不会产生额外的运行时开销。
template <class T>
void register_undo_call_clear(T & map)
{
register_undo_handler(call_clear<T>, &map);
}
至此,回调注册就完成了,例如register_undo_call_clear(int_map);
.
我的系统定义了多个类似unordered_map
的数据,例如
std::unordered_map< int, int> int_map;
std::unordered_map< int, double> double_map;
std::unordered_map< string, string> string_map;
std::unordered_map< string, string> string_map_2;
...
std::unordered_map< string, vector<string> > string_string_map;
std::unordered_map< string, vector<string> > string_string_map_2;
每个地图包含不同的数据类型。这些与注册回调函数的框架(用 C 编写)一起使用。注册函数 (register_undo_handler
) 有两个参数:指向回调函数的指针和要传递给回调的数据。在我们的例子中,数据是一个映射指针。
//register our callback function to UNDO framework
register_undo_handler(clear_data, &int_map);
//or :
register_undo_handler(clear_data, &string_map);
// this is a sketch of the callback function, which frees the map resources
void clear_data(void *data)
{
data->clear();
}
如果UNDO发生,UNDO框架将调用每个定义的回调函数,例如clear_data
,并传递注册数据,转换为void*
,作为这样的参数
// For each registered callback function, run with registered parameter
(*registered_callback_func)(data);
我们想要一个可以释放任何地图的回调函数。 对实施有何建议?
编辑:
我只是意识到如果只是清除那些地图,它并不比
首先,我强烈建议迁移到 c++
撤消框架
但是您实际上可以使用单个处理程序(第一个参数)将其归档。
重点是使用 type-erased 处理程序(下面代码中的std::function
)作为数据(第二个参数)传递,并在包装处理程序(第一个参数)中调用它
注意:你还没有添加 register_undo_handler
的签名所以我猜是
void register_undo_handler(void(*)(void), void*);
代码(Wandbox)
#include <functional>
#include <list>
#include <stack>
#include <iostream>
using namespace std;
void register_undo_handler(void(*callback)(void*), void* data){
callback(data); //I'd not gonna implement all undo in C so let me do this instead
}
void the_callback_wrapper(void* cb){
(*static_cast<function<void()>*>(cb))();
}
int main(){
list<function<void()>> undos;
//and your maps, they should have same lifetime
int a=2,b=3;
//do something...
undos.emplace_back([]{std::cout << "I'd like to undo this - 1\n";});
register_undo_handler(the_callback_wrapper, (void*)(&undos.back()));
//undo with local variables, no problem
undos.emplace_back([&]{std::cout << "I'd like to undo this - " << a << '\n';});
register_undo_handler(the_callback_wrapper, (void*)(&undos.back()));
undos.emplace_back([&]{std::cout << "I'd like to undo this - " << b << '\n';});
register_undo_handler(the_callback_wrapper, (void*)(&undos.back()));
//modify local variable, no problem
undos.emplace_back([&]{std::cout << "I'd like to modify a to 100\n"; a=100;});
register_undo_handler(the_callback_wrapper, (void*)(&undos.back()));
std::cout << "a should be 100 : a = " << a << '\n';
//use multiple local variable, no problem
undos.emplace_back([&]{std::cout << "I'd like to modify **both a and b** to 10000\n"; a=b=100000;});
register_undo_handler(the_callback_wrapper, (void*)(&undos.back()));
std::cout << a << ' ' << b;
}
旁注:如果您可以使用全局变量来存储其撤消仿函数,您也可以将它们的索引作为数据传递并让回调决定调用哪个仿函数。
如果您只是不想重复代码,您可以使用单个 模板 来完成。
#include <functional>
#include <unordered_map>
#include <iostream>
using namespace std;
void register_undo_handler(void(*callback)(void*), void* data){
callback(data); //I'd not gonna implement all undo in C so let me do this instead
}
template<typename clearable>
void Erase(void* data){
((clearable*)data)->clear();
}
int main(){
std::unordered_map<int, int> int_map;
std::unordered_map<int, double> double_map;
double_map[0] = int_map[0] = 0;
//you can wrap this into a function to avoid type types yourself
register_undo_handler(&Erase<decltype(int_map)>, (void*)&int_map);
register_undo_handler(&Erase<decltype(double_map)>, (void*)&double_map);
std::cout << int_map.size() << ' ' << double_map.size();
}
调用“撤消”回调时,有两条信息可用,即调用函数的地址和提供给函数的 pointer-sized 数据。您的要求是第一部分不发生变化,因此第二部分需要告诉我们所涉及的地图类型。一个大问题是,一旦将指针转换为 void*
,就无法再恢复其原始类型。因此,为了实现既定目标,您需要一种方法来使用原始指针以外的其他东西来识别映射。一种可能性是将指针包装在一个可以记住原始类型的对象中。这可能会变得混乱,尤其是当您需要动态分配这些包装器对象时。对此进行编码的开销比编写多个回调函数要多。同样,运行时开销也没有节省(时间和 space)。这可能不是你想要做的。
为每个类型写一个回调就简单多了。只需几行代码即可定义回调。每个函数编译成少量的目标代码;总而言之,编译函数几乎是任何解决方案所需的理论最小目标代码量。 (每个映射类型的 clear
函数都是一个不同的函数,因此它需要不同的函数调用才能调用它们中的每一个。我超过了最小值,因为我检查了 null。)
template <class T>
void call_clear(void * ptr)
{
if ( ptr )
static_cast<T*>(ptr)->clear();
}
注册其中一个回调在语法上有点麻烦(例如 register_undo_handler( call_clear<decltype(int_map)>, &int_map);
),因此可能需要编写一个包装器来处理注册。这个包装器可以推导模板参数,减少重复输入的数量。编译器应在任何优化级别内联此函数,从而不会产生额外的运行时开销。
template <class T>
void register_undo_call_clear(T & map)
{
register_undo_handler(call_clear<T>, &map);
}
至此,回调注册就完成了,例如register_undo_call_clear(int_map);
.