2009/08/17


ずいぶん前からXMLRPCをC++で扱うのに小さいライブラリないかなーと思ってました。


書いたといっても結構前からあったのですが、いらん所を削ぎ落として簡素なXMLRPCライブラリとして仕立て上げました。
簡素とは言えど、一般的な物ならば色々動きます。
例えば、ブログに一気に、しかも機械的にポストしたい場合とか

おれは今すぐXMLRPCしたいんだ!そしてどうしてもC++でXMLRPCしたいんだー!

って事ないですか?MovablebTypeをインストールしたけど、XMLRPCするのにC++コンパイラは入ってるけど、PerlやRubyやPythonは入ってない...とか悲しすぎます。
今回紹介する"tinyxmlrpc"はそんな、小さい様で大きな問題を解決出来るかもしれないライブラリです。
mattn's tinyxmlrpc at master - GitHub

tiny xmlrpc library written in C++

http://github.com/mattn/tinyxmlrpc/tree/master
今githubが不調でpushが反映されていません。リポジトリからのcloneは出来る様です。
ソースはC++で書いてます。WindowsとUNIX(Linuxでだけ動作確認)で動作します。

機能としては、STLのvectorとかmapとかに親和性高くなる様になっています。内部ではlibxmlでパースしていてcurlで通信しています。
小ささをウリにしていますが、libxmlとcurlのリンクは必須です。
サンプルコードを以下に示します。
#include "tinyxmlrpc.h"
#include <iostream>

class vargs {
private:
    tinyxmlrpc::value::Array args_;
public:
    vargs& operator <<(tinyxmlrpc::value arg) {
        args_.push_back(arg);
        return *this;
    }
    tinyxmlrpc::value::Array& list() {
        return this->args_;
    }
    vargs& first() {
        this->args_.clear();
        return *this;
    }
};

int main(int argc, char* argv[]) {
#ifdef _WIN32
    WSAData wsadata;
    WSAStartup(MAKEWORD(2,0), &wsadata);
#endif

    if (argc != 4) return -1;

    std::string endpoint = argv[1];
    std::string user = argv[2];
    std::string pass = argv[3];

    try {
        tinyxmlrpc::value::Array req;
        tinyxmlrpc::value res;
        tinyxmlrpc::value::Struct entry;
        vargs args;

        res = tinyxmlrpc::call(endpoint,
                "metaWeblog.getRecentPosts",
                (args.first() << "1" << user << pass << 3 << true).list());
        if (!tinyxmlrpc::failed(res)) {
            for(int n = 0; n < res.size(); n++) {
                std::vector<std::string> members = res[n].listMembers();
                std::vector<std::string>::const_iterator it;
                std::cout << "{" << std::endl;
                for(it = members.begin(); it != members.end(); it++) {
                    std::string val = res[n][*it].to_str();
                    std::cout << "  " << it->c_str() << "=" << val.c_str() << std::endl;
                }
                std::cout << "}" << std::endl;
            }
        } else {
            std::cerr << res << std::endl;
        }

        entry["title"] = "hasegawaにいじめられた";
        entry["description"] = "今日は、wassrでhasegawaにいじめられた。悲しかった。";
        entry["dateCreated"] = "";
        res = tinyxmlrpc::call(
                endpoint,
                "metaWeblog.newPost",
                (args.first() << "1" << user << pass << entry << 1).list());
        if (!tinyxmlrpc::failed(res)) {
            std::cout << "result:" << res << std::endl;
        } else 
            std::cerr << res << std::endl;
    } catch(tinyxmlrpc::value::Exception& e) {
        std::cerr << e.message << std::endl;
    }

    return 0;
}

これを使ってコマンドラインから引数として、MetaWeblogをサポートしているブログのエンドポイントURLユーザパスワードを指定すると、「hasegawaにいじめられた」というエントリがポストされます。
タイトルや本文に、特に意味はありません。すごいですね!


ライセンスはまだ決めてませんが、今のところBSDを予定しています。使ってみて下さい。
コードはgithubにあるので、patchウェルカムです。
今後は、この記事の為だけに仕上げた疑いのある汚いコードを綺麗にしていくつもりです。

参考文献:C++で軽量Webサーバ書いた。
Posted at by



2009/08/16


furyu-teiさんの記事でXSLTの場合はendpointを変えないといけない事が分かった。
AmazonのProduct Advertising API認証プロキシ(REST版・GAE用)ソース

XSLTを使用する場合(Styleオプション指定時)、http://webservices.amazon.co.jp/onca/xmlやhttp://ecs.amazonaws.jp/onca/xmlで指定すると認証エラーに。専用のエンドポイント(http://xml-jp.amznxslt.com/onca/xml)の指定が必要らしい。

http://d.hatena.ne.jp/furyu-tei/20090703/paproxy
お陰様で動くようになりました。
XSLとjQuery/HTMLだけで作る、amazon最速検索
Posted at by



2009/08/05


先日「jjencodeをApacheのmod_ext_filterに仕込む」という記事でhasegawaさんのjjencodeをgoogle chromeなんかで使われているjavascriptエンジン"v8"で動かしてみたのですが、あまりに遅いですし、一回javascriptエンジンが走ってしまうという事がボトルネックに繋がっているんだ...という勝手な推測の元、「jjencodeをv8に依存しない形でc++に移植しよう」と思い始めたのがこの記事をポストする30分前。今出来上がりました。テストしながら書いてます。
まず、オリジナルのまま関数で移植しました。ふつーのC++のコードです。
#include <sstream>
#include <iostream>
#include <string>

using namespace std;

string jjencode(string gv, string& text) {
  stringstream r, s;
  const char* b[] = { "___", "__$", "_$_", "_$$", "$__", "$_$", "$$_", "$$$", "$___", "$__$", "$_$_", "$_$$", "$$__", "$$_$", "$$$_", "$$$$" };

  for (int i = 0; i < text.size(); i++) {
    char n = text.at( i );
    if (n == 0x22 || n == 0x5c) {
      s << "\\\\\\" << dec << n;
    } else if ((0x20 <= n && n <= 0x2f) || (0x3A <= n == 0x40) || (0x5b <= n && n <= 0x60) || (0x7b <= n && n <= 0x7f)) {
      s << dec << n;
    } else if ((0x30 <= n && n <= 0x39) || (0x61 <= n && n <= 0x66)) {
      if (!s.str().empty()) r << "\"" << s.str() << "\"+";
      r << gv << "." << b[ n < 0x40 ? n - 0x30 : n - 0x57 ] << "+";
      s.str("");
      s.clear(stringstream::goodbit);
    } else if (n == 0x6c) { // 'l'
      if (!s.str().empty()) r << "\"" << s.str() << "\"+";
      r << "(![]+\"\")[" << gv << "._$_]+";
      s.str("");
      s.clear(stringstream::goodbit);
    } else if (n == 0x6f) { // 'o'
      if (!s.str().empty()) r << "\"" << s.str() << "\"+";
      r << gv << "._$+";
      s.str("");
      s.clear(stringstream::goodbit);
    } else if (n == 0x74) { // 'u'
      if( !s.str().empty() ) r << "\"" << s.str() << "\"+";
      r << gv << ".__+";
      s.str("");
      s.clear(stringstream::goodbit);
    } else if (n == 0x75) { // 'u'
      if( !s.str().empty() ) r << "\"" << s.str() << "\"+";
      r << gv << "._+";
      s.str("");
      s.clear(stringstream::goodbit);
    } else if (n < 128) {
      if (!s.str().empty()) r << "\"" << s.str();
      else r << "\"";
      r << "\\\\\"+";
      stringstream ss;
      string ns;
      ss << oct << int(n) && ss >> ns;
      string s08i = "01234567";
      for (string::iterator it = ns.begin(); it != ns.end(); it++) {
        if (s08i.find(*it) != string::npos)
          r << gv << "." << b[ *it-'0' ] << "+";
        else
          r << *it;
      }
      s.str("");
      s.clear(stringstream::goodbit);
    }else{
      if (!s.str().empty()) r << "\"" << s;
      else r << "\"";
      r << "\\\\\"+" << gv << "._+";
      stringstream ss;
      string ns;
      ss << hex << int(n) && ss >> ns;
      string s16i = "0123456789";
      string s16a = "abcdef";
      for (string::iterator it = ns.begin(); it != ns.end(); it++) {
        if (s16i.find(tolower(*it)) != string::npos)
          r << gv << "." << b[ *it-'0' ] << "+";
        else
          if (s16a.find(tolower(*it)) != string::npos)
            r << gv << "." << b[ tolower(*it)-'a'+10 ] << "+";
          else
            r << *it;
      }
      s.str("");
      s.clear(stringstream::goodbit);
    }
  }
  if (!s.str().empty()) r << "\"" << s.str() << "\"+";

  s.str("");
  s.clear(stringstream::goodbit);
  s <<
    gv << "=~[];" <<
    gv << "={___:++" << gv +",$$$$:(![]+\"\")["+gv+"],__$:++"+gv+",$_$_:(![]+\"\")["+gv+"],_$_:++" <<
    gv+",$_$$:({}+\"\")["+gv+"],$$_$:("+gv+"["+gv+"]+\"\")["+gv+"],_$$:++"+gv+",$$$_:(!\"\"+\"\")[" <<
    gv+"],$__:++"+gv+",$_$:++"+gv+",$$__:({}+\"\")["+gv+"],$$_:++"+gv+",$$$:++"+gv+",$___:++"+gv+",$__$:++"+gv+"};" <<
    gv+".$_=" <<
    "("+gv+".$_="+gv+"+\"\")["+gv+".$_$]+" <<
    "("+gv+"._$="+gv+".$_["+gv+".__$])+" <<
    "("+gv+".$$=("+gv+".$+\"\")["+gv+".__$])+" <<
    "((!"+gv+")+\"\")["+gv+"._$$]+" <<
    "("+gv+".__="+gv+".$_["+gv+".$$_])+" <<
    "("+gv+".$=(!\"\"+\"\")["+gv+".__$])+" <<
    "("+gv+"._=(!\"\"+\"\")["+gv+"._$_])+" <<
    gv+".$_["+gv+".$_$]+" <<
    gv+".__+" <<
    gv+"._$+" <<
    gv+".$;" <<
    gv+".$$=" <<
    gv+".$+" <<
    "(!\"\"+\"\")["+gv+"._$$]+" <<
    gv+".__+" <<
    gv+"._+" <<
    gv+".$+" <<
    gv+".$$;" <<
    gv+".$=("+gv+".___)["+gv+".$_]["+gv+".$_];" <<
    gv+".$("+gv+".$("+gv+".$$+\"\\\"\"+" << r.str() << "\"\\\"\")())();";

  return s.str();
}

int main(void) {
  string line, content;
  while(!cin.eof()) {
    getline(cin, line);
    content += line;
  }
  cout << jjencode("$", content) << endl;
}
これで前回失敗したjqueryで試してみました。結果は...




30秒以内では戻ってこず断念。
あれーと思って、「そうか。一回結果を格納しちゃってるのがまずいのか!そうなら逐次出力すればいい!」とまた勝手な推測の元、stringstreamに溜め込まずcoutに吐き出す様修正しました。
#include <sstream>
#include <iostream>
#include <string>

using namespace std;

int main(void) {
  const char* b[] = { "___", "__$", "_$_", "_$$", "$__", "$_$", "$$_", "$$$", "$___", "$__$", "$_$_", "$_$$", "$$__", "$$_$", "$$$_", "$$$$" };
  string gv = "$";
  cout <<
    gv << "=~[];" <<
    gv << "={___:++" << gv +",$$$$:(![]+\"\")["+gv+"],__$:++"+gv+",$_$_:(![]+\"\")["+gv+"],_$_:++" <<
    gv+",$_$$:({}+\"\")["+gv+"],$$_$:("+gv+"["+gv+"]+\"\")["+gv+"],_$$:++"+gv+",$$$_:(!\"\"+\"\")[" <<
    gv+"],$__:++"+gv+",$_$:++"+gv+",$$__:({}+\"\")["+gv+"],$$_:++"+gv+",$$$:++"+gv+",$___:++"+gv+",$__$:++"+gv+"};" <<
    gv+".$_=" <<
    "("+gv+".$_="+gv+"+\"\")["+gv+".$_$]+" <<
    "("+gv+"._$="+gv+".$_["+gv+".__$])+" <<
    "("+gv+".$$=("+gv+".$+\"\")["+gv+".__$])+" <<
    "((!"+gv+")+\"\")["+gv+"._$$]+" <<
    "("+gv+".__="+gv+".$_["+gv+".$$_])+" <<
    "("+gv+".$=(!\"\"+\"\")["+gv+".__$])+" <<
    "("+gv+"._=(!\"\"+\"\")["+gv+"._$_])+" <<
    gv+".$_["+gv+".$_$]+" <<
    gv+".__+" <<
    gv+"._$+" <<
    gv+".$;" <<
    gv+".$$=" <<
    gv+".$+" <<
    "(!\"\"+\"\")["+gv+"._$$]+" <<
    gv+".__+" <<
    gv+"._+" <<
    gv+".$+" <<
    gv+".$$;" <<
    gv+".$=("+gv+".___)["+gv+".$_]["+gv+".$_];" <<
    gv+".$("+gv+".$("+gv+".$$+\"\\\"\"+";
  cout.flush();

  stringstream s;
  while(!cin.eof()) {
    char n;
    cin.get(n);

    if (n == 0x22 || n == 0x5c) {
      s << "\\\\\\" << dec << n;
    } else if ((0x20 <= n && n <= 0x2f) || (0x3A <= n == 0x40) || (0x5b <= n && n <= 0x60) || (0x7b <= n && n <= 0x7f)) {
      s << dec << n;
    } else if ((0x30 <= n && n <= 0x39) || (0x61 <= n && n <= 0x66)) {
      if (!s.str().empty()) cout << "\"" << s.str() << "\"+";
      cout << gv << "." << b[ n < 0x40 ? n - 0x30 : n - 0x57 ] << "+";
      s.str("");
      s.clear(stringstream::goodbit);
    } else if (n == 0x6c) { // 'l'
      if (!s.str().empty()) cout << "\"" << s.str() << "\"+";
      cout << "(![]+\"\")[" << gv << "._$_]+";
      s.str("");
      s.clear(stringstream::goodbit);
    } else if (n == 0x6f) { // 'o'
      if (!s.str().empty()) cout << "\"" << s.str() << "\"+";
      cout << gv << "._$+";
      s.str("");
      s.clear(stringstream::goodbit);
    } else if (n == 0x74) { // 'u'
      if( !s.str().empty() ) cout << "\"" << s.str() << "\"+";
      cout << gv << ".__+";
      s.str("");
      s.clear(stringstream::goodbit);
    } else if (n == 0x75) { // 'u'
      if( !s.str().empty() ) cout << "\"" << s.str() << "\"+";
      cout << gv << "._+";
      s.str("");
      s.clear(stringstream::goodbit);
    } else if (n < 128) {
      if (!s.str().empty()) cout << "\"" << s.str();
      else cout << "\"";
      cout << "\\\\\"+";
      stringstream ss;
      string ns;
      ss << oct << int(n) && ss >> ns;
      string s08i = "01234567";
      for (string::iterator it = ns.begin(); it != ns.end(); it++) {
        if (s08i.find(*it) != string::npos)
          cout << gv << "." << b[ *it-'0' ] << "+";
        else
          cout << *it;
      }
      s.str("");
      s.clear(stringstream::goodbit);
    }else{
      if (!s.str().empty()) cout << "\"" << s;
      else cout << "\"";
      cout << "\\\\\"+" << gv << "._+";
      stringstream ss;
      string ns;
      ss << hex << int(n) && ss >> ns;
      string s16i = "0123456789";
      string s16a = "abcdef";
      for (string::iterator it = ns.begin(); it != ns.end(); it++) {
        if (s16i.find(tolower(*it)) != string::npos)
          cout << gv << "." << b[ *it-'0' ] << "+";
        else
          if (s16a.find(tolower(*it)) != string::npos)
            cout << gv << "." << b[ tolower(*it)-'a'+10 ] << "+";
          else
            cout << *it;
      }
      s.str("");
      s.clear(stringstream::goodbit);
    }
    cout.flush();
  }
  if (!s.str().empty()) cout << "\"" << s.str() << "\"+";

  cout << "\"\\\"\")())();" << endl;
}
これで前回失敗したjqueryで試してみました。結果は...




またもや30秒以内では戻ってこず断念。

おかしいな...と思ってmod_ext_filterを通さず実行したらjqueryのファイル全体でも1.5秒程で終了しているので、どうやらmod_ext_filter側が蓄積してしまっているのが原因だと分かりました。 ... orz

まーそうやわなー。あたりまえやなー。勝手な推測しすぎです。

ただ、一度作ったjavascriptファイルをこのツールで変換して静的ファイルとして置く事は出来るはずですので、利用価値がない...とまでは言えないかもしれません。なお、jQueryの場合は$を潰すので変数gvを$以外のものにしないと動きません。
Posted at by