В настоящее время я обертываю существующую библиотеку C ++ для использования в Go. Чтобы сделать это, я должен обернуть его C-прокладкой, чтобы затем получить к нему доступ из Go. Хотя C ++ API вызывает исключения, и в настоящее время я сообщаю об ошибках, используя пользовательские значения в errno
Я пытаюсь выяснить способ передачи строк ошибок из исключений.
Мои привязки берут выделенные указатели из API C ++ и упаковывают их непрозрачно => C => Go. Если бы я создал стороннюю библиотеку, я мог бы хранить последнюю ошибку прямо в классе, но мне нужно было бы связать ее где-то еще, очевидно.
Поскольку Go / cgo является многопоточным, моя первоначальная идея состояла в том, чтобы использовать локальное хранилище потока для хранения строки ошибки и позволить Go получить ее через GetLastError()
функция. Проблема, которую я обнаружил, заключается в том, что в OSX нет локального хранилища потоков (насколько я понимаю).
Отказ от ответственности: мои навыки C / C ++ являются новичками
Как я могу взять строку ошибки из исключения, которое есть у меня на стороне C ++, и сохранить ее в виде потока, чтобы сделать ее доступной для моей оболочки C (то есть, доступной для моих привязок Go), за исключением фактического использования вернуть специальную структуру в каждой из моих функций с потенциальной ошибкой? Или мой единственный вариант — заставить все мои функции C использовать выходные параметры и специальный тип возвращаемой структуры ошибок?
Редактировать: (мысли, возникшие из-за предложения объектов контекста)
Если мой C ++ -> C shim оборачивает классы так:
// 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, который использует переменную контекста. Таким образом, вы даете контроль над проблемой потоков для вызывающей стороны.
Вот непроверенный пример:
wrapper.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 содержит пример того, как использовать эту функцию.
Вы можете создать небольшую библиотеку-оболочку, которая использует локальные переменные потока, если они доступны (_Thread_local
является частью новых стандартов C и C ++, C11 и C ++ 11) и использует функции pthread в качестве запасного варианта, когда они недоступны.
AFAIR, Windows имеет аналогичные функции, но я не эксперт в этом.
Если вы действительно не хотите передавать дополнительный параметр, вам может понадобиться реализовать IPC (http://en.wikipedia.org/wiki/Inter-process_communication) система как очередь или именованный канал для передачи ваших данных.