Home > database >  Cannot read 'const char*' from python using ctypes
Cannot read 'const char*' from python using ctypes

Time:10-17

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:

  1. Create a buffer from python first using ctypes.create_string_buffer and copy the data to it through strcpy
  2. Use malloc or something to reserve the memory without releasing it. In this case you have to manually run free 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)
  • Related