从 C++ => C 包装的 API(多线程)传输错误字符串

Transferring error strings from C++ => C wrapped API (multi-threaded)

本文关键字:API 多线程 字符串 传输错误 包装 C++ gt      更新时间:2023-10-16

我目前正在包装一个现有的C++库,以便在Go中使用。要做到这一点,我必须用C垫片包裹它,然后从Go访问它。当C++API引发异常,并且我当前正在使用errno中的自定义值来传递错误条件时,我正在尝试找出一种方法来传递异常中的错误字符串。

我的绑定从C++API获取已分配的指针,并不透明地包装它们=>C=>Go。如果我编写了第三方库,我就可以将最后一个错误存储在类中,但显然我需要将其关联到其他地方。

因为Go/cgo是多线程的,所以我最初的想法是使用线程本地存储来存储错误字符串,并允许Go通过GetLastError()函数获取它。我发现的问题是,在OSX上,没有线程本地存储(据我所知)。

免责声明:我的C/C++技能是新手

我如何从C++端的异常中获取错误字符串,并以特定于线程的方式存储它,使其可用于我的C包装器(即可用于go绑定),而不是在我的每个潜在错误函数中返回一个特殊的结构?或者,我唯一的选择真的是让我的所有C函数都使用输出参数和一个特殊的错误结构返回类型吗?

编辑:(从上下文对象的建议中提出的想法)

如果我的C++->C填充程序像这样包装类:

// foo.h
#ifdef __cplusplus
extern "C" {
#endif
typedef void Thing;
const char* Thing_foo(Thing *ptr);
#ifdef __cplusplus
}
#endif
#endif
// foo.cpp
extern "C" {
const char* Thing_foo(Thing *ptr) {
return static_cast<CPP::Thing*>(ptr)->foo();
}
}

使用同时携带指针和最后一条错误消息的struct Thing,其效果是否与上下文基本相同?

typedef struct Thing { 
void *ptr; 
char *last_err;
} Thing;

您可以为使用上下文变量的C接口实现包装器。这样,您就可以将线程问题的控制权交给调用者。

这里有一个未经测试的例子:

包装纸.cpp

#include "wrapper.h"
struct context {
std::string last_error_message; 
};
char const * ctx_get_last_error(context_handle ctx) {
if(ctx->last_error_message.empty()) return 0;
return ctx.last_error_message.c_str();
}
extern "C" context_handle create_context() {        
return new context();
}
extern "C" void free_context(context_handle ctx) {
delete ctx;
}
extern "C" int my_lib_call(context_handle ctx, int some, char const * params) {
try {
lib_call(some, params);
}
catch(std::exception const & e) {
ctx->last_error_message = e.what();
return -1;
}
catch(...) {
ctx->last_error_message = "Unexpected error";
return -1;
}
return 0;
}

wrapper.h

#ifdef _cplusplus
extern "C" {
#endif
typedef struct context * context_handle;
context_handle create_context();
char const * ctx_get_last_error(context_handle ctx);
int my_lib_call(context_handle ctx, int some, char const * params);
#ifdef _cplusplus
}
#endif

如果您的平台都是POSIX平台,则可以使用pthread_[g|s]etspecific来处理线程特定的数据。pthread_key_create上的opengroup页面包含如何使用此功能的示例。

如果可用(_Thread_local是新的C和C++标准C11和C++11的一部分),您可以创建一个小型包装器库,该库使用线程本地变量,并在这些函数不可用时使用pthread函数作为后备。

AFAIR,windows有类似的功能,但我不是这方面的专家。

如果您真的不想传递额外的参数,您可能需要实现IPC(http://en.wikipedia.org/wiki/Inter-process_communication)像队列或命名管道这样的系统来传递您的数据。