При написании UDF в MySQL я застрял в вызове функции C++ из c, которая должна возвращать char *
. Я пробовал:
encrypt.cpp
string encrypt(string Data)
{
...
//some encryption logic
...
return encryptStr; //encryptStr is string
}
Чтобы вызвать указанную выше функцию из c, я использую extern c
в том же файле, что и:
extern "C" char * c_encrypt(char *bar)
{
std::string str = encrypt(std::string(bar));
return what_to_do_to_return_char*;
}
UDF.c
char* Encrypt_UDF( UDF_INIT* initid, UDF_ARGS* args, char* result, unsigned long* length, char* is_null, char* error )
{
const char* arg = args->args[0];
char * encryptData = c_encryt(arg); //calling c_encrypt()
return encryptData ;
}
Итак, что мне делать в c_encrypt()
, чтобы вернуть char *
?
Это вопрос собственности. Должен ли вызывающий Encrypt_UDF
помнить, что им нужно освободить возвращаемый буфер? Если нет, нужна ли вам потокобезопасность? Будет ли проблемой наложить требование, чтобы вся программа была связана с компилятором / компоновщиком, поддерживающим C++?
@StoryTeller: Похоже, кто-то, знакомый с mySql, может правильно ответить на этот вопрос.
Согласно Документация MySQL ваша функция должна иметь следующий прототип:
extern "C" char *encrypt(UDF_INIT *initid, UDF_ARGS *args,
char *result, unsigned long *length,
char *is_null, char *error);
Затем вы должны использовать параметры result
и length
, чтобы "вернуть" вашу строку, если она умещается в 255 байтах (согласно странице документации это):
extern "C" char *encrypt(UDF_INIT *initid, UDF_ARGS *args,
char *result, unsigned long *length,
char *is_null, char *error)
{
std::string str = encrypt_impl(args->args[0]);
strcpy(result, str.c_str());
*length = str.size();
return result;
}
encrypt_init
and encrypt_deinit
functions as described здесь and define a data structure that you will use to share resource between all three functions:
struct EncryptData
{
std::string encryptedStr;
};
extern "C" bool encrypt_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
/* Use non-throwing allocation because there is no one to catch the exception */
initid->ptr = (char *) new (std::nothrow) EncryptData();
/* Return false if allocation failed */
if (!initid->ptr)
return false;
return true;
}
extern "C" void encrypt_deinit(UDF_INIT *initid)
{
delete (EncryptData *) initid->ptr;
}
extern "C" char *encrypt(UDF_INIT *initid, UDF_ARGS *args,
char *result, unsigned long *length,
char *is_null, char *error)
{
EncryptData *data = (EncryptData *) initid->ptr;
data->encryptedStr = encrypt_impl(args->args[0]);
/* Can't return c_str() here, since the return type is non-const */
return &data->encryptedStr[0];
}
Вы также можете использовать initid->ptr
для самого строкового буфера, как предлагается здесь, используя комбинацию malloc/realloc/free
, но я думаю, что всегда лучше использовать struct
или даже class
в качестве общего шаблона для совместного использования общего контекста между несколькими функциями.
Также обратите внимание, что функция encrypt
может вызываться несколько раз (для каждой строки), а encrypt_deinit
будет вызываться только один раз (для каждого оператора SQL), поэтому вам нужно освободить или повторно использовать предыдущий буфер в encrypt
перед выделением новой.
УХ ТЫ!! Это мне помогло. Ты мой чемпион сегодня :). Спасибо за исх. В моем случае я не знаю, как долго будет мой результат (может быть> 255 байт), поэтому я выделю собственную память и верну ее.
@Amogh, я расширил свой ответ, включив пример управления памятью.
Привет, еще раз спасибо за продление. У меня есть мысль о размещении структуры в encrypt_init
. Можем ли мы вызвать new (std::nothrow) EncryptData()
в самом encrypt()
и тут же освободить его?
и согласно UDF_INIT
struct ptr
- это char *
, поэтому нам нужно выполнить приведение типов в encrypt_init ()
@Amogh, вы не можете освободить эту структуру в encrypt
, потому что вам нужно, чтобы строковый буфер оставался действительным. послеencrypt
возвращает.
Ошибка в encrypt_deinit()
и encrypt()
как: недопустимый static_cast из типа «char *» в тип «EncryptData *». Я изменил его на delete (EncryptData *)static_cast<char *>(initid->ptr)
и EncryptData *data = (EncryptData *)static_cast<char *>(initid->ptr)
соответствующим методом. Это правильно?
@Amogh, извините за чисто теоретический код, я не заметил, что ptr имеет тип char *. Вам нужно будет заменить static_cast простым приведением в стиле C.
Чтобы вернуть char * из строки C++ в целом, вы должны сделать:
char *c = (char*)malloc(str.size() + 1);
strcpy(c, str.c_str());
return c;
Обратите внимание, что вызывающий должен освободить память, на которую указывает char *.
Для того, кто уже знает, что вы собираетесь сказать, ответ будет довольно кратким. Однако для тех, кто борется с концепцией компиляции совместимой функции на C++ для вызова из источника C, ответ будет несколько кратким. Помните, что ваш ответ не для человека, который уже знает, что вы делаете, а для человека, который не знает. Будем приветствовать еще немного дополнительных объяснений. Как, где вы определяете свою функцию? Как компилируется код C++? Как получить доступ к функции в C? Как код C компилируется для использования функции C++?
Маллокировать некоторую кучу, копировать строку внутри и возвращать неверный указатель, освобождая ответственность от пользователя. В качестве альтернативы пользователь может предоставить указатель на указатель, а библиотека может изменить указатель на указатель.