オブジェクト指向プログラミング (OOP) は、プログラミング言語、開発環境、および業界の違いを超えて使用されているプログラミングパラダイムです。この記事では、LabVIEW OOPに関する、よくある質問について説明します。このドキュメントは、OOPの基本的な用語とプロセスの理解していることを前提としています。
C++はテキストベースの関数型言語で、LabVIEWはグラフィカルなデータフロー言語です。この違いにより、オブジェクト指向機能の表現方法に違いが生じます。
これらの相違点に関する詳細は、Developer Zoneチュートリアル『LabVIEWオブジェクト指向プログラミング:設計上の判断』を参照してください。
LabVIEWの新規ユーザにとっての利点は何ですか?
B. 経験のあるグラフィック言語プログラマにとっての利点は何ですか?
タスクベースのアプローチとオブジェクト指向のアプローチを比較しているテストおよび計測の例は、LabVIEW 8.2以降に含まれる「Board Testing」サンプルプロジェクトを参照してください。
labview\examples\lvoop\BoardTesting\Board Testing.lvproj
LabVIEW 8.2以降には、LabVIEWオブジェクト指向プログラミングのサンプルが含まれています。このドキュメントの追加情報セクションも参照してください。
既存のタイプ定義をLabVIEWクラスに変換するところから開始します。プロジェクトエクスプローラウィンドウでタイプ定義を右クリックし、ショートカットメニューから制御器の中身をクラスに変換を選択します。LabVIEWクラスの作成に関する詳細は『LabVIEW ヘルプ』を参照してください。
メモ: 制御器のデフォルト値と定数は変換時に保持されません。タイプ定義の詳細にアクセスするには、そのタイプ定義にアクセスする必要のあるクラスにVIを追加してください。オブジェクト指向プログラミングの基本は、操作するデータに基づいてVIをグループ化することです。オブジェクト指向設計に関する詳細は、追加情報セクションにあるNI Week 2006の2つのプレゼンテーション(英語)を参照してください。
LabVIEW 8.2以降で含まれる「ReferenceObject」サンプルプロジェクトを参照してください。
labview\examples\lvoop\ReferenceObject\ReferenceObject.lvproj
基本的には、クラスのプライベートデータをリファレンスにします。このサンプルでは、単一要素のキューリファレンスを使用してそれを実装する方法を示しています。GOOPツールキットを使用することもできます(以下のLabVIEWオブジェクト指向プログラミングとGOOPツールキットセクションを参照してください)。
LabVIEW 2009では、オブジェクト指向がLabVIEW Real-Timeターゲットでサポートされるようになりました。LabVIEW 8.6以前では、オブジェクト指向プログラミングはリアルタイムではサポートされていません。
VIを閉じる際、LabVIEWはそのVIが閉じられることにより使用されなくなるサブVIをすべて把握しているため、それらを保存するかどうかを尋ねるダイアログボックスを表示します。一方、クラスはすべてのデータインスタンスがメモリからなくなるまでメモリに保持されるため、任意のVIが閉じられることでデータの最後のインスタンスが閉じられるかどうかについてはLabVIEWは把握できません。VIのコード内にクラスへのリファレンスがない場合でも、どこかのバリアント内にクラスのインスタンスが含まれる可能性があります。すべてのVIがアイドル状態である場合、何個のクラスインスタンスがこれらのVIとともになくなるのか、それがメモリ内に残っているすべてのクラスインスタンスにあたるのかを、LabVIEWは把握することができます。LabVIEWは、クラスがVIとともにメモリからなくなることを把握しているため、VIやライブラリに対する変更を保存するかを尋ねる「変更を保存」ダイアログボックスにおいて、クラスに対する変更を保存するかについても一緒に尋ねることができます。ただし、いずれかのVIが実行中の場合、インスタンスの数は常に変動しています。またVIの実行中には、データがキューやノーティファイアなど検索不可能な場所に隠れてしまう可能性があります。VIがアイドル状態になった場合にのみこれらの追加スペースが空になります。また、LabVIEWが既にチェックし終わった場所に、実行中のVIによって新規インスタンスが作成されてしまう場合もあります。このため、VIの実行中にインスタンスを数えることは不可能です。LabVIEWは変更を保存するかどうかを尋ねた後で実行中のVIを中断するため、その時点では、クラスがメモリからなくなるかどうかはLabVIEWには判断できません。すべてのVIがアイドル状態にありLabVIEWがデータの検索を行うことができたとしても、データの検索には非常に多くの時間がかかるため、LabVIEWは検索を試みることはありません。したがってほとんどの場合、VIを閉じるとクラスに対する変更を保存するかどうかを尋ねる「変更を保存」ダイアログボックスが別に表示されます。
プロジェクトを閉じる場合のみ、LabVIEWがVIやライブラリとともにクラスを閉じることができ「変更を保存」ダイアログボックスが1回のみ表示されます。この場合、プロジェクト内のすべてのVIが閉じられるため、LabVIEWはクラスのすべてのデータインスタンスを考慮することなく廃棄してよいということが判断できます。
GOOP開発パッケージはナショナルインスツルメンツのウェブサイトから無料でダウンロードいただけます。
はい。
多くのユーザはアプリケーションにはLabVIEWクラスが最適であると答えるでしょう。LabVIEWクラスは、ユーザにとって馴染みの深い「クラスタ」のように動作します。
リファレンスは、並列に実行する複数コードセクション間の通信に使用されます。システムリソースのUMLモデリングを行っているお客様にとってリファレンスモデルは非常に有益になるため、GOOPクラスを作成したいと思われるでしょう。リファレンスは、グラフやツリーデータストラクチャを作成する際にも有益です。ただしこのような場合も、GOOPクラスの核となるデータフィールドはLabVIEWクラスでも提供されている可能性があります。並列コードセクション間の通信にリファレンスやその他の手段を使用するには、ロック、競合状態、その他の複雑な内容について知っておく必要があります。
リファレンスモデルを使用すると、メモリ内でそのクラスが確実に重複しないようにすることができます。これは、多くのメモリを必要とするクラスの場合に有益です。
既存のGOOPクラスをLabVIEWクラスとして書き直す必要性はありません。1つには、動作しているコードを重大な理由もなく破棄すべきでありませんし、GOOPクラスはLabVIEW 8.2以降では引き続き動作します。実際のところ、GOOPツールキットはLabVIEWで書かれているのです。さらに、リファレンスによって動作しているコードを値によって動作するコードに変更するには、極めて膨大な作業が発生します。
移行する理由のひとつは、パフォーマンスです。オブジェクトに対する大量のオーバーヘッドは、各関数の呼び出しにおいてリファレンス検索、インスタンスのロック、データ抽出が行われる際に発生します。GOOPツールキットではこのオーバーヘッドを極力減らすように優れた設計が行われていますが、オーバーヘッドがあることは否めません。さらに、リファレンスを使用しなければ並列処理が可能な多くの操作が、リファレンスを使用することで並列処理できなくなります。リファレンスによって動作する必要がなくパフォーマンスを重視するクラスがある場合は、GOOPクラスからLabVIEWクラスへの移行を検討してもよいでしょう。
この質問に対する回答は状況によって異なります。LabVIEWでは、自動変換を行う方法が提供されていません。
同じ状況下においては、LabVIEWクラスはGOOPクラスよりも優れたパフォーマンスを発揮します。これはLabVIEWクラスがデータフローに沿っており、ダイナミックディスパッチのネイティブコンパイラであるためです。とはいえ、同等の状況はほとんど存在しません。LabVIEWクラスはクラスタを置換えてデータフローを改善するように設計されています。GOOPクラスはLabVIEWにリファレンスを追加する手段です。2つのクラスは異なる目的を果たしています。新規クラスの作成時には、パフォーマンスではなくクラスが必要とする機能によって、GOOPクラスかLabVIEWクラスかを決定すべきです。
さまざまな種類のコンピュータにおけるベンチマークでは、この2つのパフォーマンスはほとんど同じであることが明らかになっています。パフォーマンスに関する詳細については、下の質問を参照してください。
サブVIのダイナミックディスパッチを行うと、どのサブVIを呼び出すかをLabVIEWが検索するため、ある程度の小さなオーバーヘッドが発生します。オーバーヘッドの量は一定であり、より多くのクラスを追加しても、クラスにより多くの動的VIを追加しても増加しません。LabVIEWが呼び出す実際のサブVIの機能に合致させるため、サブVI呼び出しによってパラメータのコピーが余分に作成されてしまうと、パフォーマンスに影響を与える可能性があります。呼び出すことを想定していないダイナミックVIにおいてもフロントパネル上の入力端子をフロントパネル上の出力端子に配線することによって、この問題を回避することができます(たとえば、子VIによって上書きされるコネクタぺーンを定義するためだけに存在する親VIなど)。ダイナミックディスパッチにおいてオーバーヘッドパフォーマンスがどのようにして一定に抑えられているかに関する詳細は、『LabVIEWオブジェクト指向プログラミング:設計上の判断』を参照してください。
LabVIEWクラスはプロジェクトライブラリの一種ですが、データタイプの定義に特化しています。LabVIEWクラスは、プロジェクトライブラリと同様に、メンバーVIのスコープとネームスペースを定義します。VIは1つのLabVIEWクラスにのみ所有されます。
LabVIEWクラスには、プライベートデータ制御器が含まれます。プライベートデータ制御器はクラスのデータ値を定義します。この特別な制御器は他の種類のライブラリには含まれません。この制御器はクラスファイル内に格納されます。LabVIEWクラスには、ライブラリの持つすべてのプロパティ以外に追加のプロパティもあります。追加のプロパティは、クラスプロパティダイアログボックスの継承ページ、プローブページ、ワイヤの外観ページで確認できます。
プロジェクトライブラリにはあってLabVIEWクラスにはない唯一の機能は、サブライブラリを持てることです。
LabVIEWクラスを使用したVIの場合、VIがそのサブVIをロードするのと同様に、VIをロードするとクラスがロードされます。LabVIEWクラスは、すべてのメンバーVIとその親クラスをロードします。親クラスは子クラスのロードを自動的にトリガしません。親クラスは子クラスとのリンクを持ちません。親クラスのメンバーVIが子クラスを使用している場合は、子クラスがロードされます。
「値による手法」では、そのワイヤの種類において実際のデータがワイヤ上で渡されます。「リファレンスによる手法」の場合、その種類のデータへのリファレンスがワイヤで渡され、実際のデータはセントラルリポジトリに格納されます。数値、配列、クラスタ、文字列、ブール、パスなど、ほとんどのLabVIEWのデータタイプは値を渡します。一般的に、リファレンスで渡されるのは、通信用に使用する場合や、ファイルrefnum、キューrefnum、VIサーバrefnumなどのシステムリソースを反映させる場合のみです。LabVIEWクラスでは値が渡されます。値による構文では、各オブジェクトにそれぞれのデータ値が含まれるため、それぞれオブジェクトを独立して修正することができます。リファレンスによる構文では、複数オブジェクトが参照するデータのインスタンスは1つのみです。1つのオブジェクトがデータを修正する場合、そのデータを読み書きするその他のオブジェクトと競合しないように注意する必要があります。主な利点と欠点は以下の通りです。
『LabVIEWオブジェクト指向プログラミング:設計上の判断』を参照してください。
NIからUMLツールは提供されていません。Endevo社より、UMLダイアグラムからクラスを生成し、コードからUMLダイアグラムを生成するツール(英語)が提供されています。このツールは現在のところGOOPクラス用ですが(上記の「LabVIEWオブジェクト指向プログラミングとGOOPツールキット」セクションを参照)、次期バージョンではLabVIEWクラスも同様にサポートされるようになります。
現在の値をデフォルト設定にするオプションは、LabVIEWの便利な機能です。さまざまなVIにおいて頻繁に使用されるかもしれません。優れたユーザインタフェースを提供するのが複雑であるため、この機能はLabVIEW 8.2以降のLabVIEWクラスでサポートされていません。このため、ユーザはプライベートデータであるためにそのデータを実際には見ることができなくても、データがデフォルト値と異なる場合は気付きます。この機能は、今後のLabVIEWのバージョンで対応する優先事項のひとつです。
LabVIEW 8.2では再入可能なダイナミックVIはサポートされていません。なぜなら、ダイナミックサブVIノードの編集時にはLabVIEWが実行時にどのサブVIを呼び出すのかが不明であるため、各ノードで可能性のあるすべてのサブVIのクローンを作成する必要が生じるからです。これにより、メモリが非常に多く使用されてしまいます。
(LabVIEW 8.5) LabVIEW 8.5では再入可能なダイナミックVIを使用できます。
ダイナミックディスパッチVIを再帰VIとして構成したり、ダイナミックディスパッチVI自体の定義の一部として使用することができます。再帰VIはそのブロックダイアグラム内あるいはサブVIのブロックダイアグラム内で自分自身を呼び出すことができます。再帰は、同じプロセスの出力において何度も処理を行う場合に便利です。ダイナミックディスパッチメンバーVIを再入可能に設定して再帰を許可し、インスタンス間でクローンを共有するよう構成することができます。
VIを再帰可能に構成するには以下の手順に従います。
メモ: 無限再帰に対する対処を行わない場合、不要なメモリ使用が発生したりLabVIEWがクラッシュする可能性があります。無限再帰を回避するには、再帰VIをケースストラクチャ内で呼び出してください。