Array と WriteOnlyArray (C++/CX)

C++/CX プログラムでは、正規の C スタイル配列または std::array を自由に使用できます (ただし、std::vector の方が適していることが多い) が、メタデータで発行されるどの API においても、使用法に応じて、C スタイル配列またはベクターを Platform::Array 型または Platform::WriteOnlyArray 型に変換する必要があります。 Platform::Array 型は、std::vector ほどには効率的でも強力でもありません。そのため、一般的なガイドラインとして、配列要素で多くの操作を実行する内部コードでは使用を避ける必要があります。

次の配列型は、ABI を介して渡すことができます。

  1. const Platform::Array^

  2. Platform::Array^*

  3. Platform::WriteOnlyArray

  4. Platform::Array^ の戻り値

これらの配列型は、Windows ランタイムによって定義された 3 種類の配列パターンの実装に使用します。

PassArray
呼び出し元が、配列をメソッドに渡すときに使用します。 C++ 入力パラメーターの型は、constPlatform::Array<T> です。

FillArray
呼び出し元が、メソッドが入力する配列を渡すときに使用します。 C++ 入力パラメーターの型は、Platform::WriteOnlyArray<T> です。

ReceiveArray
呼び出し元が、メソッドが割り当てる配列を受け取るときに使用します。 C++/CX では、配列を Array^ として戻り値で返すことも、型 Array^* として Out パラメーターとして返すこともできます。

PassArray パターン

クライアント コードが配列を C++ メソッドに渡し、メソッドがその配列を変更しない場合、メソッドはその配列を const Array^ として受け入れます。 Windows ランタイム アプリケーション バイナリ インターフェイス (ABI) レベルでは、これは PassArray と呼ばれます。 次の例は、JavaScript に割り当てられた配列を、読み取り側の C++ 関数に渡す方法を示しています。

//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;
}

次のスニペットは、C++ メソッドを示します。

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 パターン

ReceiveArray パターンでは、クライアント コードは配列を宣言し、それにメモリを割り当てて初期化するメソッドに渡します。 C++ 入力パラメーターの型は、ハットへのポインター (Array<T>^*) です。 次の例は、JavaScript で配列オブジェクトを宣言し、それを、メモリを割り当て、要素を初期化して JavaScript に返す C++ 関数に渡す方法を示しています。 JavaScript は、割り当てられた配列を戻り値として扱いますが、C++ 関数は出力パラメーターとして扱います。

//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] + " ";
    }
}

次のスニペットは、C++ メソッドを実装する 2 通りの方法を示します。


// 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 配列

配列を呼び出し元で割り当て、呼び出し先で初期化または変更する場合は、 WriteOnlyArrayを使用します。 次の例は、 WriteOnlyArray を使用する C++ 関数を実装して、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] + " ";
    }
}

次のスニペットは、C++ メソッドを実装する方法を示します。

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;
    }   
}

配列変換

この例では、Platform::Array を使用して、その他の種類のコレクションを構築する方法を示しています。

#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);
    }   
}

次の例は、Platform::Array を C スタイル配列から構築して、パブリック メソッドから返す方法を示しています。

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);
}

ジャグ配列

Windows ランタイムの型システムは、ジャグ配列の概念をサポートしていないため、パブリック メソッドで IVector<Platform::Array<T>> を戻り値またはメソッド パラメーターとして使用することはできません。 ABI を通じてジャグ配列またはシーケンスのシーケンスを渡すには、 IVector<IVector<T>^>を使用します。

ArrayReference 使用による、データ コピーの回避

データが ABI を介して Platform::Array に渡されており、最終的にはそのデータを C スタイル配列で処理して効率性を追求するシナリオでは、Platform::ArrayReference を使用して、不要なコピー操作を回避できます。 Platform::Array を受け取るパラメーターに引数として Platform::ArrayReference が渡されると、ArrayReference は、そのデータを指定された C スタイル配列に直接格納します。 ArrayReference にソース データへのロック オンがないため、呼び出しが完了する前に別のスレッドでそのデータが変更されるかまたは削除された場合、結果は不確定になることに注意してください。

次のコード スニペットは、DataReader 操作の結果を Platform::Array にコピーする方法 (通常のパターン) と、ArrayReference を代わりに使用してデータを C スタイル配列に直接コピーする方法を示します。

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) );
    }
};

配列をプロパティとして公開することを回避する方法

一般に、ref クラスで Platform::Array 型をプロパティとして公開することは避ける必要があります。これは、クライアント コードが単一要素にアクセスしようとしている場合でも、配列全体が返されるためです。 パブリック ref クラスでシーケンス コンテナーをプロパティとして公開する必要があるときは、Windows::Foundation::IVector の方が適切です。 プライベートまたは内部 API (メタデータに発行されません) では、std::vector などの標準 C++ コンテナーの使用を検討してください。

関連項目

型システム
C++/CX 言語リファレンス
名前空間参照