I have a simple function in a c dynamic library which returns a const char* value. This value is assigned from a string type as shown in the code. I want to read the returned value of the function in a python script using ctypes:
C
#include "pch.h"
#include <string>
#define EXPORT __declspec(dllexport)
extern "C"
{
EXPORT const char* sayHello()
{
std::string str = "hello world";
const char* chptr = str.c_str();
return chptr;
}
}
Python
from ctypes import *
lib = CDLL("c:\\mysource\\mylib.dll")
lib.sayHello.restype = c_char_p
buff = lib.sayHello()
print(buff)
Using this code, in python I get as a result:
b''
But when I change my cpp file and instead of using the string type and the conversion with c_str(), I assign the "hello world" directly into the const char*, it works ok:
EXPORT const char* sayHello()
{
const char* chptr = "hello world";
return chptr;
}
... and I get as a result in python:
b'hello world'
Why when using a string variable, I receive an empty entry in python, but when using just the const char*, it works as expected?
CodePudding user response:
Your string is destructing as you reach the end of your function block - and the memory for the associated const char * is getting freed.
EXPORT const char* sayHello()
{
std::string str = "hello world";
const char* chptr = str.c_str(); // points to memory managed by str
return chptr; // str gets destructed! This pointer points to dealloced memory
}
In your other example, the const char * points to a string literal, which is likely in the .rodata segment, and so will outlive the scope of the function.
EXPORT const char* sayHello()
{
const char* chptr = "hello world"; // String literal
return chptr; // Underlying memory isn't deallocated
}
CodePudding user response:
Both versions are wrong because the memory will be released once the sayHello function has finished.
If you need to return a string you have 2 options:
- Create a buffer from python first using
ctypes.create_string_bufferand copy the data to it throughstrcpy - Use
mallocor something to reserve the memory without releasing it. In this case you have to manually runfreeonce you're done or you'll have a memory leak.
I would strongly recommend option 1.
Option 1 would look something like this:
#include "pch.h"
#include <string>
#define EXPORT __declspec(dllexport)
extern "C"
{
EXPORT void sayHello(char* buffer, int bufferSize)
{
std::string str = "hello world";
str.copy(buffer, bufferSize);
}
}
from ctypes import *
lib = CDLL("c:\\mysource\\mylib.dll")
lib.sayHello.restype = c_char_p
buffer_size = 32
buffer = create_string_buffer(buffer_size)
lib.sayHello(buffer, buffer_size)
print(buffer.value)
