Quantcast
Channel: Big Sky
Viewing all 121 articles
Browse latest View live

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

$
0
0
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 パッケージにはにとても便利な機能が揃っています。ぜひもっとカジュアルに非同期機能を使ってみて下さい。
きっと面白い物が出来上がると思います。

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

$
0
0
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.

https://github.com/clibs/clib
言うなれば、C言語版の bundler であったり、carton であったり、npm だったりする訳です。

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

package.json を用意する

上記の様な仕様を満たすライブラリは幾つかあります。また github 上には clib から扱えるライブラリが多く存在ます。そしてそのリポジトリには package.json というファイルが含まれています。
今回の仕様を満たす為に、僕は以下の package.json を作りました。
{
  "name""helloworld",
  "version""1.0.0",
  "repo""mattn/helloworld",
  "dependencies": {
    "stephenmathieson/http-get.c""0.1.0",
    "kgabis/parson""*",
    "Constellation/console-colors.c""1.0.1"
  },
  "install""make install"
}
追記: parson-repo が無くなってたので修正

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

clib install

clib install
deps フォルダ配下にパッケージがインストールされました。
├── 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
└── package.json
簡単すぎる!!!まぁなんと便利なんでしょう!
ちなみにこの 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://cpplover.blogspot.com/feeds/posts/default?alt=json");
  if (res->status != 200) {
    cc_fprintf(CC_FG_RED, stderr"ERROR");
    goto leave;
  }

  char* json = calloc(res->size + 11);
  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数分程度で書けました。
皆さんも一度、このスピード感を味わってみて下さい。

MSVC の怖い話

$
0
0
夏だし、怖い話しようぜ!
#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: https://gist.github.com/mattn/bf1e56aa832c031e5df6 illustrates the problem. MSVC somehow succe...

https://github.com/kazuho/picojson/pull/43
NAN が宣言されていない為に、NAN という変数が宣言されてしまっていた。
NAN なんだ、まったく。
酷い一日だった。
なんて日だ!

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

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

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

http://mattn.kaoriya.net/software/lang/c/20140627222830.htm

locale-string.c

最近のライブラリは文字列が utf-8 前提で書かれている物が多く、Windows だと「それ、ツライわー(汗)」だったりするのですが、かと言ってこれだけの為に変換ライブラリを入れるのはアレですし、iconv 入れちゃったら GPL(LGPL) だし仕事で使えねー、みたいな事が起きてしまいます。
でもロケール文字列と utf-8 との変換なら mbstowcs や wcstombs があれば出来るし、Windows なら MultiByteToWideChar や WideCharToMultiByte があれば出来るので、それだけのライブラリを書きました。
mattn/locale-string.c - GitHub
https://github.com/mattn/locale-string.c
使い方は簡単。
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 以外ならばメモリ確保も解放も行いませんので、UTF8_ALLOC して使い終わったら UTF8_FREE しておくと UNIX でも Windows でも同じ様に動作する、という仕組みです。

wcwidth.c

皆さんしってる wcwidth です。
wcwidth, wcswidth, wcwidth_cjk, wcswidth_cjk がそろっていますが、オマケで utf-8 文字列の幅を求める string_width と string_width_cjk も付いてきます。
mattn/wcwidth.c - GitHub
https://github.com/mattn/wcwidth.c
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
https://github.com/mattn/ansicolor-w32.c
そこで 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 ライセンスです。

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

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

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

http://d.hatena.ne.jp/sugyan/20140705/1404493007
ちょっと動きは違うんだけど、こういうパターンもあるよねー。という事で。
package main

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

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

    queue := make(chan rune5)

    t := time.AfterFunc(-1func() {
        // ある分だけ読み込む
        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://qiita.com/mattn/items/edea1be5a6d84663ab8b
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://blog.gopheracademy.com/day-19-eject-the-web

Vim でたんざくを作ろう

$
0
0
:echo webapi#json#decode(webapi#http#get("http://tanzak.herokuapp.com/api", {"q""カッコイイVimmerになれますように"}).content).c
┏┷┓
┃ ┃
┃カ┃
┃ッ┃
┃コ┃
┃イ┃
┃イ┃
┃V┃
┃i┃
┃m┃
┃m┃
┃e┃
┃r┃
┃に┃
┃な┃
┃れ┃
┃ま┃
┃す┃
┃よ┃
┃う┃
┃に┃
┃ ┃
┗━┛

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

$
0
0
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...

https://gist.github.com/piscisaureus/3342247
$ 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 を叩く事にする。

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

$
0
0

掟1

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

掟2

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

掟3

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

掟4

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

掟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

プログラミング作法プログラミング作法
ブライアン カーニハン
アスキー / ¥ 3,024 (2000-11)
 
発送可能時間:在庫あり。


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

$
0
0
って事で書いた。
mattn/ctrlp-veco ・ GitHub
https://github.com/mattn/ctrlp-veco
実行には CtrlP が必要。

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

veco

Vim で flappyvird 書いた。

$
0
0
前回、「幅跳び」が好評だったので、あの有名なゲーム「Flappy〇ird」を Vim で出来る flappyvird-vim を書いてみました。
mattn/flappyvird-vim ・ GitHub
https://github.com/mattn/flappyvird-vim
flappyvird
画像は開発中の物で、実際のキャラクタは異なります
良かったら遊んで下さい。
:FlappyVird
でゲームスタート、スペースキーでジャンプ、pで一時停止、qもしくは ESCでゲーム終了です。

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

$
0
0

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

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

Big Sky :: Vimテクニックバイブル ~作業効率をカイゼンする150の技
http://mattn.kaoriya.net/software/vim/20110810203558.htm

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

Big Sky :: github上にvim-users-jpというorganizationを作った。
http://mattn.kaoriya.net/software/vim/20110907120904.htm

そして数日後、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)
 
発送可能時間:近日発売 予約可


発売日は8/6です。

実は今回、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 でもパッチ屋と呼ばれている部類の人です。

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


Vim scriptテクニックバイブル

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

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

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

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

的な存在となります。


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

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

$
0
0

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

ipkn/crow - GitHub
https://github.com/ipkn/crow

ルーティングの書き方が 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() != 2continue;
    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という文字列から https://github.com/mattnという返答をさせてアンカーを作る等の応用例もあります。(参考資料)

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

golang で N 個の chan を同時に待つ

$
0
0

reflect.Selectを使います。

package main

import (
    "fmt"
    "math/rand"
    "reflect"
    "sync"
    "time"
)

func multpleSelect(chans []chan bool) (intboolbool) {
    // chans の数分 select-case 文を作る
    cases := make([]reflect.SelectCase, len(chans))
    for i, ch := range chans {
        cases[i] = reflect.SelectCase{
            Dir: reflect.SelectRecv,
            Chan: reflect.ValueOf(ch),
        }
    }

    // 一括で select
    i, v, ok := reflect.Select(cases)
    return i, v.Interface().(bool), ok
}

func main() {
    rand.Seed(time.Now().UnixNano())

    // 100個の chan を作るよ
    chans := make([]chan bool100)
    for i := 0; i < 100; i++ {
        chans[i] = make(chan bool)
    }


    // 非同期で100個の chan を同時に待つ
    var wg sync.WaitGroup
    go func() {
        wg.Add(1)

        if ch, v, ok := multpleSelect(chans); ok {
            fmt.Printf("I am chan-%v, value is %v\n", ch, v)
        }
        wg.Done()
    }()

    // ランダムな chan に true を送る
    chans[rand.Int() % len(chans)] <- true

    wg.Wait()
}

何度も繰り返して待つ場合は、その都度 SelectCase を作るのがコストになり得るので、予め作ったまま保持しておくのが良いかと思います。

Windows でも色付きでログが出せる golang のライブラリ「go-colorable」書いた。

$
0
0

golang には logger が星の数ほどあるのですが

go言語におけるロギングについて — さにあらず

コマンドラインオプションについて 分かり辛いので、僕の分かった事だけをメモしておきます。 ログの出力処理は glog.go#output を読めば大体分かります。 -logtostderr これを指定...

http://blog.satotaichi.info/logging-frameworks-for-go

その殆どがエスケープシーケンスを使って色を出しており、Windows で動かすと残念な表示になる物ばかりでした。

まさかログに色を付けたいという理由だけで ansiconを使うのは悲し過ぎるし、そもそも「ansicon 使ったら負けだと思ってる」ので、go-colorable というライブラリを書きました。

golang の logger はその殆どが標準パッケージの log を参考にしており、おおよそ SetOutputという、出力先を変えられる関数が用意されています。そこで、それを横取りして Windows でも色を出せる様にしました。

mattn/go-colorable - GitHub
https://github.com/mattn/go-colorable

これを使うと今まで

bad

こんな表示だったのが

good

この様にカラフルな表示になります。使い方はとても簡単で

package main

import (
    "github.com/mattn/go-colorable"
    "github.com/Sirupsen/logrus"
)

func main() {
    logrus.SetOutput(colorable.NewColorableStdout())

    logrus.Info("succeeded")
    logrus.Warn("not correct")
    logrus.Error("something error")
    logrus.Fatal("panic")
}

この様に NewColorableStdoutを呼び出して io.Writerを取得し、それを logger 等に渡すだけです。Windows 以外の場合は os.Stdoutを返す様になっているのでいちいち Windows かどうか判定する必要もありません。

UNIX で色を標準出力するライブラリ書いたけど、Windows... 知らねぇよ!って人は、ぜひこのライブラリを使ってみて下さい。きっと pull-request を貰うまでもなく、Windows 対応が完了している事になるはずです。

C++ の JSON ライブラリ json11 は initializer_list が綺麗。

$
0
0

おなじみC/C++から使えるJSONライブラリを紹介するコーナー。まずは過去のまとめ。

今回は json11 というライブラリ。

dropbox/json11 - GitHub
https://github.com/dropbox/json11

あの Dropboxが書いてる。C++ から使える JSON ライブラリは幾つかありますが、initializer_list が使える物がなかなか少ない。

json11 は以下の様に書ける。

Json my_json = Json::object {
    { "key1""value1" },
    { "key2"false },
    { "key3", Json::array { 123 } },
};

前回の jansson の記事で使ったサンプルを json11 で書くと以下の様になります。

※ネットワーク通信部分は前回のコードよりも省略しています

C++11 向けライブラリですのでコンパイル時のコマンドラインオプションに -std=c++11を付ける必要があります。

#include <iostream>
#include "json11.hpp"
#include <curl/curl.h>

size_t
stream_write(char* ptr, size_t size, size_t nmemb, void* stream) {
  ((std::string*) stream)->append(std::string(ptr, size * nmemb));
  return size * nmemb;
}

int
main() {
  CURL* curl;
  std::string buf;
  curl = curl_easy_init();
  curl_easy_setopt(curl, CURLOPT_URL, "https://api.github.com/legacy/repos/search/unko");
  curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
  curl_easy_setopt(curl, CURLOPT_USERAGENT, "curl");
  curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buf);
  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, stream_write);
  curl_easy_perform(curl);
  curl_easy_cleanup(curl);

  std::string err;
  json11::Json v = json11::Json::parse(buf, err);
  for (auto &k : v["repositories"].array_items()) {
    std::cout << k["username"].string_value() << std::endl;
    std::cout << k["name"].string_value() << std::endl;
    std::cout << k["description"].string_value() << std::endl << std::endl;
  }
}

とてもきれい。ライセンスは MIT なので業務でも使えます。


なんでもシェルで書いちゃう男の人って...。

$
0
0
ShellScript - jq、xmllintコマンドさようなら。俺はパイプが好きだから - Qiita

UNIX哲学の一つとしてよく引用されるマイク・ガンカーズの教義に

  • 1.小さいものは美しい。
  • 2.1つのプログラムには1つのことをうまくやらせよ。

というのがあるが、まずこれができていない

http://qiita.com/richmikan@github/items/e051b5d882c3dd2a39c6

昔の UNIX で扱っていたデータはだいたい行指向でした。そして UNIX は行指向データを扱う為の OS と言っても過言ではありませんでした。 しかし JSON はどうでしょう。JSON は行指向ではありません。ならばツールを変えるのが正しい選択だと思いますし、そこでシェルを選ぶのは正直マニアの方しかいませんよね。 入力データがバイナリであれば、ツールも変えるはずです。JSON もそうすべきだと僕は思います。

理由1. 一つのことをうまくやっていない

ShellScript - jq、xmllintコマンドさようなら。俺はパイプが好きだから - Qiita

JSON だけを扱っていますすし、他のデータを扱ってはいないですよね。

理由2. フィルターとして振る舞うようになりきれてない

ShellScript - jq、xmllintコマンドさようなら。俺はパイプが好きだから - Qiita

jq は JSON の grep 的な存在だと思っています。僕は jq を使いこなしている訳ではないし、「便利だなー」くらいにしか思っていませんが、JSON をシェルで扱いたいと思った場合にはJSON が苦無く扱える言語やツールを選びます

ちなみに jq だと上記の問題はどの様に解決出来るか試してみました。

#!/bin/bash

FILE=$1
cat $FILE | jq -r 'paths|map(if type=="number" then "["+tostring+"]" else "["+tojson+"]" end)|join("")' |\
while read line; do
    value=`cat $FILE | jq -r ".$line|if type==\"string\" or type==\"number\" then \"MATCH:\"+tostring else empty end"`
    if [ "x$value" != "x" ]then
        echo $value sed "s/^MATCH:/.$line\t/"
    fi
done

実行結果は以下の通り。

.["会員名"] 文具 太郎
.["購入品"][0]  はさみ
.["購入品"][1]  ノート(A4,無地)
.["購入品"][2]  シャープペンシル
.["購入品"][3]["取寄商品"]  替え芯
.["購入品"][4]  クリアファイル
.["購入品"][5]["取寄商品"]  6穴パンチ

パスは jq のパス式です。短くていいですね。

ネタだったら、マジレスごめんなさい

「便利」と「感極まってきました」だけでプログラムが書ける日本語プログラミング言語「うじひさ」を作った。

$
0
0

誰も作ってくれないので自分で作った。

mattn/ujihisa - GitHub
https://github.com/mattn/ujihisa

golang で書かれています。go get github.com/mattn/ujihisa/ujmでインストール出来ます。使い方は

$ ujm [filename]

です。ファイル名が無い場合は標準入力から読み取ります。ベースは whitespace です。スペース文字が 便利、タブ文字が 感極まってきましたに置き換わります。whitespace に慣れている人ならスイスイと書けるかと思います。例えば

便利便利便利感極まってきました
感極まってきました
便利感極まってきました



この様なコードなら 1という数字が出力されます。

なお、便利という文字を出力するにはこれくらいのコードが必要になります。golang で書かれているので Windows でも動きますし、文字の表示でユニコードを使うと Windows でも問題なく表示されます。

とても便利だと思うので、ぜひ使ってみて下さい。

C言語の怖い話

$
0
0

追記: ほんとは怖くないよ!って話を追記してます。

夏だし怖い話しようぜ!

訳あって一部だけ C++ のコードです。

#include <stdio.h>
#include <iostream>

int
main() {
  char str[256];

  double a = 123.45;
  int b = 57;
  int c = 89;

  sprintf(str, "a=%f b=%d c=%d", a, b, c);
  std::cout << str << std::endl;

  sprintf(str, "a=%d b=%f c=%d", a, b, c);
  std::cout << str << std::endl;

  std::cout << "a=" << a << std::endl;
  std::cout << "b=" << b << std::endl;
  std::cout << "c=" << c << std::endl;

  return 0;
}

コンパイル時に警告は出るだろうけど、ちゃんと動くよね。

$ g++  -Wformat-nonliteral aaaa.cxx && ./a.out
aaaa.cxx: In function ‘int main()’:
aaaa.cxx:16:43: warning: format ‘%d’ expects argument of type ‘int’, but argument 3 has type ‘double’ [-Wformat]
aaaa.cxx:16:43: warning: format ‘%f’ expects argument of type ‘double’, but argument 4 has type ‘int’ [-Wformat]
a=123.450000 b=57 c=89
a=57 b=123.450000 c=89
a=123.45
b=57
c=89

きぇーーーーーーーーーーーーーーー!

C++ でなく C++ の場合だけ発生します。(Cでも発生しました)

ちなみに 32bit Windows だと gcc4.8, gcc4.9, clang3.4.2, VS2010, VS2012, VS2013 では

123.450000 57 89
-858993459 0.000000 89

こう出力されました。64bit Linux の gcc4.6.3, gcc4.9.1 だと上記の結果が出力されました。wandbox でもこんな結果になります。

だれか理由を教えて下さい...

追記

Cの呼び出し規約の varargs で、期待する型と与えられた型のサイズ違い(sizeof(double)-sizeof(int))で発生する様です。(参考)

さらに勘違い。64bit OS の場合は int と double で格納されるレジスタが違うらしく(XMMレジスタ)、その取り出し順序が期待と異なるのが原因らしいです。(yohhoyさんありがとうございます)

さらに 32bit Windows の場合は単に順番にstackに積むだけなのでa[31:0]をintと、b:a[63:32]をdoubleと見做して解釈する事で発生するらしいです。(kikairoyaさんありがとうございます)

#include <stdio.h>

int
main() {
  double a = 123.45;
  int b = 57;
  int c = 89;
  printf("%d %f %d\n", a, b, c);
}

このコードを Windows 32bit OS で -S出力してみました。

    .file   "aaaa.c"
    .def    ___main;    .scl    2;  .type   32; .endef
    .section .rdata,"dr"
LC1:
    .ascii "%d %f %d\12\0"
    .text
    .globl  _main
    .def    _main;  .scl    2;  .type   32; .endef
_main:
    pushl   %ebp
    movl    %esp, %ebp
    andl    $-16, %esp
    subl    $48, %esp
    call    ___main
    fldl    LC0
    fstpl   40(%esp)
    movl    $5736(%esp)
    movl    $8932(%esp)
    movl    32(%esp), %eax
    movl    %eax16(%esp)
    movl    36(%esp), %eax
    movl    %eax12(%esp)
    fldl    40(%esp)
    fstpl   4(%esp)
    movl    $LC1, (%esp)
    call    _printf
    leave
    ret
    .section .rdata,"dr"
    .align 8
LC0:
    .long   -858993459
    .long   1079958732
    .ident  "GCC: (i686-posix-sjljbuilt by strawberryperl.com project4.8.2"
    .def    _printf;    .scl    2;  .type   32; .endef

esp レジスタに 57 と 89 の順で格納され、処理順は異なりますが最後にはコードと同じ順に esp レジスタに格納されています。かたや Linux 64bit OS だと

  .file "aaaa.c"
  .section  .rodata
.LC1:
  .string "%d %f %d\n"
  .text
  .globl  main
  .type main, @function
main:
.LFB0:
  .cfi_startproc
  pushq %rbp
  .cfi_def_cfa_offset 16
  .cfi_offset 6, -16
  movq  %rsp, %rbp
  .cfi_def_cfa_register 6
  subq  $32, %rsp
  movabsq $4638387438405602509, %rax
  movq  %rax, -8(%rbp)
  movl  $57, -12(%rbp)
  movl  $89, -16(%rbp)
  movl  -16(%rbp), %edx
  movl  -12(%rbp), %ecx
  movq  -8(%rbp), %rax
  movl  %ecx, %esi
  movq  %rax, -24(%rbp)
  movsd -24(%rbp), %xmm0
  movl  $.LC1, %edi
  movl  $1, %eax
  call  printf
  leave
  .cfi_def_cfa 78
  ret
  .cfi_endproc
.LFE0:
  .size main, .-main
  .ident  "GCC: (GNU4.9.1"
  .section  .note.GNU-stack,"",@progbits

XMM レジスタが使われている様です。printf がフォーマット %f%dを見て取り出すべきレジスタに異なる値が入っているのだから起きて当たり前という訳でした。

kill で立つハッカー、シェルを濁す

$
0
0
shell - 立つハッカー、シェルを濁さず - Qiita

おことわり このTipsは不作法だとして異論が出るかもしれないが、私自身がよくやるのでここで公開してみる。それは一体何かというと…… コマンドヒストリーが残るのイヤだ! と思うことってよくないだろうか...

http://qiita.com/richmikan@github/items/2c90ddb778a7d4948324

man bashによると

HISTFILE
       The name of the file in which command history is saved (see HIS‐
       TORY below).  The default value is ~/.bash_history.   If  unset,
       the  command  history  is  not  saved  when an interactive shell
       exits.

HISTFILE を unset (HISTFILE=)したらヒストリは保存されないよと書いてある。

shell芸、嫌いではないけど、出来ればそういうのを tips として広めないで欲しい。kill -9で殺してしまったら trap で後処理としてテンポラリファイルを消してる処理が走らなくなってしまうのではないですか?それって濁さないとは言わないのでは。

追記

C言語から golang の chan っぽい事が出来るライブラリ「chan」

$
0
0

名前そのままやん感がすごいですが。

tylertreat/chan - GitHub
https://github.com/tylertreat/chan

golang の chan をC言語から使える様にするライブラリです。やはりC言語というだけあって、受け渡す値の型は void*ですがそこは目をつむりましょう。

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "./src/chan.h"

#ifndef _WIN32
#include <unistd.h>
#include <termios.h>

char
getch() {
  char c = 0;
  struct termios old = {0};
  fflush(stdout);
  if (tcgetattr(0, &old) < 0)
    perror("tcsetattr()");
  old.c_lflag &= ~ICANON;
  old.c_lflag &= ~ECHO;
  old.c_cc[VMIN] = 1;
  old.c_cc[VTIME] = 0;
  if (tcsetattr(0, TCSANOW, &old) < 0)
    perror("tcsetattr ICANON");
  if (read(0, &c, 1) < 0)
    perror("read()");
  old.c_lflag |= ICANON;
  old.c_lflag |= ECHO;
  if (tcsetattr(0, TCSADRAIN, &old) < 0)
    perror ("tcsetattr ~ICANON");
  return c;
}
#endif

void*
capture(void *p) {
  chan_t* r = (chan_t*) p;
  while (!chan_is_closed(r)) {
    char c = getch();
    if (c == 'q'break;
    chan_send(r, (void*) &c);
  }
  chan_close(r);
  return NULL;
}

void*
fizzbuzz(void *p) {
  chan_t* r = (chan_t*) p;
  int n;
  for (n = 1; n <= 100 && !chan_is_closed(r); n++) {
    char buf[20] = {0};
    if (n % 3 == 0) strcat(buf, "Fizz");
    if (n % 5 == 0) strcat(buf, "Buzz");
    if (buf[0] == 0) sprintf(buf, "%d", n);
    chan_send(r, (void*) buf);
    sleep(1);
  }
  chan_close(r);
  return NULL;
}


int
main(int argc, char* argv[]) {
  chan_t* chans[2] = {chan_init(0), chan_init(0)};
  pthread_t th[2];

  if (pthread_create(&th[0], NULL, fizzbuzz, chans[0]) !=0) {
    perror("pthread_create");
    exit(1);
  }

  if (pthread_create(&th[1], NULL, capture, chans[1]) !=0) {
    perror("pthread_create");
    exit(1);
  }

  while (!chan_is_closed(chans[0]) && !chan_is_closed(chans[1])) {
    void* v = NULL;
    switch(chan_select(chans, 2, &v, NULL0NULL)) {
    case 0:
      printf("FizzBuzz: %s\n", (char*) v);
      break;
    case 1:
      printf("KeyTyped: %c\n", *(char*) v);
      break;
    default:
      break;
    }
  }
  chan_close(chans[0]);
  chan_close(chans[1]);
  pthread_join(th[0], NULL);
  pthread_join(th[1], NULL);
  return 0;
}

1秒毎に更新される FizzBuzz を表示しながら、キーボードで入力された文字を表示します。FizzBuzz が100を超えるか、qをタイプすると終了します。FizzBuzz もキーボード入力もどちらもスレッドで実行しており非同期に処理されます。golang の様に goと書くだけで非同期処理が実行される訳ではないので pthread_create なんて長ったらしい名前を書く必要があります。C++ なら std::sync あたりを使えば簡単に書けそうな気もしますね。

pthread まわりをもうちょっと隠蔽すれば、まぁまぁかっこいい非同期処理が書ける様になるんじゃないかと思います。ただし golang の様に OS のスレッドとコルーチンをうまく切り替えたりはしないのでその辺は頑張る他無いです。

メッセージングを一から作るとなると結構大変なのでそこそこ使えるライブラリだと思います。

ただし void*で値を受け渡すということは、ヒープがそのまま渡るので、chan のバッファを1以上で動かす際には都度確保したメモリを渡さないと上書きされてしまいます。上記の例では説明の手間を省くために簡略化していますが、実際はもっと malloc/free が入り組んだコードになるかと思います。

Viewing all 121 articles
Browse latest View live