ValueTreeクラス
の使い方を学ぶ。ValueTreeクラスを使用すると、アプリケーションでデータを効率的に管理できます。
レベル:中級
プラットフォーム:Windows, macOS, Linux, iOS, Android
クラス: ValueTree,var,Identifier
はじめに
についてValueTreeクラスはJUCEの秘密兵器です。これは、アプリケーションの内部的な複雑さを大幅に合理化する力を持っており、従来は受動的であったデータモデルを、実行時の操作に積極的に参加するものに変えます。このクラスは、データをGUIと連動させたり、データ変更の自動アンドゥやリドゥを提供したり、データのユニバーサルコンテナを提供したりといった、開発における面倒な部分を引き受けてくれます。また、本質的にシリアライズが可能なので、エクスポートやインポート処理が非常に簡単になります。
このガイドブックでは、サッカーに必要な知識をすべて網羅することを目的としている。このガイドでは、それらを使用するために知っておく必要があるすべてをカバーすることを目的としています。
必要不可欠な3つの クラス
についてValueTreeクラスはまさにアンサンブルであり、彼らと仕事をする際には、少なくとも3つの重要なクラスと常に関わり合うことになる。その3つとはValueTreeクラス自体がvarクラスとIdentifierクラスである。で意味のあることをするのは不可能だ。ValueTreeそのため、それらをどのように(そしてなぜ)使うべきかを理解することは有益である。したがって、このようなデータを操作する実際の実務について説明する前に、以下のことをよく理解しておく必要 がある:
ValueTreeクラスの概要
についてValueTreeクラスは究極のコンテナ・クラスであり、あらゆる種類の情報を保持できる。これが型の明らかな役割かもしれないが、型には他にも多くの側面があり、単なる構造的な役割を超えている。
データ保管
AValueTreeオブジェクトは柔軟な多目的データオブジェクトです。基本的なタイプという名前を持ち、名前付きプロパティの任意のセットを保持できる。これは一種のユニバーサル構造体どのような(あるいはどの程度の)データを保持するかを定義する必要はない。
説明のために、1つのオブジェクト(ここでは疑似データ)は以下の情報を保持しているかもしれない:
Pet
Name = "Fluffmuff"
Animal = "Cat"
Size = 2.4
その名の通りValueTreeオブジェクトはツリー構造のノードとしても機能します。名前付きのプロパティとともに、任意の数の子ノードを含むことができます(そして、それ自体を親ノードに追加することもできます)。
Pet
Name = "Fluffmuff"
Animal = "Cat"
Size = 2.4
Accessories
Collar
Colour = "Pink"
Camera
HasFlash = false
Capacity = 32
ColourRepresents a colour, also including a transparency value.Definition juce_Colour.h:50
これらの構造は、XMLフォーマットで形成される構造と非常によく似ている。ValueTreeノードはXmlElementオブジェクトで、名前、プロパティ、子を持つ。大きな違いは、プロパティが、XMLフォーマットで使用されるテキスト・フォー・エヴリシング表現ではなく、実際の型付きデータとして格納されることである。つまり、より複雑なタイプのデータを保持し、より効率的にアクセスでき、一般的に(シリアライズだけでなく)データモデルでの使用に適しています。実際、XMLテキスト(または特殊なバイナリ形式)を任意のValueTreeノードを作成し、後でその構造を復元する。
参考カウント
各ノードは参照カウントされるため、その寿命は簡単に管理できる。データ自体は、実際には隠された共有インスタンスに格納される。ValueTreeクラスは、軽い、参照を保持するラッパー・インタフェースに過ぎない。
を返すことで、ポインタを直接使用することなく、値で素早く渡すことができます。ValueTreeオブジェクトを関数からコピーしても、データはコピーされず、参照だけがコピーされる。
自分でノードを削除する心配はありません。ノードが使われなくなると、自動的に破棄されます。これは、非同期UIでダングリングポインタが発生しないようにするために特に便利です。
シンプルなインタフェース
プロパティや子プロパティを操作するためのシンプルで汎用的なインタフェースを持つ。汎用的であることで、コンテンツの種類や構成に関係なく、コンテンツにアクセスするための単一のインタフェースを持つことができます。普遍的なvarクラスを共通のプロパティ・タイプとすることで、さまざまな入力を取ることができる。そのためIdentifierクラスを使って、これらのプロパティを名前別に保存したり取得したりすることで、直感的にデータを整理できます。
取り消しとやり直し
コンテンツを変更するすべての関数に、あらかじめ定義された取り消し可能なアクションが組み込まれている。UndoManagerオブジェクトを使うことで、アプリケーションにそのような機能を持たせることができます。まだ納得されていない方のために言っておくと、これはValueTreeオブジェクトをデータモデルに追加する。
お知らせ
もう1つの非常に強力な機能は、バリューツリーのコンテンツが変更されたときに通知を送信する機能である。例えばComponentノードの内容を表示するために使用されているオブジェクトは、データが編集されたことを確認するたびに、それ自身をリフレッシュできます。ValueTree::Listenerサブクラスである。
概要
についてValueTreeクラスは、アプリケーションのデータモデルにとって奇跡のクラスのようなもので、アプリケーションの内部をまとめる方法を単純化するための豊富な機能を難なく提供してくれる。
クラス概要
についてvarクラスは普遍的なバリアントクラスは、さまざまな型のデータを保持する。その機能は、JSONデータ構造を表現するのに適している。
従来は、コード内の変数に格納するデータの種類をあらかじめ決めておく必要がありました(たとえば、整数を格納したい場合はint
その変数が使えるのはそれだけである)。しかしvar様々なタイプに対応しているので、そのような決定を下す必要はない。
これは汎用のカメレオン変数のようなもので、基本的な数値 (int
またはdouble
)、テキスト(aStringオブジェクト)、およびbool
値と同様にvoid
状態(なぜなら0
またはfalse
とは概念的に異なることがある。何もない).から派生したクラスへのポインタを保持することもできる。ReferenceCountedObjectクラス(想像しうるあらゆる種 類の複雑なデータで構成されうる)。これだけでは十分でないかのように、単一のvarオブジェクトは、複数のvarオブジェクトがある!
この多用途性により、一般的なコンテナ(たとえばValueTree基本的な型には暗黙のキャスト(とオーバーロードされた代入演算子)があり、コード上のやりとりをシンプルにする。さらに、現在の値の文字列表現を自動的に返すこともできる(テキスト以外の型も含む)。これらの贅沢がすべて適用されないのはvarオブジェクトはReferenceCountedObjectオブジェクトにキャストする必要があります。これらは未知の型なので、自分でキャストしなければならない。dynamic_cast
を返す。nullptr
正しい型でない場合)。
においてである。ValueTreeオブジェクトとして保持されます。varオブジェクトにアクセスできます。この多目的クラスをプロパティタイプとして選択することで、どのようなものであれ、それらにアクセスするための関数を1つにまとめることができます。のために1つの関数を使う必要はない。int
このような関数はすべて、以下のように統一できる。varオブジェクトがある。
識別子クラスの概要
このクラスは、人間が読むことができるキーデータの識別に使用される。
基本的にはIdentifierオブジェクトは文字列です。オブジェクトはStringオブジェクトとして取り出すこともできます。StringオブジェクトのコンテキストではValueTreeクラスでは、ノードのタイプ名を指定するためと、各プロパティを一意にラベル付けするために使用されます。
なぜStringクラスを使わないのか?
汎用クラスではなく特殊クラスを使う主な理由は2つある。Stringクラスである。
- 限られた文字セットを強制できる:第一に、このクラスは有効なキーを構成する文字の制限を強制します。
_-:#$%
.これは少しくだらなく聞こえるかもしれないが、同じ制限(例えばXMLフォーマットやスクリプト)を持つ他のシステムとの互換性を確保することが可能になる。 - 目的に応じて最適化できる:2つ目の(しかし最も重要な)理由は、それらをどのように使いたいかに起因する。リストは、任意のサイズのリストから1つの項目を特定するためのキーとして機能するものであり、そのため、それらを使って実行される最も一般的な操作は比較である。しかしStringオブジェクトはかなり時間がかかる。実際にテキストをチェックする必要があり、最初に異なる文字を見つけて初めて、2つのStringオブジェクトは同じではない。ほとんど同じ文字列の場合、ほとんどの文字をチェックすることになるかもしれない(一致する文字列はすべての文字がテストされることになる)。しかし、1つの文字列を1つのリストと比較する場合(キーとして使用する場合)、すべての処理に長い時間がかかる可能性があります。
特別なクラスを使うことで、素早く比較できるように最適化されていることを保証できる。この最適化(と文字セットの制限)のため、一般的なテキスト・ハンドリングに使用することは意図されていないが、文字列のようなデータをキーとして使用するには最適である。
どうすればもっと早く比較できるのか?
の最適化について知っておくことは有益である。Identifierクラスで使用されます。ひとつには、コードが各テストで密 かにすべての文字をチェックしているわけではないという安心感がある。しかしもっと重要なのは、多くの最適化にはコストがつきものであり、それがどこにあるのかを知っておくことで、問題になるのを避けることができるということだ。
を使用することは完全に可能である。Identifierこのようなことを知らなくても、トレードオフがどこで発生するかは理解できるだろう。
この最適化の詳細は変更される可能性がありますが(これはクラスのセカンダリー・ナレッジです)、あなたの実装に影響を与える可能性は低いでしょう。
文字列の比較における特殊なケース
ということを知る。Stringオブジェクトが同じであることを証明しなければならない。たまたまStringクラスは参照カウントされたテキストを保持する。Stringオブジェクトが実際に同じデータを指しているのであれば、すぐにわかる(どちらも同じアドレスを保持しているからだ)。
このような特殊なケースの方がはるかに速い。Stringクラスがそれを利用できるのは、それが起こった場合だけである。アドレスが同じでない場合、その内容が等価でないことの証明にはならないので、やはり文字をチェックしなければならない。
特殊なケースを利用する
についてIdentifierクラスはこの挙動を利用し、すべての等価なIdentifierオブジェクトは常に同じデータを指す。最適化されているため、異なるIdentifierオブジェクトが異なるメモリーアドレスに存在することはない。つまり特例したがって、両者が異なることを証明するのは、両者が異なるアドレスを保持していることを見抜くのと同じくらい簡単である。
しかし、これはマジックではない。
この動作を強制するために、すべてのIdentifierオブジェクトは、単一の、隠された、ユニークな文字列のグローバルなプールを共有する。すべてのIdentifier実行時に使用されるオブジェクトは、このプールに格納される。実行時にIdentifierオブジェクトから