ゆとりーなの日記

日記的な事を書いて行くと思はれる

IUnknowの機󠄁能を實裝したクラス

DirectWriteで緣附き文󠄁字など裝飾󠄁を施した文󠄁字を書かうと思つたらIDWriteTextRendererを實裝する事になる訣だが、IUnknownのAddRef、Release、QueryInterfaceを各カスタムレンダラ每に每回書くのもダルいのでクラスを作つてみた。

template <typename IBase, typename... Suports>
class IUnknownBase : public IBase, private boost::noncopyable {
 public:
  STDMETHODIMP_(ULONG) AddRef() override {
    return InterlockedIncrement(&refCount);
  }

  STDMETHODIMP_(ULONG) Release() override {
    ULONG newCount = InterlockedDecrement(&refCount);
    if (!newCount) {
      delete this;
    }
    return newCount;
  }

  STDMETHODIMP QueryInterface(const IID &riid, void **ppvObject) override {
    *ppvObject = CheckInterface<IBase, Suports...>{}(riid, this);
    if (!*ppvObject) {
      return E_NOINTERFACE;
    }
    this->AddRef();
    return S_OK;
  }

 protected:
  IUnknownBase() : refCount{0} {}
  virtual ~IUnknownBase() = default;

 private:
  ULONG refCount;
};

多重繼承にするのもあれなので、テンプレートで基底クラスを指定して繼承するやうにした。コピー禁止設定と假裝デストラクタも用意󠄁した。ヒープに作つてReleaseで自殺󠄀をすると云ふ擧動の爲にコンストラクタと假裝デストラクタはprotectedにしておいた。
あとはQueryInterfaceだが、そのうちIStreamとかもこれを使󠄁つて實裝するかも知れないので各COMオブジェクトがサポートするインターフェースを2番目以降󠄁のテンプレート引數に指定できるやうにした(直接繼承するものは指定しなくてよい)。

template <typename NewInterface, typename... Suports>
struct CheckInterface {
  template <typename BaseInterface>
  void *operator ()(const IID &riid, BaseInterface *base) {
    return riid == __uuidof(NewInterface) ? static_cast<NewInterface *>(base) : CheckInterface<Suports...>{}(riid, base);
  }
};

template <typename NewInterface>
struct CheckInterface<NewInterface> {
  template <typename BaseInterface>
  void *operator ()(const IID &riid, BaseInterface *base) {
    return riid == __uuidof(NewInterface) ? static_cast<NewInterface *>(base) : nullptr;
  }
};

可變長テンプレートクラスでやつてきたriidがサポートしてゐるインターフェースと一致してたらその型にキャストしたthisを返󠄁す。再歸で回して1つも一致しなかつたらNULLを返󠄁すのでE_NOINTERFACEを返󠄁す。

class MyTextRenderer : public IUnknownBase<IDWriteTextRenderer, IDWritePixelSnapping, IUnknown> {
 public:
  static MyTextRenderer *Make() {
    return new MyTextRenderer{};
  }

  STDMETHODIMP IsPixelSnappingDisabled(void *, BOOL *isDisabled) override {
    return E_NOTIMPL;
  }

  STDMETHODIMP GetCurrentTransform(void *, DWRITE_MATRIX *) override {
    return E_NOTIMPL;
  }

  STDMETHODIMP GetPixelsPerDip(void *, FLOAT *) override {
    return E_NOTIMPL;
  }

  STDMETHODIMP DrawGlyphRun(void *, FLOAT, FLOAT,
                            DWRITE_MEASURING_MODE , const DWRITE_GLYPH_RUN *,
                            const DWRITE_GLYPH_RUN_DESCRIPTION *,
                            IUnknown *) override {
    return E_NOTIMPL;
  }

  STDMETHODIMP DrawUnderline(void *,  FLOAT , FLOAT ,
                             const DWRITE_UNDERLINE *, IUnknown *) override {
    return E_NOTIMPL;
  }

  STDMETHODIMP DrawStrikethrough(void *, FLOAT, FLOAT,
                                 const DWRITE_STRIKETHROUGH *, IUnknown *) override {
    return E_NOTIMPL;
  }

  STDMETHODIMP DrawInlineObject(void *, FLOAT,  FLOAT,
                                IDWriteInlineObject* , BOOL, BOOL,
                                IUnknown *) override {
    return E_NOTIMPL;
  }

 private:
  MyTextRenderer() = default;
  ~MyTextRenderer() = default;
};

テンプレート引數の意󠄁味が少しダサいやうな氣もするが(サポートする型のリストつて明記する爲に型リストを渡す方が綺麗?)、大體こんな感じで使󠄁へる。因みに最初からComPtr對應にするならこんな感じ。

template <typename Com, typename... Args>
Microsoft::WRL::ComPtr<Com> MakeComPtr(Args... args) {
  return Microsoft::WRL::ComPtr<Com>{new Com(std::forword<Args>(args)...)};
}

class MyTextRenderer : public IUnknownBase<IDWriteTextRenderer, IDWritePixelSnapping, IUnknown> {
 public:
  static Microspft::WTL::ComPtr<MyTextRenderer> Make() {
    struct Impl : MyTextRenderer {};
    return MakeComPtr<Impl>();
  }

 protected:
  ~MyTextRenderer() = default;
};

make_sharedのComPtr版を作つて函數內クラスを使󠄁つてprivateコンストラクタをMakeComPtrが呼べる樣に細工しておく。まあ別にファクトリの中で普通󠄁にnewするのでも良いけどこの邊りは好みかしら。
處でMSDNのサンプルだとスタックに置けるやうになつてゐたり、假裝デストラクタになつてゐなかつたりするのだが、その邊りは大丈󠄁夫なのだらうか。