Install Theme

Your web-browser is very outdated, and as such, this website may not display properly. Please consider upgrading to a modern, faster and more secure browser. Click here to do so.

タンブラー?

image Big Sky

7月 18 '14

C++ 製 micro web framework「crow」を使って lingr の bot 書いてみた。

先日、github で crow という、python の flask からインスパイアされた C++ 製 micro web framework を見つけました。

ipkn/crow - GitHub


http://ift.tt/TrRD1t

ルーティングの書き方が flask ぽく、かつモダンな C++ な書き方だったの気に入りました。

crow::Crow app;

CROW_ROUTE(app, ”/”)
    .name("hello")
([]{
    return "Hello World!";
});
app.port(18080)
    .multithreaded()
    .run();

何か作ってみたくなったので、lingr の bot を書いてみました。crow には JSON パーサ(シリアライザ)が同梱されているので API サーバを作るのも簡単そうです。

#include "crow.h"
#include "json.h"

#include <iostream>
#include <sstream>
#include <regex>

static std::vector<std::string>
str_split(const string s, const string d) {
  std::vector<string> r;
  std::string::size_type n;
  std::string b = s;
  while ((n = b.find_first_of(d)) != b.npos) {
    if(n > 0) r.push_back(b.substr(0, n));
    b = b.substr(n + 1);
  }
  if(b.length() > 0) r.push_back(b);
  return r;
}

static std::string&
str_replace(std::string& s, const std::string p, const std::string r) {
  std::string::size_type n(s.find(p));
  while (n != std::string::npos) {
    s.replace(n, p.length(), r);
    n = s.find(p, n + r.length());
  }
  return s;
}

class logger : public crow::ILogHandler {
public:
  void log(string message, crow::LogLevel level) override {
    cerr « message;
  }
};

int
main() {
  std::vector<std::pair<const std::regex, const std::string» resp;
  std::ifstream ifs("pattern.txt");
  if (ifs.fail()) return 1;
  string buf;
  while(ifs && getline(ifs, buf)) {
    auto t = str_split(buf, ”\t”);
    if (t.size() != 2) continue;
    std::cout « "Loading pattern: " « t[0] « ”=” « t[1] « std::endl;
    resp.push_back(make_pair(std::regex(t[0]), t[1]));
  }
  ifs.close();

  crow::Crow app;

  CROW_ROUTE(app, ”/”)
    ([]{
        return "lingr-cppbot";
    });

  CROW_ROUTE(app, "/lingr")
    ([&](const crow::request& req){
      auto x = crow::json::load(req.body);
      if (!x) return crow::response(400);

      std::stringstream os;
      for (const auto& e : x["events"]) {
        std::string t = e["message"]["text"].s();
        for (const auto& r : resp) {
          std::smatch matches;
          if (!regex_match(t, matches, r.first)) continue;
          std::string output = r.second;
          for (std::smatch::size_type i = 0; i < matches.size(); ++i) {
            std::stringstream ss;
            ss « ”$” « i;
            str_replace(output, ss.str(), matches[i]);
          }
          os « output;
          break;
        }
      }
      return crow::response{os.str()};
    });

  crow::logger::setLogLevel(crow::LogLevel::INFO);
  crow::logger::setHandler(std::make_shared<logger>());

  uint16_t port;
  std::stringstream ss;
  ss « getenv("PORT");
  ss » port;
  if (port == 0) port = 8080;
  app.port(port).multithreaded().run();
}

// vim:set et:

仕組みは至って簡単で、カレントディレクトリにある pattern.txt という、以下の様なファイルを読み込み、正規表現(タブ文字で区切られた左側)に対する置換文字列(右側)を設定します。

^あー$  いー
^いー$  うー
^うー$  えー
^えー$  おー
^おー$  もうええやろ!
^(.+)(大好き|だいすき)  $1は俺の物だ。勝手に呼び捨てにするな。

起動して、正規表現にマッチする発言を受信すると、置換文字列をサブマッチなどで入れ替えて返答します。

cppbot

サブマッチ置換が使えるので、ある程度人間っぽい発言も可能ですし、gh:mattn という文字列から http://ift.tt/1zOZhDR という返答をさせてアンカーを作る等の応用例もあります。(参考資料)

crow は内部でスレッドを使っており、複数同時リクエストにも応答出来る様です。色々遊べそうなので試してみてはいかがでしょうか。



from Big Sky http://ift.tt/1lapnHx
7月 11 '14

Vim scriptテクニックバイブル ~Vim使いの魔法の杖

こんにちわ。Vim scriptサポーターズの mattn です。

ちょうど3年程前、Vimテクニックバイブルという書籍を執筆させて頂きました。

Big Sky :: Vimテクニックバイブル ~作業効率をカイゼンする150の技


http://ift.tt/1dcpDXv

おかげ様で、色んな方から反響を頂き執筆して良かったと思いました。初めて自分が書いた書籍が販売されるという高揚感に包まれる中、書籍の販売からたった数週間後、何を思ったか僕と KoRoN さんは github 上に vim-users-jp というオープングループを作る事になりました。

Big Sky :: github上にvim-users-jpというorganizationを作った。


http://ift.tt/1sFQtLw

そして数日後、vim-jp が誕生します。

vim-jp » Vimのユーザーと開発者を結ぶコミュニティサイト


http://vim-jp.org/

今から思えば、かなり思い切った事をやったもんだなーと我ながら思います。あの思いっきりが無かったら vim-jp は生まれなかったと思います。

あれからVim界隈は人気に陰りが出るどころか、さらなる進展を遂げ、vim-jp から提供するパッチが vim_dev に溢れ、多くの contribute authors を排出し、多くの優秀な Vim プラグインが作られ、毎週 vimrc 読書会が開催され、本当に20年も昔に作られたテキストエディタなのか?と疑いたくなる状況となりました。

Vim script の知名度についてはどうでしょうか?プラグインを書く人が増えたものの、ちゃんとした誘導書が無い為に色んなテクニックが氾濫し、その為に敷居があがり「変態言語」と呼ばれる部類に入れられる事も多い様です。

そして個々の力技により色んなハックが生まれ、時に Vimmer は怖いと言われる事も目にします。しかしそれ程に Vimmer は Vim script を愛しているのです。

ただし良く考えて下さい。

プログラミング言語 Vim script は本来、vimrc を記述する為の言語なのです。

ちゃんとした vimrc を書く為にはちゃんとした Vim script を書く必要があるのです。そして誰かがちゃんとしたハックを何処かに記し、皆が同じハックを共有出来るべきだと思うのです。

Vimテクニックバイブルは Vim の凄さを広める為に生まれましたが、Vim script を皆に知って貰う為にはしっかりとした誘導書が必要なんだ、そんな風に思った事もありました。

そしてVimテクニックバイブル発売から3年、あの時にお世話になった技術評論社様にまたお力をお貸し頂き、今回「Vim scriptテクニックバイブル」という書籍の執筆をお手伝いをさせて頂きました。


Vim Script テクニックバイブルVim Script テクニックバイブル
VimScriptサポーターズ
技術評論社 / ¥ 2,786 (2014-08-06)
 
発送可能時間:近日発売 予約可


実は今回、Vim scriptサポーターズという著者名となっておりますが、かつてない程に Vim script に詳しい人間が揃いました。


Shougo

ご存じ「暗黒美夢王」です。色んな所で有名な Vimmer で、特に補完系のプラグインには絶大な拘りのある人です。

おそらく日本 Vim 界隈では年に一番 Vim script を書いている人だと思います。


thinca

こちらもご存じ「マンボウ」です。vim-jp では Vim script の挙動を一番良く知っている人だと思います。vimrc 読書会でも thinca さんの指摘内容は確実で、皆から「安心と信頼の thinca」と呼ばれています。(※要出典)

彼は基本的に Vim script を書くスピードは速くありません。しかし一つ一つが確実で、他の Vimmer に例を見ない程の慎重派です。だからこそ巷に溢れた vimrc に散乱するダメな Vim script には厳しく指摘が飛ぶのだと思います。


KoRoN

僕のこのブログをホストしている「香り屋」の店主であり、vim-jp 発起者でもあります。

十数年前から vim のバイナリ配布を始め、お世話になった Vimmer も多いと思います。僕もネット上でお付き合いを初めて10数年になりました。去年は大阪でお好み焼きを食べ、知り合った頃の懐かしい話等をさせて頂きました。

Vim の内部構造にめちゃくちゃ詳しい人です。vim-jp でもパッチ屋と呼ばれている部類の人です。

今回、書籍執筆のリーダとして仕切って頂きました。


「これだけ揃えばもうお腹いっぱいだろ!」というメンツが揃い、実践的なテクニックを惜しげも無く記してあります。

人の vimrc からコピペして自分の vimrc を作るのは簡単です。しかし自分だけの Vim を作り上げる為には自分で vimrc を書く必要があるのです。

本書はテクニックバイブルという名前は付いていますが実際は

Vimmer が自分だけの Vim 探しのお手伝いをする際の誘導書

的な存在となります。

ぜひお手に取って付箋紙をベタベタと貼って、自分だけの Vim を作り上げて下さい。



from Big Sky http://ift.tt/1oJTKHa
7月 9 '14

Vim で flappyvird 書いた。

前回、「幅跳び」が好評だったので、あの有名なゲーム「Flappy〇ird」を Vim で出来る flappyvird-vim を書いてみました。

mattn/flappyvird-vim ・ GitHub


http://ift.tt/1lTypYP

flappyvird
画像は開発中の物で、実際のキャラクタは異なります

良かったら遊んで下さい。

:FlappyVird

でゲームスタート、スペースキーでジャンプ、p で一時停止、q もしくは ESC でゲーム終了です。


from Big Sky http://ift.tt/1lTypYT
7月 8 '14

Vim で peco する「veco」書いた。

vimでpecoりたい

— Yoichi Fujimoto ? (@wozozo)

July 8, 2014
って事で書いた。
mattn/ctrlp-veco ・ GitHub


http://ift.tt/1munCZx

実行には CtrlP が必要。

peco の代わりに vim を使う。ただそれだけ。

veco



from Big Sky http://ift.tt/1kyXicB
7月 8 '14

Rob Pike のプログラミングに関する5つの掟

掟1

プログラムの時間を費やす箇所がどこにあるのかは知り得ない。ボトルネックは意外な場所で発生するため後知恵で批判してはならないし、ボトルネックがどこにあるか証明出来るまではスピードハックを入れてはいけない。

掟2

測定しよう。測定し終えるまでは、さらにはコードの一部分が残りのコードの支配的な量とならないならばチューニングを行ってはいけない。

掟3

Fancy アルゴリズムは、n が小さいときに低速となり、通常 n は小さい。Fancy アルゴリズムは大きな定数を有する。n は頻繁に大きくなり得ることを知るまでは fancy アルゴリズムを得てはならない。 (n が大きくなる場合であっても、まず掟 2 を行いなさい)

掟4

Fancy アルゴリズムは単純なものよりもバギーであり、実装が非常に難しい。簡単なアルゴリズムだけでなく、単純なデータ構造を使用すること。

掟5

データが支配する。もし正しいデータ構造と組織的な物事を上手く選択したとすれば、アルゴリズムは殆どの場合自明となる。データ構造はアルゴリズムとは違いプログラミングの中心である。

Pike の掟 1 と 2 は Tony Hoare の有名な格言「早まった最適化は諸悪の根源である。」の言い換えであり、Ken Thompson は Pike の掟 3 と 4 を「疑わしいならばブルートフォースを使用すべきだ」と言い換えた。掟 3 と 4 は、the design philosophy KISS (Keep It Simple, Stupid)の具現。掟 5 は、The Mythical Man-Month で Fred Brooks が述べた。掟 5 は、多くの場合、「スマートオブジェクトを使うと愚かなコードを書く」に短縮される。

参照:

Rob Pike’s 5 Rules of Programming

from Big Sky http://ift.tt/1thJPzh
7月 8 '14

git で pull-request を clone する設定が覚えられないので alias 書いた。

github の pull-request を clone する設定をいつも思い出せなくて、毎回ググってるので alias を書いた。
Checkout github pull requests locally


Here’s fetch : 1 git fetch From github.com:gittip/www.gittip.com * [new ref] refs/pull/100/head -> o…

http://ift.tt/Y9PDKf

$ git config —global alias.pr-setup “config —add remote.origin.fetch +refs/pull/*/head:refs/remotes/origin/pr/*”

とすれば alias がインストールされるので、以後は pull-request を監視したいリポジトリを clone した後

$ git pr-setup

とすれば .git/config に上記リンク先の設定が入るので後は普通に

$ git fetch origin
$ git checkout pr/999

とかすればいい。ちなみに clone した瞬間に設定する方法無いかなーと思ったけど、clone したいだけのリポジトリで勝手に fetch されて太っても嫌だなーと思ったので手動で pr-setup を叩く事にする。

from Big Sky http://ift.tt/1k0JYOb
7月 7 '14

Vim でたんざくを作ろう

:echo webapi#json#decode(webapi#http#get(”http://ift.tt/1lMySvP”, {"q": "カッコイイVimmerになれますように"}).content).c

┏┷┓
┃ ┃
┃カ┃
┃ッ┃
┃コ┃
┃イ┃
┃イ┃
┃V┃
┃i┃
┃m┃
┃m┃
┃e┃
┃r┃
┃に┃
┃な┃
┃れ┃
┃ま┃
┃す┃
┃よ┃
┃う┃
┃に┃
┃ ┃
┗━┛



from Big Sky http://ift.tt/1rFBJOY
7月 6 '14

Re: 断続的にデータを受けながら並行で時間差リアクションを行う

断続的にデータを受けながら並行で時間差リアクションを行う - すぎゃーんメモ


断続的にデータを受けながら並行で時間差リアクションを行う Go はじめて、の次のGo - すぎゃーんメモ にて作った go-genki-bot 、UserStreamから Tweet 取得して返信する…

http://ift.tt/1vGi1jX

ちょっと動きは違うんだけど、こういうパターンもあるよねー。という事で。

package main

import (
    "bufio"
    "fmt"
    "os"
    "runtime"
    "time"
)

func main() {
    runtime.GOMAXPROCS(runtime.NumCPU())

    queue := make(chan rune, 5)

    t := time.AfterFunc(-1, func() {
        // ある分だけ読み込む
        leave: for {
            select {
            case r := <-queue:
                fmt.Println("recv", string(r))
            case <-time.After(100 * time.Millisecond):
                // 100ms 読めなかったら諦める
                break leave
            }
        }
    })

    // タイマーは止めておく
    t.Stop()

    in := bufio.NewReader(os.Stdin)
    for {
        // 何かキー入力
        r, _, err := in.ReadRune()
        if err != nil {
            break
        }

        // LFはいらない
        if r == 0x0A {
            continue
        }

        // タイマーを活性化(3秒後)
        t.Reset(3 * time.Second)

        // タイマーに読んで貰う為に入力したキーをchanで送信しておく
        queue<-r
    }
}

入力を得たらタイマーを活性化する。タイマーは一つしか使わなくて、データが来るたびに更新するのでデータが最後の入力から3秒間何も更新がなければ発動する。タイマーは発動すると100ms内に見つかった分だけ抜き取って表示。

$ go run jisa.go 

recv あ
あいうえ
recv あ
recv い
recv う
recv え

この手法は gof の画面表示部分で使われていて、連続するキー入力に対して毎回描画を行っていると画面がチラ付くという問題を回避出来る。

詳しくは以下を参照して下さい。
Golang でコマンドライン Fuzzy Finder 「gof」作った。 - Qiita


この記事は Go Advent Calendar 2013 の 10 日目の投稿です。 はじめに 業務のツールや連携させる一部の機能として golang を使い出している方もチラホラ現れ始めました。 …

http://ift.tt/1feEizg

Go Advent Day 19 - Eject the Web - The Gopher Academy Blog


Other Articles Next article Go Advent Day 20 - Go in Academia: Emulating Wireless Networks Previous …

http://ift.tt/19UB0wq



from Big Sky http://ift.tt/1oabp9l
7月 3 '14

Windows で便利な clib パッケージを3つ程書いた

先日、C言語向けのパッケージマネージャ clib を紹介したのですが、その後 Windows で使えそうなパッケージを3つ程書きました。
Big Sky :: clib の使い勝手にマジ感動した


C言語でアプリケーションを書くのは他の言語と比べて少し気合が必要ですよね。例えば HTTPからデータを取得する 取得したデータを json パースする 結果の一部を色付きで表示する こんな場合、C言語…

http://ift.tt/1mlyUuG

locale-string.c 最近のライブラリは文字列が utf-8 前提で書かれている物が多く、Windows だと「それ、ツライわー(汗)」だったりするのですが、かと言ってこれだけの為に変換ライブラリを入れるのはアレですし、iconv 入れちゃったら GPL(LGPL) だし仕事で使えねー、みたいな事が起きてしまいます。
でもロケール文字列と utf-8 との変換なら mbstowcs や wcstombs があれば出来るし、Windows なら MultiByteToWideChar や WideCharToMultiByte があれば出来るので、それだけのライブラリを書きました。
mattn/locale-string.c - GitHub


http://ift.tt/1iZ6QCO

使い方は簡単。

setlocale(LC_CTYPE, ””);
const char* ptr = "こんにちわ世界";
char* mbs = utf8_to_locale_alloc(ptr);
assert(NULL != mbs);
char* utf8 = utf8_from_locale_alloc(mbs);
assert(NULL != utf8);
assert(0 == strcmp(ptr, utf8));

使い終わったら free が必要。utf8_to_locale_alloc で utf-8 文字列をロケール文字列へ、utf8_from_locale_alloc でロケール文字列から utf-8 へ変換します。
Linux や Mac だと必要ない機能なのでマクロを用意してあります。

#ifdef _WIN32
# define UTF8_ALLOC(p) utf8_from_locale_alloc(p)
# define UTF8_FREE(p) free(p)
# define LOCALE_ALLOC(p) utf8_to_locale_alloc(p)
# define LOCALE_FREE(p) free(p)
#else
# define UTF8_ALLOC(p) (p)
# define UTF8_FREE(p)
# define LOCALE_ALLOC(p) (p)
# define LOCALE_FREE(p)
#endif

Windows 以外ならばメモリ確保も解放も行いません。

wcwidth.c 皆さんしってる wcwidth です。
wcwidth, wcswidth, wcwidth_cjk, wcswidth_cjk がそろっていますが、オマケで utf-8 文字列の幅を求める string_width と string_width_cjk も付いてきます。

assert(14 == string_width("こんにちわ世界"));
assert(20 == string_width("こ★ん■に●ち▲わ☆世◆界"));
assert(26 == string_width_cjk("こ★ん■に●ち▲わ☆世◆界"));

こんな感じに使います。

ansicolor-w32 最近の Linux のアプリケーションでコンソールに色付きで出力される物も最近では珍しくなくなってきました。
ただ Windows だと API が超メンドクサイので Constellation さんの console-colors.c を使うのが良いのですが、UNIX 系ツールを移植していてなるべくオリジナルコードを触りたくない場合にはツライんです。
mattn/ansicolor-w32.c - GitHub


http://ift.tt/1iZ6Se3

そこで ansicolor-w32.c というのを書きました。ansicolor-w32.h を include すると、エスケープシーケンスを解析して Windows でも色付けで表示してくれます。
ansicon というツールも存在しますが、このライブラリはそれ無しでも色付け可能です。

fprintf, fputs, printf, puts だけ対応しています。

#include <stdio.h>
#ifdef _WIN32
# include "ansicolor-w32.h"
#endif

int
main(int argc, char* argv[]) {
  printf(”\x1b[2J\x1b[10,10H\x1b[24m\x1b[30m”);
  printf(”\x1b[42m博\x1b[43m多\x1b[46mの\x1b[45m塩\x1b[0m”); 
  return 0;
}

例えばこんなコードを書いて実行すると

博多の塩

この様に表示されます。

よろしければお使い下さい。MIT ライセンスです。


from Big Sky http://ift.tt/1oc4yxx
6月 30 '14

MSVC の怖い話

夏だし、怖い話しようぜ!

#include <cstdio>
#include <math.h>
 
class C {
public:
  C() {}
  C(double) { printf("hello world\n”); }
};
 
int main(int argc, char** argv)
{
  C(NAN);
  return 0;
}

僕は今日… こんなコードをコンパイルしたんだ…。gcc でコンパイルして実行したんだ…。

hello world

期待通りだった。
僕は安心し、これを今度は MSVC でビルドしたんだ…。

実行すると…


何も出ない…

僕は怖くなった。

何なんだ!何なんだ!NaN なんだーーー!

僕の C++ 人生はなんだったのか…。僕は病に侵されているに違いない…。

怖いながらもデバッガで追ったんだ…

C++ の怖い話

そしてステップインした…

C++ の怖い話

C++ の怖い話

えっ…

尋常ではない程の汗が僕の頬を伝った。

まっ…まさか!

そう、MSVC には NAN が宣言されていない。
これのおかげで2時間程無駄にした。
Fix test. MSVC doesn’t have definition of NAN by mattn - Pull Request #43 - kazuho/picojson - GitHub


Note: http://ift.tt/Tw7z1P illustrates the problem. MSVC somehow succe…

http://ift.tt/1r5cp4J

NAN が宣言されていない為に、NAN という変数が宣言されてしまっていた。
NAN なんだ、まったく。
酷い一日だった。

なんて日だ!



from Big Sky http://ift.tt/1x4pImF
6月 28 '14

clib の使い勝手にマジ感動した

C言語でアプリケーションを書くのは他の言語と比べて少し気合が必要ですよね。例えば
  • HTTPからデータを取得する
  • 取得したデータを json パースする
  • 結果の一部を色付きで表示する
こんな場合、C言語プログラマは

「HTTP か、じゃぁcurlかな」
「JSON か、parson かな」
「色表示か…エスケープシーケンスでもいいけどWindowsがなー…」

といった事を考え、そこから curl や parson といった資材の調達を始める事になります。途中で新しい機能を追加したくなり、それを外部ライブラリに頼る場合だとその都度資材を調達する必要があり、思考を停止しなければなりません。
この辺は ruby や perl、nodejs、golang 等の様に、ちょっとした手間だけで済ませたい物です。
またC言語の場合、ヘッダファイルはシステムの include フォルダに提供元が期待する通りに配置するか、手元の Makefile でパスを通す必要があり、ライブラリもバージョンに従ったファイル名である必要があります。
こういうチマチマとした作業も楽しかったりはするのですが、今すぐ作りたいって時には腰が重い作業となります。include 配下を整理し出したらどうも気になり始めて、気付けば全く関係の無い事をやっていて作りたかった物が何も出来ていなかった、なんて事もありますよね。

以前 github で clib というプロジェクトを見つけました。
clibs/clib - GitHub


Package manager for the C programming language.

http://ift.tt/16zMDLr

言うなれば、C言語版の bundler であったり、carton であったり、npm だったりする訳です。

今日は、この clib を使うとどの様に開発が進められるかを手順と共に説明します。

package.json を用意する 上記の様な仕様の場合、まず http クライアントが必要になります。github 上には clib から扱えるライブラリが多く存在ます。そしてそのリポジトリには package.json というファイルが含まれています。
今回の仕様を満たす為に、僕は以下の package.json を作りました。

{
  "name": "helloworld",
  "version": "1.0.0",
  "repo": "mattn/helloworld",
  "dependencies": {
    "stephenmathieson/http-get.c": "0.1.0",
    "stephenmathieson/parson-repo.c": "0.1.0",
    "Constellation/console-colors.c": "1.0.1"
  },
  "install": "make install"
}

これをリポジトリ直下に置いて以下のコマンドを叩きます。 clib install

clib install

deps フォルダ配下にパッケージがインストールされました。

│  package.json
│  
└─deps
    ├─console-colors
    │      console-colors.c
    │      console-colors.h
    │      package.json
    │      
    ├─http-get
    │      http-get.c
    │      http-get.h
    │      package.json
    │      
    └─parson
            package.json
            parson.c
            parson.h
            

簡単すぎる!!!まぁなんと便利なんでしょう!
ちなみにこの clib install は、使用するパッケージが依存する別のパッケージも合わせて持ってきてくれる為、依存物地獄に陥る心配もありません。
Makefile を用意する まず src ディレクトリを作り、リポジトリ直下には以下の Makefile を置きます。

CC     ?= cc
PREFIX ?= /usr/local

ifeq ($(OS),Windows_NT)
BINS    = helloworld.exe
LDFLAGS = -lcurldll
CP      = copy /Y
RM      = del /Q /S
MKDIR_P = mkdir
else
BINS    = helloworld
LDFLAGS = -lcurl
CP      = cp -f
RM      = rm -f
MKDIR_P = mkdir -p
endif

SRC  = $(wildcard src/*.c)
DEPS = $(wildcard deps/*/*.c)
OBJS = $(DEPS:.c=.o)

CFLAGS  = -std=c99 -Ideps -Wall -Wno-unused-function -U__STRICT_ANSI__

all: $(BINS)

$(BINS): $(SRC) $(OBJS) $(RES)
    $(CC) $(CFLAGS) -o $@ src/$(@:.exe=).c $(OBJS) $(RES) $(LDFLAGS)

%.o: %.c
    $(CC) $< -c -o $@ $(CFLAGS)

clean:
    $(foreach c, $(BINS), $(RM) $(c);)
    $(RM) $(OBJS)

install: $(BINS)
    $(MKDIR_P) $(PREFIX)/bin
    $(foreach c, $(BINS), $(CP) $(c) $(PREFIX)/bin/$(c);)

uninstall:
    $(foreach c, $(BINS), $(RM) $(PREFIX)/bin/$(c);)

test:
    @./test.sh

.PHONY: test all clean install uninstall

この Makefile を使うと、deps ディレクトリ配下にうまくパスを通してくれる為、以後 make コマンド一発で依存物がビルド出来る様になっています。
コードを書こう コードは、江添さんのブログ「本の虫」の Blogger JSON Feed をパースし、タイトルと URL を一覧表示します。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "console-colors/console-colors.h"
#include "parson/parson.h"
#include "http-get/http-get.h"

int
main(int argc, char* argv[]) {
  http_get_response_t* res = http_get(”http://ift.tt/1jV8qQN”);
  if (res->status != 200) {
    cc_fprintf(CC_FG_RED, stderr, "ERROR");
    goto leave;
  }

  char* json = calloc(res->size + 1, 1);
  if (!json) goto leave;
  strncpy(json, res->data, res->size);

  JSON_Value *root_value = json_parse_string(json);
  JSON_Object *root = json_value_get_object(root_value);
  JSON_Array *entries = json_object_dotget_array(root, "feed.entry");
  for (int i = 0; i < json_array_get_count(entries); i++) {
    JSON_Object *entry = json_array_get_object(entries, i);
    cc_fprintf(CC_FG_BLUE, stdout, ”%s\n”,
      json_object_dotget_string(entry, "title.$t"));
    JSON_Array *links = json_object_get_array(entry, "link");
    for (int l = 0; l < json_array_get_count(links); l++) {
      JSON_Object *link = json_array_get_object(links, l);
      if (!strcmp("alternate", json_object_get_string(link, "rel"))) {
        cc_fprintf(CC_FG_YELLOW, stdout, ”%s\n”,
          json_object_dotget_string(link, "href"));
        break;
      }
    }
  }
  json_value_free(root_value);

leave:
  if (res) http_get_free(res);
  if (json) free(json);
  return 0;
}

ちょっと適当なのでバグあるかもしれませんがご愛嬌で。
実行結果は以下の通り。

本の虫

簡単すぎる!!!
僕の中ではちょっとしたライフチェンジングですね。
このスピード感なら色んな物が作れそうな気がしますね。実際このコードも clib install を含めて10数分程度で書けました。
皆さんも一度、このスピード感を味わってみて下さい。


from Big Sky http://ift.tt/1mlyUuG
6月 25 '14

golang の sync パッケージの使い方

golang と言えば非同期に特化した言語ですが、慣れない内は簡単な非同期しか使えません。しかし sync パッケージを知る事でもっとカジュアルに、かつ確実な非同期処理を行う事が出来る様になります。 今日はそんな sync パッケージについて説明してみたいと思います。

sync.Mutex ご存じ sync.Mutex です。皆さんが一番使う排他制御だと思います。

package main

import (
    "fmt"
    "runtime"
    "sync"
    "time"
)

func parallel(wg *sync.WaitGroup) {
    fmt.Println("博")
    time.Sleep(100 * time.Millisecond)
    fmt.Println("多")
    time.Sleep(100 * time.Millisecond)
    fmt.Println("の")
    time.Sleep(100 * time.Millisecond)
    fmt.Println("塩")
    wg.Done()
}

func main() {
    runtime.GOMAXPROCS(runtime.NumCPU())

    wg := new(sync.WaitGroup)
    for i := 0; i < 3; i++ {
        wg.Add(1)
        go parallel(wg)
    }
    wg.Wait()
}

このコードを実行すると結果はおおよそ以下の様になります。













時には順番が入り乱れる事もあるでしょう。これに sync.Mutex を追加して以下の様にします。

package main

import (
    "fmt"
    "runtime"
    "sync"
    "time"
)

func parallel(wg *sync.WaitGroup, m *sync.Mutex) {
    m.Lock()
    defer m.Unlock()

    fmt.Println("博")
    time.Sleep(100 * time.Millisecond)
    fmt.Println("多")
    time.Sleep(100 * time.Millisecond)
    fmt.Println("の")
    time.Sleep(100 * time.Millisecond)
    fmt.Println("塩")
    wg.Done()
}

func main() {
    runtime.GOMAXPROCS(runtime.NumCPU())

    wg := new(sync.WaitGroup)
    m := new(sync.Mutex)
    for i := 0; i < 3; i++ {
        wg.Add(1)
        go parallel(wg, m)
    }
    wg.Wait()
}

すると













期待通りの順番に表示されます。上記のコードでは表示の排他のみを扱いましたが、更新処理と参照処理により排他制御を区別出来る RWMutex もあります。
また、上記のコードでしれーっと書いていますが sync.WaitGroup は Wait() を呼び出すと Add() を呼び出した回数から Done() を呼び出した回数を引いて 0 になるまで待機する機能が簡単に実装出来ます。全ての goroutine の終了を待つ場合に使用します。

sync/atomic sync/atomic パッケージは、名前の通りアトミックな演算を提供します。

package main

import (
    "fmt"
    "runtime"
    "sync"
)

var v int32

func main() {
    runtime.GOMAXPROCS(runtime.NumCPU())
    wg := new(sync.WaitGroup)
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func() {
            v++
            v++
            v++
            v++
            v++
            v++
            v++
            v++
            v++
            v++
            wg.Done()
        }()
    }
    wg.Wait()
    fmt.Println(v)
}

このコードを実行すると、10回の加算を10並行で行うため合計で100になる事が期待出来ますが実際には何回かに1回100でない事象が発生します。
演算がアトミックでないからです。こういう場合に sync/atomic を使います。

package main

import (
    "fmt"
    "runtime"
    "sync"
    "sync/atomic"
)

var v int32

func main() {
    runtime.GOMAXPROCS(runtime.NumCPU())
    wg := new(sync.WaitGroup)
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func() {
            atomic.AddInt32(&v, 1)
            atomic.AddInt32(&v, 1)
            atomic.AddInt32(&v, 1)
            atomic.AddInt32(&v, 1)
            atomic.AddInt32(&v, 1)
            atomic.AddInt32(&v, 1)
            atomic.AddInt32(&v, 1)
            atomic.AddInt32(&v, 1)
            atomic.AddInt32(&v, 1)
            atomic.AddInt32(&v, 1)
            wg.Done()
        }()
    }
    wg.Wait()
    fmt.Println(v)
}

このコードは何回実行しても合計が100になります。

sync.Once ある関数ではある処理を1回のみ行いたい、そういった場合に使用します。

package main

import (
    "fmt"
    "runtime"
    "sync"
)

var once = new(sync.Once)

func greeting(wg *sync.WaitGroup) {
    once.Do(func() {
        fmt.Println("こんにちわ")
    })

    fmt.Println("ごきげんいかがですか")
    wg.Done()
}

func main() {
    runtime.GOMAXPROCS(runtime.NumCPU())

    defer fmt.Println("さようなら")

    wg := new(sync.WaitGroup)
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go greeting(wg)
    }
    wg.Wait()
}

このコードの実行結果は以下の様になります。

こんにちわ
ごきげんいかがですか
ごきげんいかがですか
ごきげんいかがですか
ごきげんいかがですか
ごきげんいかがですか
さようなら

並列で走った場合でも1度しか実行されません。

sync.Cond 例えば goroutine を先行で10個用意しておき、ある号令に従い1つずつ並行処理を開始したいとします。こういうケースでは sync.Cond を作り Signal() を1回ずつ呼び出します。

package main

import (
    "fmt"
    "runtime"
    "sync"
    "time"
)

func main() {
    runtime.GOMAXPROCS(runtime.NumCPU())

    l := new(sync.Mutex)
    c := sync.NewCond(l)
    for i := 0; i < 10; i++ {
        go func(i int) {
            fmt.Printf("waiting %d\n”, i)
            l.Lock()
            defer l.Unlock()
            c.Wait()
            fmt.Printf("go %d\n”, i)
        }(i)
    }

    for i := 0; i < 10; i++ {
        time.Sleep(1 * time.Second)
        c.Signal()
    }
    time.Sleep(1 * time.Second)
}

このコードを実行すると以下の様な出力になります。

waiting 0
waiting 9
waiting 5
waiting 1
waiting 2
waiting 3
waiting 4
waiting 8
waiting 6
waiting 7
go 0
go 9
go 5
go 1
go 2
go 3
go 4
go 8
go 6
go 7

また全 goroutine に対して一斉に号令を掛ける場合は Broadcast() を使います。

package main

import (
    "fmt"
    "runtime"
    "sync"
    "time"
)

func main() {
    runtime.GOMAXPROCS(runtime.NumCPU())

    l := new(sync.Mutex)
    c := sync.NewCond(l)
    for i := 0; i < 10; i++ {
        go func(i int) {
            fmt.Printf("waiting %d\n”, i)
            l.Lock()
            defer l.Unlock()
            c.Wait()
            fmt.Printf("go %d\n”, i)
        }(i)
    }

    for i := 3; i >= 0; i— {
        time.Sleep(1 * time.Second)
        fmt.Println(i)
    }
    c.Broadcast()
    time.Sleep(3 * time.Second)
}

このコードを実行すると以下の様な出力になります。

waiting 0
waiting 1
waiting 4
waiting 7
waiting 8
waiting 9
waiting 5
waiting 2
waiting 3
waiting 6
3
2
1
0
go 0
go 9
go 1
go 4
go 7
go 8
go 3
go 5
go 2
go 6


カウントダウン後、「go」の部分が一気に出力されます。

sync.Pool golang 1.3 から追加された機能です。
例えば、逐次行う作業と非同期に割込みで入ってくる作業を上手く処理したいとします。

package main

import (
    "fmt"
    "runtime"
    "sync"
    "time"
)

func main() {
    runtime.GOMAXPROCS(runtime.NumCPU())
    p := sync.Pool{
        New: func() interface{} {
            return "定時作業"
        },
    }

    wg := new(sync.WaitGroup)

    wg.Add(1)
    go func() {
        for i := 0; i < 10; i++ {
            p.Put("割込作業")
            time.Sleep(100 * time.Millisecond)
        }
        wg.Done()
    }()

    wg.Add(1)
    go func() {
        for i := 0; i < 10; i++ {
            fmt.Println(p.Get())
            time.Sleep(50 * time.Millisecond)
        }
        wg.Done()
    }()

    wg.Wait()
}

このコードでは、10個の「定時作業」(Getを行う途中に「割込作業」(Put)が割り込みます。作業を行うのは10個限りとします。

定時作業
定時作業
割込作業
定時作業
割込作業
割込作業
定時作業
定時作業
割込作業
定時作業

作業を割り込む側、作業を処理する側が並列で動いていても上手く処理出来ています。

この様に、golang の sync パッケージにはにとても便利な機能が揃っています。ぜひもっとカジュアルに非同期機能を使ってみて下さい。
きっと面白い物が出来上がると思います。


from Big Sky http://ift.tt/1mp9TmU
6月 25 '14

from Big Sky http://ift.tt/1mp3DvE

6月 19 '14

IIS で golang やろう

golang で Web と言えば、net/http でハンドラ書いて http.ListenAndServe を呼び出すサーバ方式が思い浮かびますが、他にも選択はあるはずです。

mattn/go-cgi - GitHub


http://ift.tt/1qfmQ39

golang で CGI が書けます。

まず上記リポジトリを clone して go-cgi.exe を作ります。

git clone http://ift.tt/1qfmQ39
cd go-cgi
go build

次に「管理ツール」から「インターネットインフォメーションサービス」を起動し、サイトに仮想ディレクトリを足します。

IIS

「ハンドラーマッピング」を選び、一覧上を右クリックして「スクリプトマップの追加」を選択します。

スクリプトマップ

「要求パス」は *.go、「実行可能ファイル」に先程ビルドした go-cgi.exe へのパスを、「名前」に CGI-go を入力します。

要求の制限

「要求の制限」をクリックして、「要求のマップ先が次の場合のみハンドラーを呼び出す」にチェックを入れ、「ファイル」を選び、あとは「OK」をクリックします。

最後に画面左のツリーの最上部にあるPC名をクリックして「ISAPIおよびCGIの制限」を選び、一覧に表示される CGI-go を右クリックして「許可」に変更します。

ISAPIおよびCGIの制限


これで IIS 上で拡張子 go のファイルが CGI として実行出来る準備が整いました。
Apache/nginx で動作させる場合は、これから作る CGI の拡張子を cgi にしてファイルの先頭行に #!/usr/local/bin/go-cgi といった感じに shebang を書くと動きます。
なお、go コマンドへパスを通していない場合は、これから説明する CGI を置くディレクトリに .go-cgi というフォルダを作り、そこに env というファイルを作成します。そこに GOROOT=c:/go といった感じに GOROOT を教えてあげるおまじないを書くと動く様になると思います。

さて CGI を書きましょう。上記で仮想ディレクトリをマッピングしたディレクトリに移動し、例として foo.go というファイルを以下の様に作ります。

package main

import (
    "fmt"
)

func main() {
    fmt.Print("Content-Type: text/html;\r\n\r\n”)
    fmt.Println("Hello World")
}

ブラウザから http://localhost/mattn/foo.go を開くと Hello World が表示されるかと思います。
go-cgi は /tmp/go-cgi(Windows だと %TEMP%\go-cgi)、もしくは CGI ファイルと同じディレクトリに .go-cgi というディレクトリを作り、そこにハッシュ値で管理された go ファイルを生成すると同時にコンパイルして実行しています。
なお、golang の net/http は CGI を書く場合でも http.Handler を使う事が出来ます。

#! go-cgi
package main

import (
    "fmt"
    "net/http"
    "net/http/cgi"
)

func main() {
    cgi.Serve(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "text/plain; charset=utf-8")
        fmt.Fprintf(w, "Hello %s", r.FormValue("name"))
    }))
}

簡単ですね。Windows の皆さんもぜひ golang で Web やりましょう。


from Big Sky http://ift.tt/UNOOsc
6月 18 '14

GitHub で一番 star を貰ったリポジトリを調べる方法

GitHub で一番お星さまを貰ったリポジトリを調べてみた。

stars:>1 user:mattn

ユーザ名の所はご自分のIDをお使い下さい。ちなみに僕のリポジトリのトップ10は…

こんな感じでした。vim 物が多いですね。star ありがとうございました。

from Big Sky http://ift.tt/ULnN8X