QMLからC++のコンテナクラスを参照する

# QMLからC++のコンテナクラスを参照する
##要点
* QListに変換するとQML内で配列として扱える
* std::copyがとても便利

詳細は以下のURLから
[Qt Project : Data Type Conversion Between QML and C++](http://qt-project.org/doc/qt-5/qtqml-cppintegration-data.html)

---
###実行環境
Qt Creator 3.2.1
Qt 5.3.2

---

### 対象とするコンテナクラス
 * std::array
 * std::list
 * std::vector
 * QVector

---
まず, QML側にC++の拡張クラスを登録する。

```cpp:main.cpp
#include <QtQml>
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QObject>
#include <containertranslate.h>   //登録するC++クラスのインポート

int main(int argc, char *argv[]){
    QGuiApplication app(argc,argv);
    //QMLに登録するC++クラスの宣言
    //(engine.loadの後に宣言すると怒られる)
      qmlRegisterType<ContainerTranslate>("containertranslate",1,0,"ContainerTranslate");
    QQmlApplicationEngine engine;
    //読み込むQMLファイルの指定
    engine.load(QUrl(QStringLiteral("qrc:///main.qml")));

    return app.exec();
}


```

main.cppで重要なのは
* 登録するクラスのインポート
*  _qmlRegisterType_ を使ったクラスの登録

の2つ. _qmlResisterType_ は

```cpp:
qmlRegisterType<C++クラス名>("インポートファイル名",メジャーバージョン,マイナーバージョン,"QML内のエレメント宣言名")
```

といった具合に書く。
これをengine.load(...)の後に書くと, QML起動時に怒られるので必ず前に書こう。

---
次に, C++の拡張クラスを書く。
プロジェクトにファイルを追加するとき、"C++クラスを追加"を選択する。
このときに,  **"型情報" -> "QQuickItem"** を選ぶことをお忘れなく。
これをしないとコンパイルエラーになって辛い思いをする。

これのクラスでは, 
* メンバ変数の宣言
* QMLからのアクセッサ
を定義する。

```cpp:containertranslate.h
// /////////////// ヘッダファイル //////////////////////

#ifndef CONTAINERTRANSLATE_H
#define CONTAINERTRANSLATE_H

// -------------------------------------
//         include をお忘れなく
// -------------------------------------
#include <QQuickItem>
#include <QVector>
#include <vector>
#include <list>
#include <array>
// -------------------------------------
class ContainerTranslate : public QQuickItem
{
    Q_OBJECT          
public:
    //コンストラクタ
    explicit ContainerTranslate(QQuickItem *parent = 0);
    // C++コンテナ -> QList変換し,  QList をQMLに渡すメソッド
    //Q_INVOKABLEをつけることでQMLから呼び出し可能にする
      Q_INVOKABLE QList<qreal> getContainer();    

//-----------メンバ変数定義------------------
private:
      //検証に使うコンテナ達
      std::vector<double> vec;
      std::array<int,10> ary;
      std::list<double> list;
      QVector<double> qvec;
      //みんな最終的にはこれに変換
      QList<qreal> qlist;
};

#endif // CONTAINERTRANSLATE_H

```
---
続いてソースファイル

```cpp:containertranslate.cpp
// /////////////// ソースファイル //////////////////////
#include "containertranslate.h"

ContainerTranslate::ContainerTranslate(QQuickItem *parent) :
    QQuickItem(parent)
{
    //コンテナを初期化
    for(int i=0; i<10; i++){
        vec.push_back(i);
        ary[i] = i;
        list.push_back(i);
        qvec.push_back(i);
    }
}
// C++標準コンテナをQListに変換
QList<qreal> ContainerTranslate::getContainer(){

            qlist.clear();              //メモリ解放

            //ここで std::** -> QListに変換している
            std::copy(vec.begin(),vec.end(),std::back_inserter(qlist));
//          std::copy(ary.begin(),ary.end(),std::back_inserter(qlist));
//          std::copy(list.begin(),list.end(),std::back_inserter(qlist));
//          std::copy(qvec.begin(),qvec.end(),std::back_inserter(qlist));
            return qlist;
}

```

ここで登場する `std::copy` が非常にcoolで, _std::vector, list, array, QVector_ のすべてに対して同じ文法でコピーが行える.

 `x.begin()` , `x.end()` でコンテナの先頭/末尾の[イテレータ](http://www.cppll.jp/cppreference/iterators.html)を参照する.
` std::back_inserter(Container &y)` は _y_ の末尾に, 指定した _x_ の要素を挿入する。

このコピーは参照渡しで動いているらしく, 複数回コピーを行っても非常に高速に動作する。

---
これで, QMLにコンテナを渡す機能は揃ったので, 次はQMLを書こう。



``` qml
//----------------------main.qml------------------------
import QtQuick 2.0
import QtQuick.Window 2.0
import containertranslate 1.0  //ここでC++拡張クラスをインポート

Window{
        visible: true
        width: 100
        height: 62

        ContainerTranslate{id: container}  //C++拡張クラスをエレメントとして宣言

        //QMLが無事起動したときに送られるシグナル
        Component.onCompleted: {
            var ctnr = container.getContainer()  //メソッド呼び出し
            for(var i=0; i<10; i++)
                    console.debug(ctnr[i])       //コンテナの参照
        }
}

```

QMLからC++拡張クラスのメソッドを呼ぶときに必要なことは2つ。
* `qmlRegisterType` で宣言したインポート名でクラスをインポート
* `qmlRegisterType` で宣言したエレメント名でエレメントを作る。またidも必ず振る。

これさえやれば, QMLの他のエレメントメソッドを呼ぶようにC++拡張クラスのメソッドを呼び出すことができる。

上記QMLの実行結果は, `ContainerTranlate::getContainer()`のどの変換式を使っても

```
qml:0
qml:1
qml:2
qml:3
qml:4
qml:5
qml:6
qml:7
qml:8
qml:9
```

と表示される。

---
QListをQMLへメソッドで渡す場合参照渡しで渡されるため, 高速に動作する。
上記の方法を用いれば, コピーから参照までを高速に, かつ, とても簡単に実装することができる。

QMLが認識できるQListの型は `int, qreal(double), bool, QUrl, QString` と, 基本型を扱うならば, ほとんどがこの方法で間にあう。

もう生の配列には戻れないね .... orz