名前そのままやん感がすごいですが。
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, NULL, 0, NULL)) {
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 が入り組んだコードになるかと思います。