2017年2月
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28        

Amazonウィジェット

  • miniPC
  • 最近買った本
  • Raspberry Pi
  • クアッドコプター
  • 書籍ランキング

AdSense

  • 広告
無料ブログはココログ

C言語

2016年11月27日 (日)

C言語 CSVを読み込む関数をsscanfとstrchrで作ってみた。

CSVを読み取る機会は多いものの、C言語の入門本やWebにはなかなかそういうのが書いていなくて、学習の最初のほうは難儀しました。たしか最初に作ったアプリは単語帳ソフトで、CSVで保存しようとしていたのですが、どうにも動作が安定せず挫折したような記憶があります。最初から文字列処理が得意なperlないしはpythonでも使っていればもう少し簡単だったのかもしれませんが。

今までにもC言語で色々な方法でCSVを読み取る関数を作ってきましたが、今回はsscanfを使って作ってみました。


とりあえずべた書きで処理を書いてみた。

#include 

char str[] = "1,2,3,4,5,6,7,8";

void main(void){
       char *adr;
       adr = str;
       for (int i = 0; i < 10; i++){
              int res;
              int col;

              res = sscanf(adr, "%d", &col);
              if (res < 1 || res == EOF){
                     continue;
              }
              printf("%d\n", col);
              adr = strchr(adr, (int)',');
              if (adr == NULL){
                     continue;
              }
              else{
                     adr += 1;
              }
       }
}


関数にまとめてみた。

#include 

char str[] = "1,2,3,4,5,6,7,8";


//戻り値:読み取りに成功=0,取りこぼし=-1
//引数:読み取り対象文字列、読み取り値を入れる配列、読み取り要素数
int csvIntRead(const char *str, int *col, const int num){
	char *adr;

	int succNum = 0; //読み取り成功カウンタ

	//開始位置をセット
	adr = str;

	for (int i = 0; i < num; i++){
		int res;

		//数字を読み取る
		res = sscanf(adr, "%d", &col[i]);
		if (res < 1 || res == EOF){
			//読み取れなかったら終了
			break;
		}
		else{
			//読み取り成功回数をカウント
			succNum++;
		}
		
		//次のカンマを探す
		adr = strchr(adr, (int)',');
		if (adr == NULL){
			//見つからなかったら終了
			break;
		}
		else{
			//カンマがあればその次の文字を開始位置にセット。
			adr += 1;
		}
	}

	if (succNum == num){
		//指定した読み取り数と等しければ成功
		return 0;
	}
	else{
		//どこか取りこぼしがあった場合
		return -1;
	}
}


void main(void){

	int dat[8];

	int res = csvIntRead(str, dat, 2);
	for (int i = 0; i < sizeof(dat)/sizeof(int); i++){
		printf("%d\n",dat[i]);
	}
	printf("\n");

}

2016年10月23日 (日)

C言語 関数ポインタを使って遊ぶ

可変長引数を使うと、いくらでも引数を渡せるということがわかったので、それを使って関数ポインタを渡したらどうなるかやってみました。まずは関数ポインタというのがどんな風に使われるかの基本をおさらいしてから、この記事の本題に移っていきます。




まず、九九の表を作れといわれたとする。これから何が起こるのか知らされもせず。

#include 

int mult(int a, int b){        return a*b; } void dispCalc(void){        for (int i = 0; i < 10; i++){               for (int j = 0; j < 10; j++){                      int res = mult(i, j);                      printf("%3d,", res);               }               puts("");        } } void main(void){        dispCalc();        getchar(); }

じゃあ足し算の表は?となったとき、mult関数とは別にdispCalcAddでも作ればよいのだが、 もっと良い方法に、計算方法を書いた関数へのポインタを渡すというのがある。

#include 

int mult(int a, int b){        return a*b; } int add(int a, int b){        return a+b; } void dispCalc(int (*func)(int,int)){        for (int i = 0; i < 10; i++){               for (int j = 0; j < 10; j++){                      int res = func(i, j);                      printf("%3d,", res);               }               puts("");        } } void main(void){        dispCalc(mult);        dispCalc(add);        getchar(); }


ここまでが関数ポインタの概要。同じ機能を実現する関数は、振る舞いに応じて別々に関数を作らなくても、どのような振る舞いをするかを関数ポインタで渡してやればよいということ。

ここからが本題。じゃあ九九の表と足し算の表のあと、もう一回九九の表を書け。とか、ファイルに何の表をどの順番で書くか指示が書いてあるからその順番で書けとか、もうむちゃくちゃ色々な指示が出たらどうするか。そこで可変長引数を使ってみたら、

#include 

#include

//計算をする関数のポインタをtypedefする typedef int(*calc)(int, int); int mult(int a, int b){        return a*b; } int add(int a, int b){        return a + b; } void execList(int n,int a,int b, ...){        va_list argv;        va_start(argv, b);        for (int i = 0; i < n; i++){               calc pnt = va_arg(argv, calc);               int result = (pnt)(a, b);               printf("res %d\n", result);        }        va_end(argv); } void main(void){        //掛け算、足し算、掛け算        execList(3, 5, 2, mult, add, mult);        getchar(); }


関数のポインタをtypedefしておくと少しでも見やすくなる。また型チェックで警告も出してもらえるかもしれない。


ゲームのキャラクターの振る舞いをセットしたり、いろんなログを決まった順番で加工したり。色々使えそうだ。

2016年10月16日 (日)

C言語 sscanfの続きとvsscanfとの違い。それぞれの使いどころ

前回の記事でsscanfを使って文字列の読み込みをやりました。今日はもう少しsscanfを使ったテクニックを掘り下げます。

scanf、sscanfのほかに、vsscanfというのがあるのですが、これは可変長引数が受け取れるようになっています。可変長引数というのは、引数を何個でも受け取れるということ。しかしどこで使えばいいのか。一見sscanfとの違いがわからずもやもやしてしまいました。

以下、vsscanfを使ったときのメモです。

下記のような3種類のログがファイルの中に行毎に混ざって存在しているとする。

"channels  ,0(0),1(3),2(?),3(-),4(R),5(-)"
"answers   ,ss,0,sd,2,us,1"
"handles    ,-10         ,10         ,102    "

ログを読むための書式文字列は下記のようになった。読んだ後の値は構造体や配列に入れる。

int ret = sscanf(buff, "%10s ,%10d ,%10d ,%10d"
                     ,enc.name,&enc.left,&enc.right,&enc.time);
char ch[6];
int ret = sscanf(buff, "%s ,0(%c),1(%c),2(%c),3(%c),4(%c),5(%c),"
              , enc.name, &ch[0], &ch[1], &ch[2], &ch[3], &ch[4], &ch[5]);
       int res[3];
       char name[32];

       int ret = sscanf(buff, "%s ,2d,%d,3d,%d,us,%d"
              , name,&res[0],&res[1],&res[2]);

vsscanfを使うには可変長引数の使い方についての知識が必要で、それを使うにはstdarg.hをインクルード下上で、下記のようなラップ関数を用意する必要がある。そのラップ関数に書式文字列と、入れ物のアドレスを渡して使う。

//書式を指定してレコードを読みこむ
void input_vsscanf(char *s, char *format, ...){
	va_list argv;

	va_start(argv, format);
	vsscanf(s, format, argv);
	va_end(argv);

	return 0;
}

//この関数からvsscanfのラップ関数を呼び出す
void logReader(char *str){
	if (NULL != strstr(str, "answers")){
		input_vsscanf(str, "%s ,2d,%d,3d,%d,us,%d"
			, name
			, &result_log.res_ss, &result_log.res_sd, &result_log.res_us);
	}
}

やっているうちにvsscanfの強みには気がついたものの、引数については相変わらず一個ずつ指定しないといけないので、いい使い方例を書くまでにはいたりませんでした。今のところ用途は無いのです が、書式文字列を変えることによって、読み込みたい値が書いてある場所を任意に指定するとか。たとえば、同じ情報が書かれたログだけれど書式がちょっとず つ違っていて、そういうときに場合分けして読みたいとか。そういう用途なのかと思っていました。

追記
vsscanf以外のvprintfや他の関数についてみていたら、どうやらデバッグ用関数を作るときに有用なことがわかりました。たとえば読み込んだ文字列から値を取り出すと同時に、ファイルに書き出すとかUDPでどこかへ転送するとか。そういうデバッグ用関数を作るときに使えそうです。

今日のところはここまで。

2016年10月 9日 (日)

C言語 可変長引数の使いかた

いままで注目していなかったが、vsscanfのついでに使えるようになっておこうと色々試しました。下記にメモを記します。


最初にva_startを呼び、どこからが可変長になっているかを知らせ、va_list型の変数に受け取らせる。上の例で行くと、関数argtameの引数はint nと...の可変長引数。

va_start(argv,n)


ということは、引数nの次から可変長引数ということを処理系に教えている。あとはva_argを呼び出すことで順番に受け取った引数が出てくる。最後にva_endを呼んで終了。

とりあえず動きを見たいので、for文を使わずに一個ずつargvが取り出される様子を見ることにした。


#include 

#include

void argtame(int n, ...){ va_list argv; va_start(argv, n); int a; a = va_arg(argv, int); printf("%d\n", a); a = va_arg(argv, int); printf("%d\n", a); va_end(argv); } void main(void){ argtame(2, 3, 4); }

もとのva_argを壊したくない場合は、一旦va_copyでコピーを作ってからやればよいとのこと。

va_copyの使い方の例(別HP)

2016年10月 2日 (日)

C言語で文字列を読むときのノウハウ

文字列を読ませる作業は結構基本的なことですが、どうやればいいか入門書にはあまり載っていませんでした。
そこで見よう見まねでscanfを使って、うまくいかずにプログラムを暴走させていました。
今思えばそこが簡単にクリアできていれば、もっと上達していたのかなと思います。。。

scanfをいきなり使うよりも、コマンドラインならgetsとかで受けて。ファイルならfgetsで一行単位でバッファにまず受けてから、そのバッファに対してsscanfというのが使えます。

sscanfというのは使い方を知っているとなかなか便利で、正規表現を知らなくても簡単に使えます。以下に例を示します。

#include 

void main(void){ FILE *fp; if (NULL == (fp = fopen("tame.txt", "r"))){ printf("fileopen error\n"); getchar(); exit(1); } char buff[256]; while (NULL != (fgets(buff, sizeof(buff), fp))){ printf("%s", buff); int x, y; int ret; ret = sscanf(buff, "%d,%d",&x,&y); printf("sscanf : %d\n",ret); if (ret == EOF){ printf("EOF returned\n"); getchar(); exit(1); } printf("x:%d , y:%d\n", x, y); } }

ファイルを開いてから、whileループの中で一行ずつbuffに読み込む。sscanfの書式を使ってbuffから2つの数値x,yを取り出す。sscanfの戻り値は、書式に沿って取り出せた値の個数となる。書式にうまく当てはまらないものがあると、書式で指定した値の個数よりも小さくなる。または0になる場合がある。エラーが出るとEOFを返すのだが、これは実際に試してみたら、書式文字列で"%d"を指定しているのに、その位置に文字列しかなかった場合など、型が全く違う場合に起こった。


具体的にログファイルを対象に読み込みを試してみた。書式文字列を色々作った。たとえばこんな文字列があったとする。

"20160613142310549,handle     ,0         ,0         ,25005     "

こんな書き方をすれば読み込めることがわかった。

int ret = sscanf(buff, "%[^,],%10s ,%10d ,%10d ,%10d"
                     ,enc.timestamp,enc.name,&enc.left,&enc.right,&enc.time);

ちなみに、CSVで文字列を読ませるときは、カンマが読んだ文字列の中に含まれないように書式文字列に工夫が必要。下記のようにすれば、1カラム目で文字列、2,3カラム目は数値を読ませられる。[^,]でカンマを読まないようにできる。さらに、[^0-9]とすれば、数字を読まなくできるし、[^a-z]なら小文字のアルファベットを読まなくできる。

ret = sscanf(buff, "%[^,],%d,%d",name,&x,&y);


補足:文字列の長さが決まっているなら、文字数の最大長を指定することもできる。

scanf("%5s",str);

これで5byteまでと指定したことになる。


補足:カラムの中に空白文字が含まれる場合。たとえば文字列の前に空白文字があれば、%sの前にスペースを入れて指定すれば、空白文字を無視した部分を文字列として読み込む。

scanf(" %s,",str);

この調子で行けば長いCSVを読むときも結構使えるのかと思い、書式の繰り返し指定ができるのかどうか探してみた。カラムが何十個も続いていたら一個ずつ指定するのが面倒だからである。

書式文字列の仕様については色々HPが出てくる。しかし具体的な使用例が載っていないので、別途探したのだが、日本語のページは中々出てこず。

printfの書式について(別HP)

stackoverflowなど、英語ページで具体的な例が色々出てきた。

書式文字列で繰り返しを指定(別HP) scanfを繰り返して使う(別HP)



※今回のCSVを読ます用途には使えないのだが、ちょっと面白い書式文字列を見かけた。
printf("%0*d\n",50,0);
とすると、0が50個並んで表示される。

結局、カラムが数十個、数百個になると、sscanfを使って一個ずつ指定するのは面倒だ。デリミタの位置を最初に検索してそれにそって文字列を読ませるなどのやり方を選択するのがよさそう。


※脱線するがpythonだったらこうする。

>>> print ('{:4} '*5).format(3,3,43,5333,6)

※Perlにもprintfがあり、よく似ているので途中間違って試してしてしまった。


sscanfを使って色々な文字列の読み込みを行いました。書式は常に固定ですが、文字数を指定して何文字目から何のデータ、その次の何文字が何のデータと細かく指定する必要が無く、atoiやstrtolなどの変換を行わずに書式指定だけで変換もやってくれるので、非常に有用です。

2015年10月 5日 (月)

とあるcmakeでトラブルがあったときに対処した内容 使ってみた感想

オープンソースを使っているとよくお目にかかるmakefileやCMakeであるが
ちょっとでもエラーで躓くと、その対処にはものすごく時間がかかることを覚悟しなければならない。しかも、たまに他のPCで試してみると、なぜかすんなり通ったりすることがあるのでなんだか遣る瀬無い。

別のマシンを使って試せないときは、おとなしくエラー内容を解析していくことになる。
色々グーグルをあさってみるのだが、CMakeに限っては有力な手がかりってそうそう無いものだ。

以下
openRTMのパッケージを作ろうとしてCMakeを使ったときのメモ。


エラー情報によると、
一個目はワーニングで、

CMake Warning at CMakeLists.txt:51 (find_package):
  Could not find a package configuration file provided by "OpenRTM" with any
  of the following names:

    OpenRTMConfig.cmake
    openrtm-config.cmake

  Add the installation prefix of "OpenRTM" to CMAKE_PREFIX_PATH or set
  "OpenRTM_DIR" to a directory containing one of the above files.  If
  "OpenRTM" provides a separate development package or SDK, be sure it has
  been installed.

OpenRTMConfigというパッケージコンフィグファイルがないと言っていて、
・CMAKE_PREFIX_PATHにOpenRTMのインストールプレフィックスを加える
・OpenRTM_DIRに上記のうちいずれかを含むディレクトリをセットする。
のどちらかをやれとある。
OpenRTMが分割開発パッケージやSDKか、インストールされているか確かめろと言っている。

残りの2つについては、

Use cmake/Modules/FindOpenRTM.cmake in the project
Could NOT find PkgConfig (missing:  PKG_CONFIG_EXECUTABLE)
CMake Error at cmake/Modules/FindOpenRTM.cmake:73 (file):
  file STRINGS file
  "C:/Users/Nanashi/workspace/Flip/OPENRTM_INCLUDE_DIR-NOTFOUND/rtm/version.h"
  cannot be read.
Call Stack (most recent call first):
  CMakeLists.txt:57 (find_package)

プロジェクトの中の、cmake/Modules/FindOpenRTM.cmake
はPkgConfigを見つけられない。
cmake/Modules/FindOpenRTM.cmakeの73行目とあり、
STRINGSの中のパスがおかしなことになっている。

"C:/Users/Nanashi/workspace/Flip/OPENRTM_INCLUDE_DIR-NOTFOUND/rtm/version.h"

OPENRTM_INCLUDE_DIR-NOTFOUNDという文字列が入っていて、
存在しないパスをさしているのだ。
OPENRTM_INCLUDE_DIR-NOTFOUNDをセットしたのがどこだったかを探し、
正しいパスをセットすれば動きそう。

CMake Error at C:/Program Files (x86)/CMake/share/cmake-3.3/Modules/FindPackageHandleStandardArgs.cmake:148 (message):
  Could NOT find OpenRTM (missing: OPENRTM_INCLUDE_DIR COIL_INCLUDE_DIR
  OPENRTM_LIBRARY COIL_LIBRARY OPENRTM_IDL_COMPILER)
Call Stack (most recent call first):
  C:/Program Files (x86)/CMake/share/cmake-3.3/Modules/FindPackageHandleStandardArgs.cmake:388 (_FPHSA_FAILURE_MESSAGE)
  cmake/Modules/FindOpenRTM.cmake:99 (find_package_handle_standard_args)
  CMakeLists.txt:57 (find_package)


ーーーーーーーーーーーーーーーー解析編

2個のエラーは、
最初に出てくるワーニングにあるファイルが見つからないために、DIR-NOTFOUNDというパスが無理やり設定されていると想像できるので、

OPENRTM_INCLUDE_DIR-NOTFOUND
がセットされる場所を検索した。

書いてあるとおぼしきcmakeやlistをnotepad++で洗いざらい検索してみたが、ちっとも出てこなかった。
そこで、find_packageというコマンドを使っている場所(ワークスペースのプロジェクトフォルダ直下にあるCMakeLists.txtの51行目)を見つけ、
その中にある

/usr/lib64/openrtm-1.1/cmake
というパスが、完全にUNIX向けであることに気づいた。そこで、windows向けに
C:\Program Files\OpenRTM-aist\1.1\cmake
のようなパスをいれてみた。
注意:エスケープ文字として、スペースの前にバックスラッシュを入れる必要がある。

すると、先ほどとは違うエラーが出て、

CMake Error at idl/CMakeLists.txt:34 (add_custom_target):
add_custom_target cannot create target "InterfaceDataTypes_TGT" because
another target with the same name already exists. The existing target is a
custom target created in source directory
_"C:/Users/Nanashi/workspace/Flip/idl". See documentation for policy CMP0002
for more details.
Call Stack (most recent call first):
idl/CMakeLists.txt:45 (_COMPILE_IDL)
idl/CMakeLists.txt:50 (OPENRTM_COMPILE_IDL_FILES)

add_custom_target
というのが、同じ名前のターゲットがすでに存在していたため
"InterfaceDataTypes_TGT"を作成できなかった。
というエラーに変わった。

そのすでに存在するターゲットはソースディレクトリ
"C:/Users/Nanashi/workspace/Flip/idl"
の中に作られたカスタムターゲットである。

CMP0002ドキュメントを見ろとあるが、これはマクロや変数に、全体にわたって(globalに)同じ名前を付けるなという内容のようで、エラー内容とほぼ同じ意味を書いてある。

同じ名前のファイルがあるからいけないのかとおもい、
プロジェクト内のidlフォルダにある
InterfaceDataTypes.idl ファイルを
別なフォルダに移してやってみた。
しかし、エラー内容は全く変わらなかった。


Call Stackにある、45、50行目を見ると、
idl,idlsという変数があり、macro定義の中で使われている。

setというコマンドは、リストへの追加を意味する。
最初の一行目で
set(idls ${CMAKE_CURRENT_SOURCE_DIR}/InterfaceDataTypes.idl ${CMAKE_CURRENT_SOURCE_DIR}/InterfaceDataTypes.idl )

ということをやっているから、
idls
にはCMAKE_CURRENT_SOURCE_DIRにある
InterfaceDataTypes.idl
のファイルがリストに追加される。

add_custom_target
というのはcmakeのコマンドの一種で、ドキュメントに説明がある。
http://www.cmake.org/cmake/help/v2.8.10/cmake.html#command:add_custom_target

CallStackというからには関数、マクロを呼び出したということ。
よく見ると、

macro(OPENRTM_COMPILE_IDL_FILES)

macro(_COMPILE_IDL _idl_file)

macro(_IDL_OUTPUTS _idl _dir _result)

と3つのマクロが書かれていることがわかる。
エラーの中で、

_COMPILE_IDL
OPENRTM_COMPILE_IDL_FILES

というマクロが使われたことがわかる。
そして、OPENRTM_COMPILE_IDL_FILES
というやつがforeachを使ってファイルを一個ずつ_COMPILE_IDL
というマクロにファイル名を渡していっているから、

エラーが出たのは_COMPILE_IDLの中らしい。
28から30行目にかけて、TGTという文字列が登場するから、
InterfaceDataTypes_TGT
というのはこの中で作られた文字列の一つであると想定できる。

気になるのは、.pyというのが混ざっている点で、どうもpythonを入れてないと動かないところがあるのかもしれない。

desktopPC(win8)ではここまでやってタイムアップだったが、別のミニPC(win8.1bing)でやったところすんなりcmakeが通った。
このときはちっともエラーが出なかった!!!


解析のコツ
自分で書いたCソースをコンパイルするときと同じで、CMakeが吐き出すエラー内容を読み解いていくしかない。
そもそもcmakeというツールを使うことがあまりなかったので、
どういうツールであり、何をするためにこのツールがあるのか。入力・出力するものは何か。
をまず知識として入れないと、やみくもにGoogle検索しても、有力な情報は得られなかった。

cmakeってこんなツール

つまるところCのマクロと同じで、OPENRTM_DIRなどの文字列がパス文字列に置き換わっていくと考えれば、
ほとんどのエラーは、パスの指定がおかしくて起こっているということがわかる。

messageというコマンドを使えば、configuration中の途中経過にメッセージを表示できる。
ちょうどprintfを使ったデバッグのようだ。

 

#include 

void main(void){
  printf("hello world\n");
}

2015年2月15日 (日)

OpenGL 文字列を表示させる Cのシンプルな関数

OpenGLを使って文字列を表示させる。
Cで書いてある例が見つからなかったのでCで書いてみた。

void printString(float x, float y, char* str, int length){
	float z = -1.0f;
	glRasterPos3f(x, y, z);
	
	for (int i = 0; i < length; i++){
		glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, str[i]);
	}
}

呼び出しについては、こんな風に文字列の長さと一緒に渡すことにした。
        printString(0, 0, "HogeHoge", 8);

または、文字列と一緒にsprintfを使ってデータを表示することもできる。
	char dstr[256];
	sprintf(dstr, "acc %d,%d", ax, ay);
	printString(ax, ay, dstr, strlen(dstr));


フォントの大きさは定義されているもので10pt,18ptなどから選ぶようだ。拡大縮小を使って微調整するのだろうか。

2015年2月 8日 (日)

VC++ UDPで受けたデータをOpenGLを使ってアニメーションするグラフを表示する。

なんらかのデータをUDPで出し、そのデータを受けてグラフやグラフィックを表示するためのベースを作成した。実験データを可視化したり、デバイスの動作チェックするのに使えそう。

「OpenGLプロッター」第一弾

プロジェクトファイル
「150208_plot1.zip」をダウンロード

<<通信のやり方>>
まず、UDPの受け取り方はWinsock2.hを使う。winsockの設定が必要。
UDPのrecv関数は、何かデータを受け取るまで先に進まないブロッキングという制御をするのが標準動作なので、受信するまで動作が止まってしまうのが困る場合はスレッドで、UDPの受信処理を切り分けてやらないといけない。

<<グラフィック>>
この記事ではOpenGLでの表示がメイン。
OpenGLを使ったグラフィック表示は参考サイトを見ながら、まずは点々をアニメーション表示させてみる。

glutTimerFuncを使ってtimerという関数をコールする。
この関数を使ってglutPostRedisplayをトリガーしている。こいつを呼ぶと画面が切り替わる。

glutIdleFuncを使って、プログラムアイドル時にglutPostRedisplayを呼び出す形にすると、UDPの受信が全く割り込めなくなってしまい、一度も受信できなくなる。

glutPostRedisplayの呼び出し方を工夫しないと、受信と描画が両立しないのだ。



<<おまけ>>
UDPのデータを垂れ流すプログラム。
Cygwin上やRaspberry上でコンパイルして使います。GCC環境対応
「pcSend.c」をダウンロード

プロッターを起動してから、このツールでUDPを出すと、SINカーブを描くように点が移動する。

VC++でマルチスレッドプログラミング windows C++

VisualStudio2013を使って、マルチスレッドプログラミングのためのプロジェクトを作った。

いままでArDroneを飛ばしたり、通信の実験をするためにCygwinやRaspberryPiでlinux環境のマルチスレッド pthread.hを使ってきた。

windows環境では色々な選択肢があり、賞味どれが一番よいのかさっぱりわからなかった。一番簡単にできたのが、process.hをインクルードして、_beginthreadexを使う方法。
参考HP。

他に、MFCのCThreadやらBoostライブラリなどがあるのだが、Windowsにどっぷり漬かるのには抵抗があった。

そこで、いままで使っていたpthreadを使える方法を調べた。
まさに探していた情報がここ。
参考HP。


<<まずはサンプルプロジェクトファイル>>
下記準備を済ませたら使えるようになる。
「VC_pthread.zip」をダウンロード

1.下準備
ダウンロード元サイトにて、必要なファイルを落とす。
prebuilt-dll-1-10-0-releaseにある、includeとlibの中身を落としてくる。一個ずつやったので面倒だったので、「pthread.zip」をダウンロード にZIPにしてまとめた。

2.ファイルコピー
ファイルをVC++2013のincludeにコピーする。

・ヘッダファイルはここ
X:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include
・libファイルはここ
X:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\lib
・dllファイルはここ
X:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin

3.ファイルをプロジェクトとリンクさせる
プロジェクトを作成したら、先ほどコピーした.libファイルをリンクに入力する。
このようにソリューションエクスプローラで右クリックし、プロパティを開く。
Ws000036_2

リンカーの入力。右の”追加の依存ファイル”の欄に、2.でコピーしたファイルの名前を入れる。pthreadVC1.dllというやつ。
Ws000037
※このdllファイルの名前はバージョンによってちょこちょこ変わるようで、このときはVCとついているやつを選べばいけた。

Ws000038_3  

あとのソースの書き方はlinuxでpthreadを使うときと全く同じ。

スレッドの動作を適当に確かめたければこのソースで
「ser_thred.c」をダウンロード








2015年1月31日 (土)

C++ MinGWでwinsockを使う。  ODEで得たデータをUDPで配信する

ODE(open dynamics engine)は、フリーの物理演算エンジン。これを使って色々遊んでいるのだが、仮想空間で起こった現象にまつわる情報をリアルタイムで外に取り出し、何かに応用したくなった。

ODEを始めた時からCode::Blocks(フリーのIDE環境)を使っていて、CodeBlocksのC++コンパイラはMinGWを使っている。というのも、こちらを参考にして色々試してみたら、CodeBlocksが最もすんなり入ったからだ。
MinGWコンパイラでTCP、UDPを使おうと思ったら、WindowsAPIを使う必要がある。

TODO:
1>WindowsでUDPを使う手段 winsockの使い方を知る。
 これはとっても使いやすいソースがある。こちらからコピーしたやつをそのまま使える。

2>codeblocksの環境でwinsockをリンクする方法を調べる。
 これは意外と簡単だった。codeblocksはメニューがあんまりないのでわかりやすい。

まず、プロジェクトのビルドオプションを開く。

Ws000034_3

図のアイコンの上で右クリック。→Build Options


こんな窓がひらくので、
Linker settingsのタブのLink librariesのところにAddで
"ws2_32"を追加する。

注意
ws2_32.dllなどとすると、エラーでコンパイルが通らない。

Ws000035

コンパイルして完了。