モノトーンの伝説日記

Apex Legends, Splatoon, Programming, and so on...

WinRT のための COM 拡張について見ていく。

 今日は、WRL 使って書いてたプログラムがうまくいかなくて、そもそも WinRT のための COM 拡張とは何なのかを知る必要がありそうだったのでここに記述しておきます。

概要

  1. generic 周りの追加について
  2. generic 周りのコードを読む
  3. まとめ

1. generic 周りの追加について

 Windows Runtime では難なく generic が使えていますが、COM では扱えないわけですよ。そのためかなり厄介な方法をとっている気がします。

 まず idl のコンパイルですが、generic は名前空間以下に declare スコープを作り、そして特殊化による宣言を行います。

// Generic instantiations
namespace Windows { namespace Foundation { declare {
    interface Windows.Foundation.Collections.IIterable<BYTE>;
} } }

Windows.Foundation.idl より

 たとえばこのように。何気に使ってる人もいるかもしれませんが、特殊化されていないものはきっとうまく使えないはずです。一部 EventHandler は例外だった気がしますが。(TypedEventHandler はこの例外には当てはまらない)

 厄介なのは Windows.Foundation.Collections.h です。そもそもこいつは idl が存在しません。C++ の template をフル活用して書いているので仕方ないと思います。

template <class T>
struct is_foundation_struct { enum {value = false};};
template <>
struct is_foundation_struct<GUID> { enum { value = true};};

Windows.Foundation.Collections.h より

 たとえば、こんなやつ。テンプレート宣言とその特殊化宣言で foundation 型なのかそうでないのかっていう条件分岐になります。

 そのファイルの一番上に Notes on design があるので読んでみましょう。


The bodies of each of these interface templates are each defined once, with the postfix “_impl”, defining each of the members.

これらのテンプレート本体はそれぞれ 1 回ずつ定義されます。末尾「_impl」で。メンバーごとに定義する。

MIDL will then hook up the “_impl” with the method definitions to the real template, and assign it a guid.

MIDL は実テンプレートへのメソッド定義とともに「_impl」を接続するでしょう。そしてそれは GUID を割り当てるでしょう。

For instance, it might generate:

たとえば、次のようなコードは:

template &lt; &gt;
struct
__declspec(uuid("77e5b300-6a88-4f63-b490-e25b414bed4e"))
IVectorView<int> : IVectorView_impl<int>
{
};

In this fashion, IVectorView is assigned an IID.

このようなやり方で、IVectorView&lt;int&gt; は IID に割り当てられます。

As well, for each parameterized type, the original template is not just forward declared, but defined and derives from the “_impl” form.

なお、for each パラメーター型いわゆるオリジナルのテンプレート型は前に定義されたものと同じではない。しかし、「_impl」型から定義、継承されている。

This is done as it was found to generate usable autocomplete in IDEs.

こうすると、IDE (統合開発環境) で自動補完 (インテリセンス) の利用に適したものを生成できると発見できた。

The original unspecialized template also is made to static_assert if the user attempts to use it. This is to avoid ODR violations, and the scenario that the user forgot to mention a 'declare' clause in the MIDL input.

もしユーザーがそれを使ってみたいなら、オリジナルの特殊化されていないテンプレートは static_assert で作られている。ユーザーは MIDL 入力で「declare」条項に言及したことを忘れるシナリオにて、これは ODR (1 回定義の規則; One-Definition Rule) 違反を避ける。 ※ここの訳は自信ない


 という感じである。

 試行錯誤の上に編み出した一つの結論というのが読み取れる気がする。ここを読んでると、ODR 違反を避けるために static_assert 仕込んだよ、とかいう捕捉も書いてある感じ。

 一番重要なのは「なお (as well)」からじゃないであろうか。結局こういう構図が成り立つ。

IVectorView<T> -> IVectorView_impl<T> -> IInspectable

 少し見えてきた。

2. generic 周りのコードを読む

 次の:

#ifndef DEF___FIVector_1_HSTRING_USE
#define DEF___FIVector_1_HSTRING_USE
#if defined(__cplusplus) && !defined(RO_NO_TEMPLATE_NAME)
} /*extern "C"*/ 
namespace ABI { namespace Windows { namespace Foundation { namespace Collections {
template <>
struct __declspec(uuid("98b9acc1-4b56-532e-ac73-03d5291cca90"))
IVector<HSTRING> : IVector_impl<HSTRING> {
static const wchar_t* z_get_rc_name_impl() {
return L"Windows.Foundation.Collections.IVector`1<String>"; }
};
typedef IVector<HSTRING> __FIVector_1_HSTRING_t;
#define ____FIVector_1_HSTRING_FWD_DEFINED__
#define __FIVector_1_HSTRING ABI::Windows::Foundation::Collections::__FIVector_1_HSTRING_t

/* ABI */ } /* Windows */ } /* Foundation */ } /* Collections */ }
extern "C" {
#endif //__cplusplus
#endif /* DEF___FIVector_1_HSTRING_USE */

Windows.Foundation.h より

をよく見ると、上の Notes on Design にあったような型が見られる。ちゃんと継承関係も上に読み取った通りになっている。さほど難しくない。

3. まとめ

 ある程度の定義の構図は見えてきたと思う。ただ、これを使って何かを作るのはもっと難しい。

 うまくいったら次も記事にしたいな、って思います。今日はこの辺で。

※ 間違っている情報等ありましたら、ブログコメント欄を使って記述していただけるとありがたい。