Hey All,
I’ve been dealing with a seemingly simple but frustrating issue for a while and I have yet to find a solution that I find satisfying.
I have 2 binaries, a DLL and an executable. The executable calls exported functions from the DLL. Both of them are compiled using the same version of stdlib and JUCE, but the DLL might be used with other apps later, so I cannot use juce::String
as return types of exported functions. So far, sounds pretty simple.
Here is a minimal code example outlining my issue:
Public API in the DLL
// This returns a pointer to statically allocated memory (no issues here)
__declspec(dllexport) const char* getVersion()
{
return _VERSION;
}
// This returns dynamically allocated memory, making it the caller's responsibility to free it
__declspec(dllexport) char* getSomeDynamicData()
{
return _strdup(state.someData.toRawUTF8());
}
On the caller’s side, I want to avoid calling free()
manually and I’d love to convert the char*
to a juce::String
as soon as possible. However, if I’m reading the JUCE code right, juce::String
calls memcpy
when constructed from a char*
.
Here are the solutions that I considered:
- Using
std::unique_ptr<char>
:
std::unique_ptr<char> someDynamicData(dll.getSomeDynamicData());
juce::String someDynamicDataString(someDynamicData.release());
This introduces a completely redundant memcpy
and I also don’t like how it looks. Other than that, it gets the job done.
- Returning pointers without
_strdup
and copying the data on the caller’s side:
__declspec(dllexport) const char* getSomeDynamicData()
{
return state.someData.toRawUTF8();
}
juce::String someDynamicDataString(dll.getSomeDynamicData());
This looks beautiful. The constructor of juce::String
would take care of copying and deallocating the returned data. The only problem is that I assume that I cannot guarantee complete thread safety this way and it is a deal breaker for me.
- The oldschool C way:
__declspec(dllexport) void getSomeDynamicData(char* buf, size_t size)
{
memcpy(buf, state.someData.toRawUTF8(), num);
}
I just hate how this looks, especially when the API function has other parameters. It’s ugly on the caller side as well.
Is there something I’m missing here? I get the feeling that this shouldn’t be this complicated. Is there maybe a way to initalize a juce::String
and make it also take ownership of the pointer that I pass into it?
Thanks for all your input in advance!
Matyi