在 C++ 中使用 map,并提供包装函数以在 C 语言中使用它

using map in C++ and providing wrapper functions to use that in C

本文关键字:函数 包装 语言 C++ map      更新时间:2023-10-16

在 MSVC6 中,我需要在 C++ 中使用 map,并提供包装函数以在 c 中使用它们,仅用于添加、删除和获取大小。我想在不使用任何类的情况下C++这样做。这将由线程使用,每个线程都有一个结构句柄,该句柄将有一个void *,该句柄将作为参数传递给C++包装器函数,我希望C++代码将此void *转换为映射并进行操作。我无法将void *转换为地图,C++任何解决方案都会有所帮助。

我要试一试,因为我喜欢猜测:

我假设你有一个传递给创建 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
// (...)
}

有几点值得注意:

  1. 我们被允许使用static_cast因为输入是一个void指针,您应该在创建线程时传递目标映射的地址,并静态转换为 void 指针,然后从 void 指针到同一类型是明确定义的。

  2. 这绝不是线程安全的。同时从多个线程访问同一资源时必须特别小心。如果您打算只有一个映射并让所有线程访问该映射,则应包含互斥锁以确保一次只有一个线程访问它。一个漂亮的方法是简单地创建一个包含映射和互斥锁的小结构:

    结构map_n_mutex { 地图类型地图; some_mutex_t互斥锁;不确定窗口上互斥锁的正确类型是什么。 };

在线程初始化阶段:

// 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回调函数内部。

C 代码需要从C++代码中获取void指针,执行类似操作

void *get_map_from_cpp();   /* declare the function */
void *our_map = get_map_from_cpp();

其中get_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);

其中insert_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;
}

同样,retrieve_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 中有意义的类型,函数必须声明为 C++ 编译器的extern "C"(以便可以从 C 调用它们),并且函数的主体必须处理从 C 类型到 C++ 的映射。

这确实依赖于 C 和 C++ 编译器之间的兼容性(例如,来自同一供应商、兼容的 ABI 等)。