Вызов функции C++ из C для возврата char *

При написании 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 *?

Маллокировать некоторую кучу, копировать строку внутри и возвращать неверный указатель, освобождая ответственность от пользователя. В качестве альтернативы пользователь может предоставить указатель на указатель, а библиотека может изменить указатель на указатель.

user1095108 17.12.2018 07:08

Это вопрос собственности. Должен ли вызывающий Encrypt_UDF помнить, что им нужно освободить возвращаемый буфер? Если нет, нужна ли вам потокобезопасность? Будет ли проблемой наложить требование, чтобы вся программа была связана с компилятором / компоновщиком, поддерживающим C++?

StoryTeller - Unslander Monica 17.12.2018 07:16

@StoryTeller: Похоже, кто-то, знакомый с mySql, может правильно ответить на этот вопрос.

P.W 17.12.2018 07:26
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
3
407
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Согласно Документация 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;
}


In case your string doesn't always fit in 255 bytes you'll need to allocate the buffer for it yourself and more importantly освободите его, чтобы избежать утечки памяти. To achieve this you will have to implement additional 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 17.12.2018 07:56

@Amogh, я расширил свой ответ, включив пример управления памятью.

r3mus n0x 17.12.2018 09:22

Привет, еще раз спасибо за продление. У меня есть мысль о размещении структуры в encrypt_init. Можем ли мы вызвать new (std::nothrow) EncryptData() в самом encrypt() и тут же освободить его?

Amogh 17.12.2018 10:20

и согласно UDF_INIT struct ptr - это char *, поэтому нам нужно выполнить приведение типов в encrypt_init ()

Amogh 17.12.2018 10:39

@Amogh, вы не можете освободить эту структуру в encrypt, потому что вам нужно, чтобы строковый буфер оставался действительным. послеencrypt возвращает.

r3mus n0x 17.12.2018 10:40

Ошибка в encrypt_deinit() и encrypt() как: недопустимый static_cast из типа «char *» в тип «EncryptData *». Я изменил его на delete (EncryptData *)static_cast<char *>(initid->ptr) и EncryptData *data = (EncryptData *)static_cast<char *>(initid->ptr) соответствующим методом. Это правильно?

Amogh 17.12.2018 11:11

@Amogh, извините за чисто теоретический код, я не заметил, что ptr имеет тип char *. Вам нужно будет заменить static_cast простым приведением в стиле C.

r3mus n0x 17.12.2018 11:27

Чтобы вернуть char * из строки C++ в целом, вы должны сделать:

char *c = (char*)malloc(str.size() + 1);
strcpy(c, str.c_str());
return c;

Обратите внимание, что вызывающий должен освободить память, на которую указывает char *.

Для того, кто уже знает, что вы собираетесь сказать, ответ будет довольно кратким. Однако для тех, кто борется с концепцией компиляции совместимой функции на C++ для вызова из источника C, ответ будет несколько кратким. Помните, что ваш ответ не для человека, который уже знает, что вы делаете, а для человека, который не знает. Будем приветствовать еще немного дополнительных объяснений. Как, где вы определяете свою функцию? Как компилируется код C++? Как получить доступ к функции в C? Как код C компилируется для использования функции C++?

David C. Rankin 17.12.2018 08:20

Другие вопросы по теме