Sending strings between c++ libraries and python - c++

I am attempting to pass a string from python to a c++ library. However, I have been crashing with variations of segfaults std::bad_alloc, and invalid type messages while I have been attempting to do so. Here is the code that I am attempting to use in c++:
#define DLLEXPORT extern "C"
DLLEXPORT std::string returnAString()
{
std::string ret = "Returning string from lib";
return ret;
}
DLLEXPORT char* returnACharArray()
{
return "Returning char* from lib";
}
DLLEXPORT void passInAString(std::string incomingString)
{
printf("Recieved message in passInAString\n");
printf("Recieved incoming message: %s", incomingString);
}
DLLEXPORT void passInACharArray(char* incomingString)
{
printf("Recieved message in passInACharArray\n");
printf("Recieved incoming message: %s", incomingString);
}
Realistically, with what I am doing I can work with either the char* or the std::string once it gets in my c++ code, and I don't really have a preference either way. Here is what I am doing in python:
from ctypes import *
import os
libPath = os.path.join(os.getcwd(), "mylib.so")
lib = cdll.LoadLibrary(libPath)
string = "hello from python"
lib.passInAString(string)
#lib.passInACharArray(string)
#ret = lib.returnAString()
#print("recieved string: " + string)
#ret = lib.returnACharArray()
#print("recieved char*: " + string)
here, I will uncomment whichever line I am attempting to test. When passing in a string, I will get my first printf statement, I will get std::bad_alloc. When I pass in a char*, I get a segfault. When I attempt to receive a message, I get back a number (I am assuming that this is the pointer), but I am unable to decode this message into an actual string message.
I have attempted to use c_char_p to convert my python string into a char* to pass to my library, but when I do that I get "invalid type". Trying to convert the returned message by doing c_char_p(lib.returnACharArray) and then print(str(string.value)) then gives me the hex value of 4 bytes... which is not what I am returning.
What am I missing to make this functionality work?

Your DLL has the wrong argument types.
Since you're passing a string from Python the function should take a char*. You can convert this to a std::string within the function if you want to.
There's a table in the Python docs showing the corresponding types in both C and Python here: https://docs.python.org/3.6/library/ctypes.html#fundamental-data-types
Edit: Just realized the string types differ for Python 3 meaning the function needs to be declared differently.
e.g.
// Python2, or Python3 passing bytes
DLLEXPORT void passInAString(char* incomingString)
{
printf("Received message in passInAString\n");
printf("Received incoming message: %s", incomingString);
std::string myStr = incomingString;
// and do stuff with myStr if needed
printf("Incoming message as std::string is %s", myStr.c_str());
}
// Python3 passing strings
DLLEXPORT void passStringPy3(wchar_t* wideString)
{
printf("Message >>%ls<<\n", wideString);
}

Related

Convert a QString to a const char*

For the purpose of creating a custom exception class, I need to convert a QString to a const char*. Here's the main code of the aforementioned class:
// File "Exception.cpp"
const auto MESSAGE_PREFIX = QStringLiteral("Exception ");
Exception::Exception(const char* file, int line, const QString& cause)
: m_Message{MESSAGE_PREFIX + file + ':' + QString::number(line) + ": " + cause}
{
}
const char* Exception::what() const noexcept
{
// QString -> const char*
const auto ba = m_Message.toLocal8Bit();
return ba.constData();
}
So, the conversion happens in the overriden method Exception::what and the returned C-string indicates, among others, the file which throws the exception.
In addition, I've defined a macro THROW_EXCEPTION_IF() which helps throwing exception:
// File "exception_macros.h"
#include "Exception.h"
#ifdef Q_OS_WINDOWS
#define __FILENAME__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
#else
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
#endif
#define THROW_EXCEPTION_IF(condition, cause)
if (condition) {
auto str = QString{};
QTextStream stream{&str};
stream << cause;
throw Exception{__FILENAME__, __LINE__, str};
}
Finally, I test the above macro with the following code:
void TestException::testMacro()
{
try {
THROW_EXCEPTION_IF(true, "Test")
QVERIFY(false);
}
catch (const std::exception& e) {
QVERIFY(true);
QCOMPARE(QString{e.what()}, QStringLiteral("Exception TestException.cpp:36: Test"));
}
}
And here's the problem: when I run this test on Linux (Qt 5.7.1, GCC 6.3), it fails with this message:
FAIL!: TestException::testMacro() Compared values are not the same
Actual (QString{e.what()}): "Exception TestExn\u0000\u0000T\u0000e\u0000s\u0000t\u0000E\u0000x\u0000n\u0000\u0000\u0000"
Expected (QStringLiteral("Exception TestException.cpp:36: Test")): "Exception TestException.cpp:36: Test"
And it's the same problem on Windows (Qt 5.15) with MSVC 2019 but it works with MinGW 8.1. Moreover, on Linux, when I replace m_Message.toLocal8bit() with m_Message.toLatin1(), the test passed successfully. I think there's a problem with Unicode characters but I don't understand where's the problem in my code. Thank yout very much for your help.
Your problem is that you are returning an invalid pointer:
const auto ba = m_Message.toLocal8Bit(); // here you create a bytearray
return ba.constData();//here you take the pointer to the data of the bytearray
//then you destroy the array, hence the pointer.
//then you return an invalid pointer
You have different behaviors in the different platforms because it is not warrantied when the pointer will not be longer available. It depends on the platform, the compiler, the compilation flags, etc.
Why it doesn't work
I agree with Pablo Yaggi that you are trying to access destroyed data, see QByteArray::constData:
The [const char *] pointer remains valid as long as the byte array isn't reallocated
or destroyed.
Solution
You should store the QByteArray (returned by toLocal8Bit) in your exception class (instead of the QString). Note that you may want to return the string UTF-8 encoded (see toUtf8) to support all (special) characters. The behaviour of toLocal8Bit and toLatin1 are undefined if unsupported characters are encountered:
If this string contains any characters that cannot be encoded in the
locale, the returned byte array is undefined. Those characters may be
suppressed or replaced by another. [source]

How can I pass the contents of a tstringstream to a method that takes in an LPTSTR?

I have tried the following:
tstringstream s;
s << _T("test") << std::endl;
LPTSTR message = s.str().c_str();
Log(5, _T("Example", message);
Where Log is defined as such:
void Log(DWORD dwSeverity, LPTSTR szAppID, LPTSTR szMsgString, ...)
{
}
But I get the following error:
Error: A value of type "const char *" cannot be used to initialize an entity of type "LPTSTR"
But I'm not sure exactly how to handle this conversion. In my case, I am compiling a multi-byte character-set application using the MSVC++ compiler. Under these conditions, LPTSTR is defined as an LPSTR, which is defined as a CHAR *.
You're hitting const-incompatibility. For some reason, the function Log takes a pointer to mutable data, which is incompatible with the pointer to const data returned by c_str().
If you have the option, change Log to take its parameter as const (I assume it does not actually modify the string passed in):
void Log(DWORD dwSeverity, LPCTSTR szAppID, LPCTSTR szMsgString, ...)
The same way, declare message as LPCTSTR.
Also note that you cannot actually initialise message the way you're doing it now: the string returned by str() is temporary, so you have to store it:
tstring message = s.str();
Log(5, _T("Example", message.c_str());
If Log is outside your control, but you know that it does not modify its arguments, you can use a const_cast:
LPTSTR message = const_cast<LPTSTR>(message.c_str()); // `message` changed as above
If you cannot vouch for what Log does internally, you'll have to create a mutable buffer:
std::vector<TCHAR> buffer(message.begin(), message.end());
buffer.push_back(0);
Log(5, _T("Example", &buffer[0]);
I can see a couple of problems.
First of all here:
LPTSTR message = s.str().c_str();
The call to s.str() returns a temporary which will be destroyed unless you keep it alive. Therefore the address returned by calling c_str() becomes invalid. You need a temporary local:
string str = s.str();
LPCTSTR message = str.c_str();
And since c_str() returns a const C string, you need to declare message to be a const C string too.
The other problem is that your Log function receives a non-const C string, but c_str() returns a const C string. Presumably the Log function does not need to modify the message, so why are you asking for a modifiable buffer. Change Log to receive a const C string:
void Log(..., LPCTSTR szMsgString, ...)
Finally, since this is C++, why are you using C strings at all? It is better to use C++ strings.

How to pass variables to lua function in cocos2d-x?

I am trying to call lua functions in cocos2d-x. However when I try to pass some variables to lua functions. My program stopped at lua_call().
My function:
const char* getData::callLuaFunction(const char* luaFileName,const char* functionName){
lua_State* ls = CCLuaEngine::defaultEngine()->getLuaStack()->getLuaState();
std::string filefullpath = CCFileUtils::sharedFileUtils()->fullPathForFilename(luaFileName);
const char* pfilefullpath = filefullpath.c_str();
int isOpen = luaL_dofile(ls, pfilefullpath);
if(isOpen!=0){
CCLOG("Open Lua Error: %i", isOpen);
return NULL;
}
lua_getglobal(ls, functionName);
lua_pushstring(ls, "einverne");
lua_pushnumber(ls, 2);
lua_pushboolean(ls, true);
lua_call(ls, 3, 1);
const char* iResult = lua_tostring(ls, -1);
return iResult;
}
Function in lua file:
function luaLogString(_logStr,_logNum,_logBool)
    print("Lua string from C:",_logStr,_logNum,_logBool)
    return "call lua function OK"
end
Edit:
I have found lua_call is not protected. lua_pcall function is safer. And after I changed to lua_pcall. Errors show that attempt to call global '聽聽聽聽print' (a nil value)
Actually I found the problem.
I delete four space before print function in lua file and everything is OK.
And I suggest newbie to use lua_pcall rather than lua_call. Because if there is an error when calling lua_call , this function will call exit(EXIT_FAILURE) and shutdown host program without giving an error message.
The difference between lua_pcall and lua_call
English version
Chinese version

Using a passed by reference string array to runtime loaded dll function

I'm creating a DLL lib that should be used during runtime (i.e. loaded in Windows 7 with LoadLibrary, closed with FreeLibrary and function handles given by GetProcAddress). This is being done in C++ using Borland C++ Builder. All functions in the DLL should receive some value by reference as parameter (normally std::string).
By now the method I'm using to do this is the following (example) (summarized):
typedef void (*HIS_validity)(string &);
//LoadLibrary
HIS_validity fValidity = (HIS_validity) GetProcAddress(frMain->HIS_DLL.hisLibrary,"checkForValidity");
if (fValidity == NULL) return;
string testeValidade;
fValidity(testeValidade);
const AnsiString testeValidade2(testeValidade.c_str());
if (testeValidade2 != "...")
//etc...
In the DLL:
extern "C" void LIBRARY_API checkForValidity(string &str);
void checkForValidity(string &str)
{
str = "...";
}
Now this code is running fine. The problem is that in some functions I want to pass a whole array of strings by reference. Previously I discovered how to pass a string array by reference here and I though it would be just a matter of ajusting things accordingly:
typedef void (*HIS_patientData)(string (&)[32]);
HIS_patientData fPatientData = (HIS_patientData) GetProcAddress(frMain->HIS_DLL.hisLibrary,"patientDataFields");
string strDado2[32];
fPatientData(strDado2);
frMain->pluginData.patientData.numProntuario = AnsiString(strDado2[cont1++].c_str());
frMain->pluginData.patientData.pacNome = AnsiString(strDado2[cont1++].c_str());
In the DLL:
extern "C" void LIBRARY_API patientDataFields(string (&str)[32]);
void patientDataFields(string (&str)[32])
{
str[0] = "One";
str[1] = "Two";
str[2] = "Three";
//....
}
But here the problem appears. When I compile and run my application, the same problem always come up: if my function in the DLL has only two data attributed to 'str[]', the code goes one after 'fPatientData(strDado2);' but when I read the content of strDado2[0], it has the value of str[1] and strDado2[1] has NULL inside! By the other hand, if I add three or more attributions to 'str[]' in my DLL function, the software always crash when it comes to 'fPatientData(strDado2);' with a pop-up telling "access violation ... in module libstdc++-6.dll".
And I have no ideia what the problem is :T
Thanks for any help,
Momergil
Ok, it seems I found the answer to all such problems... Namely, I'm trying to return a C++ class (std::string) in a "extern "C"" function. It was just a matter of making it return a standart const char* that everything started to run just fine.
Thanks for the contributors,
Momergil

Passing const char* from std::string onto Lua stack turns into null

I've got this piece of code, where I gather device id from different types of devices supported by my game and set lua global to have value of id of current device.
When I get id of the iOS device I receive a const char* from a mixed C++/Objective-C class and pass it on to the Lua stack. It all works fine.
However I receive std::string from a piece of code responsible for getting Android device id. When I push deviceId.c_str() I get nil in Lua.
I've tried passing const char* from the code responsible for getting the device id, but then it seems something wrong goes on with the pointer when it's returned from function [that's why I decided to return string, it works fine this way].
What should I do to allow passing const char* out of std::string without problems?
EDIT:
I've tried using strcpy but it didn't work :/ still having the same problem.
So.. the code responsible for gathering deviceId from different devices looks like this:
#include "DeviceInfo.h"
#include "DeviceInfoIOS.h"
#include "DeviceInfoAndroid.h"
#include <string>
USING_NS_CC;
extern "C" {
const char *getDeviceId() {
const char *deviceId;
CCLog("test");
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
DeviceInfoIOS ios;
deviceId = ios.getIOSDeviceId();
CCLog("iOS platform %s", deviceId);
#endif // CC_PLATFORM_IOS
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
CCLog("Android platform");
std::string tempId = getAndroidDeviceId();
CCLog("Android platform test %s", tempId.c_str());
char y[tempId.size() + 1];
strcpy(y, tempId.c_str());
deviceId = (const char*) y;
CCLog("Android platform %s", deviceId);
#endif // CC_PLATFORM_ANDROID
CCLog("Finished platform check");
return deviceId;
}
}
Just a small note: All the logs look ok. Devie id is passed fine.
This is how I pass device id to Lua:
//deviceInfo
CCLog("DeviceInfo load");
const char *deviceId = getDeviceId();
CCLog("DeviceInfo %s", deviceId);
lua_pushstring(d_state, deviceId);
lua_setglobal(d_state, "DEVICE_ID");
Also in here, logfile contains the device id.
Your getDeviceId function is broken. Both tempId and y are stack variables. They will be destroyed once you return. Returning pointers to stack variables is always a bad idea.
Your function ought to return a std::string. Failing that, it should return a char* array that it allocates with new, and that the user is expected to deallocate with delete. That's generally why it's preferable to just return a std::string. Alternatively, you could delcare y as a static local variable using a fixed size (rather than one based on the string).