Edit

Share via


Array and WriteOnlyArray (C++/CX)

You can freely use regular C-style arrays or std::array in a C++/CX program (although std::vector is often a better choice), but in any API that is published in metadata, you must convert a C-style array or vector to a Platform::Array or Platform::WriteOnlyArray type depending on how it is being used. The Platform::Array type is neither as efficient nor as powerful as std::vector, so as a general guideline you should avoid its use in internal code that performs lots of operations on the array elements.

The following array types can be passed across the ABI:

  1. const Platform::Array^

  2. Platform::Array^*

  3. Platform::WriteOnlyArray

  4. return value of Platform::Array^

You use these array types to implement the three kinds of array patterns that are defined by the Windows Runtime.

PassArray
Used when the caller passes an array to a method. The C++ input parameter type is const Platform::Array<T>.

FillArray
Used when the caller passes an array for the method to fill. The C++ input parameter type is Platform::WriteOnlyArray<T>.

ReceiveArray
Used when the caller receives an array that the method allocates. In C++/CX you can return the array in the return value as an Array^ or you can return it as an out parameter as type Array^*.

PassArray pattern

When client code passes an array to a C++ method and the method does not modify it, the method accepts the array as a const Array^. At the Windows Runtime application binary interface (ABI) level, this is known as a PassArray. The next example shows how to pass an array that's allocated in JavaScript to a C++ function that reads from it.

//JavaScript
function button2_click() {
    var obj = new JS-Array.Class1();
    var a = new Array(100);
    for (i = 0; i < 100; i++) {
        a[i] = i;
    }
    // Notice that method names are camelCased in JavaScript.
    var sum = obj.passArrayForReading(a);
    document.getElementById('results').innerText
        = "The sum of all the numbers is " + sum;
}

The following snippet shows the C++ method:

double Class1::PassArrayForReading(const Array<double>^ arr)
{
    double sum = 0;
    for(unsigned int i = 0 ; i < arr->Length; i++)
    {
        sum += arr[i];
    }
    return sum;
}

ReceiveArray pattern

In the ReceiveArray pattern, client code declares an array and passes it to a method which allocates the memory for it and initializes it. The C++ input parameter type is a pointer-to-hat: Array<T>^*. The following example shows how to declare an array object in JavaScript, and pass it to a C++ function that allocates the memory, initializes the elements, and returns it to JavaScript. JavaScript treats the allocated array as a return value, but the C++ function treats it as an out parameter.

//JavaScript
function button3_click() {
    var obj = new JS-Array.Class1();

    // Remember to use camelCase for the function name.
    var array2 = obj.calleeAllocatedDemo2();
    for (j = 0; j < array2.length; j++) {
        document.getElementById('results').innerText += array2[j] + " ";
    }
}

The following snippet shows two ways to implement the C++ method:


// Return array as out parameter...
void Class1::CalleeAllocatedDemo(Array<int>^* arr)
{
    auto temp = ref new Array<int>(10);
    for(unsigned int i = 0; i < temp->Length; i++)
    {
        temp[i] = i;
    }

    *arr = temp;
}

// ...or return array as return value:
Array<int>^ Class1::CalleeAllocatedDemo2()
{
    auto temp = ref new Array<int>(10);    
    for(unsigned int i = 0; i < temp->Length; i++)
    {
        temp[i] = i;
    }

    return temp;
}

Fill arrays

When you want to allocate an array in the caller, and initialize or modify it in the callee, use WriteOnlyArray. The next example shows how to implement a C++ function that uses WriteOnlyArray and call it from JavaScript.

// JavaScript
function button4_click() {
    var obj = new JS-Array.Class1();
    //Allocate the array.
    var a = new Array(10);

    //Pass the array to C++.
    obj.callerAllocatedDemo(a);

    var results = document.getElementById('results');
    // Display the modified contents.
    for (i = 0; i < 10; i++) {
        document.getElementById('results').innerText += a[i] + " ";
    }
}

The following snippet shows how to implement the C++ method:

void Class1::CallerAllocatedDemo(Platform::WriteOnlyArray<int>^ arr)
{
    // You can write to the elements directly.
    for(unsigned int i = 0; i < arr->Length; i++)
    {
        arr[i] = i;
    }   
}

Array conversions

This example shows how to use a Platform::Array to construct other kinds of collections:

#include <vector>
#include <collection.h>
using namespace Platform;
using namespace std;
using namespace Platform::Collections;

void ArrayConversions(const Array<int>^ arr)
{
    // Construct an Array from another Array.
    Platform::Array<int>^ newArr = ref new Platform::Array<int>(arr);

    // Construct a Vector from an Array
    auto v = ref new Platform::Collections::Vector<int>(arr); 

    // Construct a std::vector. Two options.
    vector<int> v1(begin(arr), end(arr));
    vector<int> v2(arr->begin(), arr->end());

    // Initialize a vector one element at a time.
    // using a range for loop. Not as efficient as using begin/end.
    vector<int> v3;
    for(int i : arr)
    {
        v3.push_back(i);
    }   
}

The next example shows how to construct a Platform::Array from a C-style array and return it from a public method.

Array<int>^ GetNums()
{
    int nums[] = {0,1,2,3,4};
    //Use nums internally....

    // Convert to Platform::Array and return to caller.
    return ref new Array<int>(nums, 5);
}

Jagged arrays

The Windows Runtime type system does not support the concept of jagged arrays and therefore you cannot use IVector<Platform::Array<T>> as a return value or method parameter in a public method. To pass a jagged array or a sequence of sequences across the ABI, use IVector<IVector<T>^>.

Use ArrayReference to avoid copying data

In some scenarios where data is being passed across the ABI into a Platform::Array, and you ultimately want to process that data in a C-style array for efficiency, you can use Platform::ArrayReference to avoid the extra copy operation. When you pass a Platform::ArrayReference as an argument to a parameter that takes a Platform::Array, the ArrayReference will store the data directly into a C-style array that you specify. Just be aware that ArrayReference has no lock on the source data, so if it that data is modified or deleted on another thread before the call completes, the results will be undefined.

The following code snippet shows how to copy the results of a DataReader operation into a Platform::Array (the usual pattern), and then how to substitute ArrayReference to copy the data directly into a C-style array:

public ref class TestReferenceArray sealed
{
public:

    // Assume dr is already initialized with a stream
    void GetArray(Windows::Storage::Streams::DataReader^ dr, int numBytesRemaining)
    {
        // Copy into Platform::Array
        auto bytes = ref new Platform::Array<unsigned char>(numBytesRemaining);            

        // Fill an Array.
        dr->ReadBytes(bytes);

        // Fill a C-style array
        uint8 data[1024];
        dr->ReadBytes( Platform::ArrayReference<uint8>(data, 1024) );
    }
};

Avoid exposing an Array as a property

In general, you should avoid exposing a Platform::Array type as a property in a ref class because the entire array is returned even when client code is only attempting to access a single element. When you need to expose a sequence container as a property in a public ref class, Windows::Foundation::IVector is a better choice. In private or internal APIs (which are not published to metadata), consider using a standard C++ container such as std::vector.

See also

Type System
C++/CX Language Reference
Namespaces Reference