閑古鳥

オールドプログラマの日記。プログラミングとか病気(透析)の話とか。

oven::block

インターフェイスの向き - 偏見プログラマの語り!を読んで、EnumWindows関数をアルゴリズムに渡したいという部分に既視感を覚えたのでぐぐってみたら、この手の関数はovenを使うとおいしく頂けるようになっているようです。元記事の話とは脱線してしまいますが、元記事の注釈にもある内向きのインターフェイス外向きにするひとつの方法ということで。

EnumChildWindows関数をラップして、任意のウィンドウの子ウィンドウの名前を持ったRangeを作ってみます。以下、P-Stade 1.04.3のovenを使用しています。

struct enum_child_windows
{
    typedef std::string yield_type;

    explicit enum_child_windows(HWND parent = ::GetDesktopWindow()) : parent(parent) {}

    template<typename Yield>
    void operator()(Yield yield) const
    {
      typedef data<Yield> data_t;
      data_t data(yield);
      ::EnumChildWindows(parent, &callback<data_t>, reinterpret_cast<LPARAM>(&data));
    }

private:
  template<class Yield>
  struct data
  {
    data(Yield yield) : yield(yield) 
    { }

    Yield yield;
  };

  template<class DataT>
  static BOOL CALLBACK callback(HWND hwnd, LPARAM lParam)
  {
    DataT* data = reinterpret_cast<DataT*>(lParam);

    // 名前からするとHWNDを渡す方が良いんだろうけど
    std::string title(::GetWindowTextLength(hwnd) + 1, '\0');
    ::GetWindowTextA(hwnd, &title[0], title.size());
    data->yield(title);

    return TRUE;
  }

  HWND parent;
};

このような関数オブジェクトを作成し、oven::blockに渡すとRangeにしてくれます。

#include <iostream>
#include <boost/range/algorithm/for_each.hpp>

void print(std::string const& name)
{
  std::cout << name << std::endl;
}

int main(int argc, char* argv[])
{
  // すべてのウィンドウタイトルを印字する
  boost::for_each(pstade::oven::block(enum_child_windows()), &print);
}

これを使用すれば、元記事の「最も長いウィンドウタイトルを取得する処理」は以下のように書けます。

#include <iostream>
#include <boost/range/algorithm/max_element.hpp>
#include <pstade/oven/memoized.hpp>

struct comp {
  bool operator()(std::string const& lhs, std::string const& rhs) {
    return lhs.length() < rhs.length();
  }
};

int main(int argc, char* argv[])
{
  using namespace pstade;
  using namespace oven;
  std::cout << *boost::max_element(block(enum_child_windows()) | memoized, comp());
}

oven::blockが作るのはSingle Pass Rangeであるため、そのままではmax_elementには渡せないのでoven::memoizedも使用しています。正直この辺の理屈はよくわかっていません……が、とりあえずovenはすごいものだと改めて認識しました。

oven::blockができるまで(?)

ぐぐって出てきた一連の記事。

自分ではとても書けない……。