From 33058f2b292b3a581333bdfb21b8f671898c5060 Mon Sep 17 00:00:00 2001 From: Peter Bengtsson Date: Tue, 8 Dec 2020 14:40:17 -0500 Subject: initial commit --- files/ja/implementing_queryinterface/index.html | 161 ++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 files/ja/implementing_queryinterface/index.html (limited to 'files/ja/implementing_queryinterface/index.html') diff --git a/files/ja/implementing_queryinterface/index.html b/files/ja/implementing_queryinterface/index.html new file mode 100644 index 0000000000..df238daa9a --- /dev/null +++ b/files/ja/implementing_queryinterface/index.html @@ -0,0 +1,161 @@ +--- +title: Implementing QueryInterface +slug: Implementing_QueryInterface +tags: + - XPCOM +translation_of: Mozilla/Implementing_QueryInterface +--- +

このドキュメントでは、QueryInterface() の正しい書き方について解説します。

+

QueryInterface のリファレンス実装

+
NS_IMETHODIMP
+nsMyImplementation::QueryInterface( REFNSIID aIID, void** aInstancePtr )
+  {
+    NS_ASSERTION(aInstancePtr, "QueryInterface requires a non-NULL destination!");
+      // このメソッドの結果を置く場所を用意せずにこのメソッドを呼ぶのは、実行時エラーではなく、論理エラーです。
+
+      // ...しかし非デバッグビルドにおいて、間違ってこのメソッドを呼び出す時は問題にはなりません。
+    if ( !aInstancePtr )
+      return NS_ERROR_NULL_POINTER;
+
+    nsISupports* foundInterface;
+
+    if ( aIID.Equals(nsCOMTypeInfo<nsIX>::GetIID()) )
+      foundInterface = NS_STATIC_CAST(nsIX*, this);
+    else if ( aIID.Equals(nsCOMTypeInfo<nsIY>::GetIID()) )
+      foundInterface = NS_STATIC_CAST(nsIY*, this);
+
+    // ...必要に応じて複数の場合を書きます...
+
+    else if ( aIID.Equals(nsCOMTypeInfo<nsISupports>::GetIID()) )
+      foundInterface = NS_STATIC_CAST(nsISupports*, NS_STATIC_CAST(nsIX*, this));
+        //このオブジェクトは複数の |nsISupports| を持っているかもしれません。
+        //  そのため、まず特定のベースインタフェースへキャストして、あいまいさを避けます。
+    else
+      foundInterface = 0;
+
+    nsresult status;
+    if ( !foundInterface )
+      status = NS_NOINTERFACE;
+    else
+      {
+        NS_ADDREF(foundInterface);
+        status = NS_OK;
+      }
+
+    *aInstancePtr = foundInterface;
+    return status;
+  }
+
+

どこが良いのでしょうか?

+ +

いくつかの代案

+

NS_IMPL_QUERY_INTERFACE[012] マクロ

+

上記のサンプルは、nsISupports に加えて、二つの XPCOM インタフェースを実装しています。NS_IMPL_QUERY_INTERFACE2 マクロを使って、この関数を書くことができます。(もっともマクロを勧めるのは気が進まないのですけど。) 例えば、

+
NS_IMPL_QUERY_INTERFACE2(nsMyImplementation, nsIX, nsIY)
+                                          // implements |nsMyImplementation::QueryInterface| as above
+
+NS_IMPL_QUERY_INTERFACE1(nsFoo, nsIFoo)   // |nsFoo::QueryInterface| provides |nsIFoo| and |nsISupports|
+NS_IMPL_QUERY_INTERFACE0(nsBar)           // |nsBar::QueryInterface| can only provide an |nsISupports|
+
+

同様に、実装するインタフェースをひとつだけ追加したい時は、NS_IMPL_QUERY_INTERFACE1 マクロを使うことができます。また、nsISupports だけを実装する時は、NS_IMPL_QUERY_INTERFACE0 マクロを使うことができます。これらのマクロは、NS_IMPL_ISUPPORTS[012] マクロを使った時に実行されます。このマクロは、対応する QueryInterfaceAddRefRelease の実装を提供します。

+

継承した QueryInterface を呼び出す

+

時々、多くのインタフェースをサポートする実装に、単にひとつか二つのインタフェースを加えたい場合があるでしょう。そのような場合は、おそらく、関係のある特定の IID をテストした後で、元の実装を呼び出したいでしょう。これにより、コードスペースと複雑さが低減されます。以下のコードでは、異なっている部分が強調されています。

+
class nsMyImplmentation : public nsBaseImplementation, public nsIX, public nsIY { ... };
+
+NS_IMETHODIMP
+nsMyImplementation::QueryInterface( REFNSIID aIID, void** aInstancePtr )
+    /*
+      (このクラスには) |nsIX| と |nsIY| を追加しました。
+      (このクラスの) ベースクラスの |nsBaseImplementation| は残りのすべてを提供します。
+    */
+  {
+    NS_ASSERTION(aInstancePtr, "QueryInterface requires a non-NULL destination!");
+
+    if ( !aInstancePtr )
+      return NS_ERROR_NULL_POINTER;
+
+    nsISupports* foundInterface;
+
+    if ( aIID.Equals(nsCOMTypeInfo<nsIX>::GetIID()) )
+      foundInterface = NS_STATIC_CAST(nsIX*, this);
+    else if ( aIID.Equals(nsCOMTypeInfo<nsIY>::GetIID()) )
+      foundInterface = NS_STATIC_CAST(nsIY*, this);
+    // 注: |nsISupports| をチェックしないでください。|nsBaseImplementation| がこのクラスのためにそれを行うはずです
+    else
+      foundInterface = 0;
+
+
+    nsresult status;
+    if ( !foundInterface )
+        // OK, インタフェースを見付けることができませんでした。このクラスのベースクラスがやってくれるでしょう。
+      status = nsBaseImplementation::QueryInterface(aIID, &foundInterface);
+    else
+      {
+        NS_ADDREF(foundInterface);
+        status = NS_OK;
+      }
+
+    *aInstancePtr = foundInterface;
+    return status;
+  }
+
+

ベースの実装の QueryInterface が適切なインタフェースを見付けた場合、あなたの QueryInterface では AddRef を呼び出してはいけないことに注意してください。上記のコードでは、このことが考慮されています。

+

このテクニックは、nsBaseImplementation がすでにそれ自身で使われている完全なクラスであるために、正常に動作します。このテクニックは、複数の完全なクラスから派生した時は、あまり適切ではありません。しかし、もし順番にこだわるのであれば、このテクニックを使うことができます。例えば、

+
    // ...
+    nsresult status;
+    if ( !foundInterface )
+      {
+        // OK, ask |nsBase1Imp| first, because I want _it_ to be the one true |nsISupports|.
+        status = nsBase1Imp::QueryInterface(aIID, &foundInterface);
+
+        if ( !foundInterface )
+          status = nsBase2Imp::QueryInterface(aIID, &foundInterface);
+
+        if ( !foundInterface )
+          status = nsBase3Imp::QueryInterface(aIID, &foundInterface);
+      }
+    else
+      {
+        NS_ADDREF(foundInterface);
+        status = NS_OK;
+      }
+    // ...
+
+

もし不可能でないとしても、あなたのベースクラスのどれかが本当の集約に加わって、正常に動作させるのは困難でしょう。集約されたオブジェクト上の QueryInterface に対する呼び出しを捕まえることはできないでしょう。それができたとすると、間違ったインタフェースを返すかもしれません。特に集約を避ける、また複雑な階層構造を避けるもう一つの理由があります。

+

NS_GET_IID マクロ

+

あなたは、完全な GetIID 表現をタイプする代わりに、NS_GET_IID マクロを使うことができます。 一般的に私は、マクロが異なる状況で異なるテキストへ拡張されるマクロでない限り、認めません。 例えば、異なるプラットフォーム、デバッグ時と非デバッグ時、などです。 そのような場合、マクロなしで済ますことはできません。 他の場合では、マクロはいくらかの人々を助けるかもしれませんが、しばしば他の問題に悪い影響を与えます。 それらは、常にプログラムソースをより脆弱にしてしまいます。 この場合、マクロは便宜のためだけのものであり、私はこれを勧めはしませんが、代案としては示そうと思います。

+
    // ...
+    if ( aIID.Equals(NS_GET_IID(nsIX)) )
+      foundInterface = NS_STATIC_CAST(nsIX*, this);
+    else if ( aIID.Equals(NS_GET_IID(nsIY)) )
+      foundInterface = NS_STATIC_CAST(nsIY*, this);
+
+    // ...as many cases as needed...
+
+    else if ( aIID.Equals(NS_GET_IID(nsISupports)) )
+    // ...
+
+

謝辞

+

Heikki ToivonenChris WatersonJohn Bandhauer に感謝します。らが、価値あるフィードバックをしてくれたおかげで、ここに載せた実装を著しく改善することができました。

+
+

原文書の情報

+ +
+
+  
+

{{ languages( { "en": "en/Implementing_QueryInterface" } ) }}

-- cgit v1.2.3-54-g00ecf