物置き

RapidXMLのノードをBOOST_FOREACHをつかってループする

ノードとかアトリビュートをforループでぶんまわす際の

for (xml_node<> *node  = doc.first_node(); node; node = node->next_sibling()) {

という記述はVimScriptだのPythonのfor構文に毒された身にはタイプするのがしんどいというか、
せめてBOOST_FOREACHで回せるようにしたいと思ったので、
回せるようにするためのクラスを書いてみた。(車輪再発明の予感)

#include <rapidxml_iterators.hpp>
namespace rapidxml {
  template <template <typename T> class Iter, typename Ch = char>
    struct bgnend
    {
      typedef const Iter<Ch> const_iterator;
      typedef Iter<Ch> iterator;

      bgnend(xml_node<Ch>& obj_) : obj(obj_) {}
      xml_node<Ch>& obj;
      iterator  begin() const { return iterator(&obj); }
      iterator  end() const { return iterator(); }
    };

  typedef bgnend<node_iterator> node_bgnend;
  typedef bgnend<attribute_iterator> attr_bgnend;
}	// end of namespace rapidxml

これをつかうとBOOST_FOREACHでノード|アトリビュートを回せるようになる。こんな感じ。

#include <iostream>
#include <string>
#include <rapidxml_iterators.hpp>
#include <boost/foreach.hpp>

using namespace std;
using namespace rapidxml;

#define BOOST_FOREACH foreach

// (中略)このへんで上のヘルパークラスを定義しているものとする

int main()
{
  std::string data("<a><aa attr=\"&lt;value&gt;\">test</aa></a>");
  xml_document<> doc;
  doc.parse<0>((char*)data.c_str());

  node_bgnend n(doc);
  foreach(xml_node<>& node, n) {
    cout << "ノード名: " << node.name() << "\n";
    node_bgnend n2(node);
    foreach(xml_node<>& child, n2) {
      cout << "ノード名: " << child.name() << "\n";
      cout << "テキスト: " << child.value() << "\n";
      attr_bgnend a(child);
      foreach(xml_attribute<>& attr, a) {
        cout << "属性名: " << attr.name() << " ";
        cout << "属性値: " << attr.value() << "\n";
      }
    }
  }
}

forループの記述量が減るのはいいのだけど
これはこれで、いちいちヘルパークラスのインスタンスを生成しなきゃいけないのが面倒だったりする。
もっとスマートな解決方法はないものだろうか・・・

(追記)
BOOST_FOREACHってIteratorのpairも渡せるのか。
それじゃあ、make_pairみたいな、pairを生成する関数を呼び出してBOOST_FOREACHに渡すような形にできないかな。
後で試してみよう。

(さらに追記)
できたー。

#include <iostream>
#include <string>
#include <utility>
#include <rapidxml_iterators.hpp>
#include <boost/foreach.hpp>

namespace rapidxml {
  template <template <typename T> class I, typename Ch>
  std::pair<I<Ch>, I<Ch> >
  range(xml_node<Ch>& obj)
  {
    return std::make_pair(I<Ch>(&obj), I<Ch>());
  }

  template <typename Ch>
    std::pair<node_iterator<Ch>, node_iterator<Ch> >
  node_range(xml_node<Ch>& obj)
  {
    return range<node_iterator,Ch>(obj);
  }
  template <typename Ch>
    std::pair<attribute_iterator<Ch>, attribute_iterator<Ch> >
  attr_range(xml_node<Ch>& obj)
  {
    return range<attribute_iterator,Ch>(obj);
  }
} // end of namespace rapidxml

using namespace std;
using namespace rapidxml;

#define foreach BOOST_FOREACH

int main()
{
  std::string data("<a><aa attr=\"&lt;value&gt;\">test</aa></a>");
  xml_document<> doc;
  doc.parse<0>((char*)data.c_str());

  foreach(xml_node<>& node, node_range<char>(doc)) {
    cout << "ノード名: " << node.name() << "\n";
    foreach(xml_node<>& c , node_range<char>(node)) {
      cout << "ノード名: " << c.name() << "\n";
      foreach(xml_attribute<>& attr, attr_range<char>(c)) {
        cout << attr.name() << "\n";
        cout << attr.value() << "\n";
      }
    }
  }
}