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_buffer
and copy the data to it throughstrcpy
- Use
malloc
or something to reserve the memory without releasing it. In this case you have to manually runfree
once 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)