在 MSVC6 中,我需要在 C++ 中使用 map 并提供包装函数以在 c 中使用它们来添加、删除和获取大小。我想在不使用 C++ 中的任何类的情况下做到这一点。这将由线程使用,每个线程都有一个结构句柄,该句柄将void *
作为参数传递给 C++ 包装函数,我希望 C++ 代码将其转换void *
为映射并执行操作。我无法将其转换为void *
C++ 中的地图,任何解决方案都会有所帮助。
2 回答
C 代码将需要void
从 C++ 代码中获取指针,执行类似的操作
void *get_map_from_cpp(); /* declare the function */
void *our_map = get_map_from_cpp();
whereget_map_from_cpp()
将(在 C++ 中)定义为类似的东西;
#include <map>
#include <string>
extern "C" void *get_map_from_cpp()
{
std::map<std::string, float> *the_map = new std::map<std::string, float>;
return static_cast<void *>(the_map);
}
但它并不止于此。要将值插入映射中,我们需要将值传入,例如,在 C 中
void insert_to_map(void *, const char *str, float f); /* declaration */
insert_to_map(our_map, "Everything", 42.0);
whereinsert_to_map()
也必须在 C++ 中定义,例如
extern "C" void insert_to_map(void *m, const char *str, float f)
{
std::map<std::string, float> *the_map;
the_map = static_cast< std::map<std::string, float> *>(m);
std::string the_str(str);
(*the_map)[the_str] = f;
}
类似地,aretrieve_from_map()
可以实现为
extern "C" float retrieve_from_map(void *m, const char *str)
{
std::map<std::string, float> *the_map;
the_map = static_cast< std::map<std::string, float> *>(m);
std::string the_str(str);
std::map<std::string, float>::const_iterator i = the_map->find(the_str);
if (i != the_map->end())
return i->second;
else
return 0.0f; // string not found in map, so return 0.0
}
从 C 调用的函数必须提供一个纯 C 接口——这意味着 C 代码不能直接接触 C++ 类型。其次,到 C++ 的映射必须仅在 C++ 代码中完成,因为 C 编译器不会理解这些结构。这意味着函数必须只接受或返回在 C 中有意义的类型,函数必须像extern "C"
C++ 编译器一样声明(以便可以从 C 调用),并且函数的主体必须处理来自 C 类型的映射到 C++。
这确实依赖于 C 和 C++ 编译器之间的兼容性(例如,来自同一供应商、兼容的 ABI 等)。
我要试一试,因为我喜欢猜测:
我假设您有一个传递给 create thead 函数的函数,如下所示:
int callback(void* userdata);
因此,让我们创建一个访问地图的回调函数:
// In the header file: "my_callback.h" we first need to declare that this function will be used from a C file, this is so the compiler will name the function according to the C standard and not C++.
#ifdef __cplusplus
extern "C" {
#endif
// Declare your function:
int my_callback(void * map_handle);
#ifdef __cplusplus
extern "C" {
#endif
现在,在 c++ 文件中确保包含头文件:
#include "my_callback.h"
int my_callback(void * map_handle) {
// First, for convenience, let us define the map type
using mapType = std::map<std::string, int>;
// First cast the void * to a the correct map * pointer
mapType *mapPtr = static_cast<mapType*>(map_handle);
mapPtr->insert( "Test", 1 ); // Insert
cout << (*mapPtr)["Test"]; // read
// Or you can assign it to a reference to hide away the pointer syntax:
mapType & mapRef = *static_cast<mapType*>(map_handle);
mapRef.insert( "Test", 1 ); // Insert
cout << mapRef["Test"]; // read
// (...)
}
有几点值得注意:
我们被允许使用
static_cast
,因为输入是一个void
指针,您应该在创建线程时传递目标映射的地址,然后静态转换为 void 指针,然后从 void 指针到相同类型的定义是明确的。这绝不是线程安全的。同时从多个线程访问同一资源时必须特别小心。如果您打算只有一个映射并让所有线程访问该映射,则应包含一个互斥锁以确保一次只有一个线程访问它。一个很好的方法是简单地创建一个包含映射和互斥锁的小结构:
struct map_n_mutex { mapType 映射;some_mutex_t 互斥体;// 不确定在 windows 上互斥锁的正确类型是什么。};
在您的线程初始化阶段:
// Create map
map_n_mutex user_data;
// Create thread (I'm making this function up, use what you have been using)
create_thread( my_callback, &user_data); // pass a pointer to userdata
如果您采用这种方法,请记住您需要强制转换map_n_mutex
,而不是mapType
在回调函数内部。