Search code examples
c++arrayspointerscharjna

How to get a Java string from a char pointer in C++


I am porting the openvr sample to jogl, after we created the binding with jna.

Almost at the end (before rendering the controllers and the tracking base stations), I got stuck trying to translate a char pointer in C to a String in Java.

C++ code here:

//-----------------------------------------------------------------------------
// Purpose: Helper to get a string from a tracked device property and turn it
//          into a std::string
//-----------------------------------------------------------------------------
std::string GetTrackedDeviceString( vr::IVRSystem *pHmd, vr::TrackedDeviceIndex_t unDevice, vr::TrackedDeviceProperty prop, vr::TrackedPropertyError *peError = NULL )
{
    uint32_t unRequiredBufferLen = pHmd->GetStringTrackedDeviceProperty( unDevice, prop, NULL, 0, peError );
    if( unRequiredBufferLen == 0 )
        return "";

    char *pchBuffer = new char[ unRequiredBufferLen ];
    unRequiredBufferLen = pHmd->GetStringTrackedDeviceProperty( unDevice, prop, pchBuffer, unRequiredBufferLen, peError );
    std::string sResult = pchBuffer;
    delete [] pchBuffer;
    return sResult;
}

GetStringTrackedDeviceProperty here:

/** Returns a string property. If the device index is not valid or the property is not a string type this function will 
* return 0. Otherwise it returns the length of the number of bytes necessary to hold this string including the trailing
* null. Strings will generally fit in buffers of k_unTrackingStringSize characters. */
virtual uint32_t GetStringTrackedDeviceProperty( vr::TrackedDeviceIndex_t unDeviceIndex, ETrackedDeviceProperty prop, VR_OUT_STRING() char *pchValue, uint32_t unBufferSize, ETrackedPropertyError *pError = 0L ) = 0;

Where VR_OUT_STRING() is defined here as:

# define VR_CLANG_ATTR(ATTR)

#define VR_OUT_STRING() VR_CLANG_ATTR( "out_string: ;" )

I have already done something similar where I had to call a function that expect the pointer to an array of TrackedDevicePose_t structures:

private TrackedDevicePose_t.ByReference trackedDevicePosesReference = new TrackedDevicePose_t.ByReference();
public TrackedDevicePose_t[] trackedDevicePose
            = (TrackedDevicePose_t[]) trackedDevicePosesReference.toArray(VR.k_unMaxTrackedDeviceCount);

I created first the reference and then from it the actual array.

But here I can't have a class extending the char array..

private String getTrackedDeviceString(IVRSystem hmd, int device, int prop, IntBuffer propError) {

    int requiredBufferLen = hmd.GetStringTrackedDeviceProperty.apply(device, prop, Pointer.NULL, 0, propError);

    if(requiredBufferLen == 0) {
        return "";
    }


    CharArray.ByReference charArrayReference = new CharArray.ByReference();
    char[] cs = charArrayReference.toArray(requiredBufferLen);

    return null;
}

Where apply (here) is:

public interface GetStringTrackedDeviceProperty_callback extends Callback {

    int apply(int unDeviceIndex, int prop, Pointer pchValue, int unBufferSize, IntBuffer pError);
};

CharArray class, crap attempt here

Any ideas?


Solution

  • I've done some porting of C and C++ code to Java, and while it's probably horribly hacky, the best I've come up with to solve cases where a pointer to an int primitive or a char*/String is needed for a function call, is to create a small wrapper class with a single property, pass that object into the function, change the property as needed, and retrieve the new value after the function call. So something like:

    public class StringPointer {
        public String value = "";
    }
    
    StringPointer pchBuffer = new StringPointer();
    unRequiredBufferLen = pHmd.GetStringTrackedDeviceProperty( unDevice, prop, pchBuffer, unRequiredBufferLen, peError );
    String sResult = pchBuffer.value;
    

    and inside GetStringTrackedDeviceProperty()

    ...
    pchValue.value = "some string";
    ...
    

    In this case, you can use a String, since that's what your code is doing with the char* after the function call, but if it actually really needs to be a char[], you can just create char[] pchBuffer = new char[unRequiredBufferLen]; and pass that into the function. It will be just like you were using a char* in C++, and any changes you make inside the array will be visible after the function ends, and you can even do String sResult = new String(pchBuffer);.