2018/02/25

[c/c++]構造体のメンバのサイズだけ知りたい

検索すると、この手の情報はたくさん出てくるのだが、自分でやってみたら書いてみたくなっても仕方なかろう?


サイズをしらべると言えば、sizeof演算子だ。
ただ、これは構造体のメンバを直接指定することができない・・・というよりも、C言語として、構造体の型のメンバだけを表す方法が無いのだと思う。

01: struct aaa {
02:     uint8_t     item8;
03:     uint64_t    item64;
04:     const char* itemc;
05:     int16_t     item16;
06: };

こんな構造体があったとして、たとえばitem64だけを指す方法が無い。
インスタンスになってしまえばよいのだが、sizeofしたいだけなのインスタンスを作るのは避けたい。

じゃあどうするかというと、C言語としてインスタンスのように見えるものを使えばよい。
というわけで、適当な数字を「これはstruct aaa型のアドレスなんです!」と主張して、そのポインタのメンバをsizeofしてしまうわけだ。

#define M_SIZE(type, mem)   sizeof(((type *)0)->mem)

こんなマクロを作って、

M_SIZE(struct aaa, item64)

とすると、

sizeof(((struct aaa*)0)->item64)

と展開される。
意味としては、アドレス0にstruct aaa型のメモリが入っていると見なしているだけだ。
アドレスにアクセスするわけではないので、0じゃなくても、100でも1000でもよい。


急にこの話が出てきたのは、lmdbと関係している。

いま、構造体のデータをlmdbでがさっとメモリダンプ的に保存させているのだが、構造体の構成が変わるたびに前のDBが使えなくなってしまうので、そろそろ何とかしたいと考えている。

やるなら、各メンバごとにkey/dataで保存させていくしか無かろう。
無いのだが、書き直すのがめんどくさい。
なるべく多く書かずにやらせたいわけだ。


lmdbは、keyもdataもMDB_val型を使っている。
MDB_val型は、サイズとデータを持つ構造体だ。
keyにつける名前はバイナリでも文字列でも何でもよいのだが、構造体であればメンバ名にするのがよいだろう。
keyのサイズはstrlen()で出せるからよいとして、あとはdataのサイズだ。
これを機械的に出したいなら、上記のマクロのようなものを作ってしまうのがよかろう。

そして、key名もdataサイズもメンバ名で取り出せるなら、こんなものを用意するとよいか。

#define ARRAY_SIZE(a)       (sizeof(a) / sizeof(a[0]))
#define M_SIZE(type, mem)   sizeof(((type *)0)->mem)
#define M_ITEM(type, mem)   { #mem, M_SIZE(type, mem) }

struct aaa {
    uint8_t     item8;
    uint64_t    item64;
    const char* itemc;
    int16_t     item16;
};

const struct {
    const char  *name;
    size_t      sz;
} DB[] = {
    M_ITEM(struct aaa, item8),
    M_ITEM(struct aaa, item64),
    M_ITEM(struct aaa, itemc),
    M_ITEM(struct aaa, item16)
};


int main(void)
{
    for (int lp = 0; lp < ARRAY_SIZE(DB); lp++) {
        printf("name=%s: %lu\n", DB[lp].name, DB[lp].sz);
    }
}

まあ、そこまで悪くはないかな。

[c/c++]仮引数を書かない場合と戻り型未指定を実際にやってみる

昔のC言語に仕様としてあり、今となってはほぼ使われていない書き方や、推奨されていない書き方がある。
いくつかあるのかもしれんが、私が気付くのはこの2つだ。

  • 戻り値の型を書かないとint
  • 仮引数を省略すると...


知識として持っていて、実際にやったことが無かったので、やってみた。

01: #include <stdio.h>
02: #include <stdarg.h>
03: 
04: func()
05: {
06:     va_list argptr;
07:     va_start(argptr, 3);
08: 
09:     const char *str = va_arg(argptr, const char*);
10:     printf("%s\n", str);
11:     int a = va_arg(argptr, int);
12:     int b = va_arg(argptr, int);
13:     return a + b;
14: }
15: 
16: int main(void)
17: {
18:     int res = func("hello", 3, 8);
19:     printf("result=%d\n", res);
20: }


書くのは書けたが、ビルドが通らない。。。

$ gcc -std=c89 -o tst noarg.c
In file included from noarg.c:2:0:
noarg.c: In function ‘func’:
noarg.c:7:5: error: ‘va_start’ used in function with fixed args
      va_start(argptr, 3);
      ^

funcを、

func(int num, ...)

として、呼び出しの第1引数に何か整数を入れておくと、warningが出る(second parameter of ‘va_start’ not last named argument)ものの通るし、実行できる。
va_start()の第2引数をnumにすれば、warningも消える。


https://linuxjm.osdn.jp/html/LDP_man-pages/man3/stdarg.3.html

va_start()の第2引数は、引数リストのうちで最後にならんでいる引数名、となっている。
うーん、では、「引数名を省略したら...扱い」かどうか確認できないではないか。。。


上記リンクの「注意」「バグ」に説明が載っていた。
stdargでは許されていない、ということで、<varargs.h>を使えば動かせそうだ。

#error "GCC no longer implements <varargs.h>."

あー、gccでは徹底的に使えなくなってるのですな。
残念だが、ここであきらめよう。


01: #include <stdio.h>
02: #include <stdarg.h>
03: 
04: func(int num, ...)
05: {
06:     va_list argptr;
07:     va_start(argptr, num);
08: 
09:     const char *str = va_arg(argptr, const char*);
10:     printf("%s\n", str);
11:     int a = va_arg(argptr, int);
12:     int b = va_arg(argptr, int);
13:     return a + b;
14: }
15: 
16: int main()
17: {
18:     int res = func(3, "hello", 3, 8);
19:     printf("result=%d\n", res);
20: }


こそっと、main関数はreturnなくてもOK、というやつも使っている。
自分でmainを呼び出す処理を書くときは、サブルーチンコールではなくてジャンプで飛ばしてスタックの消費を減らしたりしてしまいがちだ。
C11だと「noreturnマクロなんてものがあるから、今後はそれを書いた方が親切かもしれん。

2018/02/24

[tech]お悩みのKVS DB

愚痴というか、悩みというか、反省談というか。


いま作っているシステムでは、lmdbというKey-Value Storeのデータベースを使っている。
というといろいろ考えて選択したように見えるが、

  • SQL使うほどでも無い
  • C言語で使えるもの
  • よく使われている

というところで選んだだけだ。
そもそも、私はストレージが使えない組み込みシステムをやることが多かったので、DB自体くわしくない。


lmdbは、NoSQLと呼ばれる類のDBらしい。
https://ja.wikipedia.org/wiki/NoSQL

DBという全体集合があり、その部分集合としてSQL DBがある。
そして、NoSQLは、その反転した集合になりそうな気がするのだが、Wikipediaに書いてある"Not only SQL"が語源だとするならば、SQLを含んでいてもよいし、含まなくてもよい、ということになりそうだ。


lmdbは、SQL文は使わない。
DBの要素としては、こういうものがある。

  • environment
  • DB
  • key
  • data

まず、environmentという大きな広場があり、その中にDBを複数持つことができる。
そして、DBの中にkey-dataのセットでデータが格納されていく。

「トランザクション」というアクセス単位は、environment単位になる。
これに気付かなくてねぇ・・・。
トランザクションってDB単位でやるものだろうと思い込んでいたので、DBのオープンAPIにトランザクションが必要だとわかったときはショックだったわ。

イメージとしては、ドライブとディレクトリみたいなものか。
environmentがドライブで、DBがディレクトリ。
そして、ロックを掛けられるのはドライブ単位だ、と。

どうしても同時にロックしたくて、でもシステム上うまいことやれそうにないなら、environment自体を分けてしまうしかない。
システムを作って半年以上あれこれいじっているが、設計がよくなかったのか、lmdb以外だったらうまくやれたのか、未だによくわかっていない。


結局、environmentを分けることで対応したのだが、未だにどうするのがよかったのか、あるいはどうする方がよいのか、私の中で結論が出ていない。

SQLとかリレーショナルとか、そういう関連性がないということだけはわかっている。
そして、C言語から使いたい。
あとはメンテナンスされていっているとか、枯れているとか、そのくらいか。


https://ja.wikipedia.org/wiki/NoSQL
https://en.wikipedia.org/wiki/NoSQL


うーん、jaとenのWikipediaを並べてみたのだが、内容はともかく、数が多い!
これだけあるということは、特色がそれぞれあって、望む形はいろいろあるということなのか・・・。
「Aである」という事象はAだけだが、「Aではない」という事象は無限にあるのと同じか。

つまり、私が"lmdbでよかったんだろうか"という悩みは、ある意味では当然のことというわけだ。
無限にあるものの中から正解を1つだけ見つける、ということはあり得ないからだ。

[c/c++]memory.hは標準ヘッダではない

よくやってしまうのだが、malloc()やmemcpy()を使ったのに、ヘッダファイルを追加し忘れていることがある。
まあ、コンパイル時にエラーが出るので分かるのだが、問題はその次だ。
どれをインクルードするとよいのだっけ?


ふっと思いつくのが、連続データに対する処理だからストリング命令、ということで<string.h>。
しかし、心の中で「もしかしたら<memory.h>だったかも・・・」と心配になる。
コンパイルすればわかるのだけど、なんか追加した標準ヘッダを間違えるって、なんか素人っぽいじゃないか。
できれば、修正後は一発で正解したいのだ。

そして、malloc()/free()は<stdlib.h>だということを思い知らされるのである。。。

どうでもいいけど、strcpy()なんかも<string.h>に入っていると、文字列用のヘッダだと勘違いしてしまうよね。


それはさておき。
<stdlib.h>と<string.h>をインクルードすれば何とかなるのはわかるが、では<memory.h>には何が入っているのだろう?

オライリーのCクイックリファレンスで調べてみたのだが・・・<memory.h>は標準ヘッダではなかった!
そうか、そうなんだ・・・。

/usr/include/memory.hを見ると、<features.h>と<string.h>をインクルードしているだけだった。
<features.h>なんて通常はインクルードさせたことが無かったのだが、GLIBCのバージョンなどいろいろ定義されている。
じゃあ、単に<string.h>をインクルードするより<memory.h>の方がいいのか?と思ったが、<string.h>も先頭で<features.h>をインクルードしていた。


というわけで、私の結論はこうだ。

  • <string.h>でも<memory.h>でもいい
  • <memory.h>は標準ヘッダではないので、すべてのコンパイラが持っているかは定かでは無い
  • 標準ヘッダでないからといって、<memory.h>をインクルードしていても変ではない


標準じゃないからといって、使って問題があるわけではないのだ。
それに、個人的には「str***()」は<string.h>に、「mem***()」は<memory.h>に、となっていた方がわかりやすいと思う。

2018/02/17

[btc]addwitnessaddressのアドレスからdumpprivkeyはできない

bitcoindの話だ。

bitcoin-cli getnewaddressすると、P2PKHのアドレスが返ってくる。
HDウォレットのインデックスがインクリメントされて新しいアドレスが生成されたということか。

そのアドレスについてbitcoin-cli addwitnessaddressすると、P2WPKHのアドレスが返ってくる。
ただし、現在(2018/02/17)のところ、bitcoindではnativeのP2WPKHアドレスを扱うことができない。
だから、P2WPKH nested in BIP16 P2SHというアドレスがが返ってきている。

nested in BIP16 P2SH形式のよいところは、見た目上はP2SHアドレスになっているというところだ。
これなら、segwitに対応していないウォレットであっても送金することができる。
じゃあ、その形式だけでいいじゃないか、となりそうだが、segwitのnativeアドレスには利点がある。
その場合には、トランザクションのサイズを小さくすることができるのだ。

nativeなP2WPKHの場合、今までのscriptSigの部分がまったく無くなり、witnessのセクションに移動する。
segwitにするとブロックに入るトランザクションの数が増えるというが、たしかにトランザクションの数は増えるものの、トランザクション自体のサイズについては別の話だ。
nested in BIP16 P2SH形式の場合、scriptSigにもデータを置かないと既存ウォレットとの互換性が無くなってしまうので、トランザクションとしてのサイズが多少増えてしまうのだ。
ブロックに入れる際にwitnessの部分は別の場所に入るため、そこまで影響は無いのかもしれんが、大きくなることはなるのだ。
これがnative形式だとトランザクション自体が別物という扱いになるからかscriptSigのサイズは0になり、署名などはすべてwitnessに押し込められる。


私が不勉強なので、ブロックに取り込むところなどのしくみをよくわかっていないのだが、まあ、そこはbitcoindなんかがやってくれるからいいや、と割り切っている。
フルノードの庇護を受けている間は、けっこう楽ができるのだ。


ただ、というか、しょうがないか、というところだが、addwitnessaddressでつくったアドレスはdumpprivkeyで秘密鍵が取って来れないのだ。
P2SHという時点であきらめてしまう気がする。


次の0.16ではBECH32形式のアドレスが使えるようになるとか。
そうなると、直接nativeの形式でアドレスを生成できるようになるのかもしれん。

2nd Layerというか、BOLTではfundingにnativeなアドレスを使うことになっているので、今の時点では何とかしてP2WPKHから送金させる必要があるのだった。

まあ、そういうこともあるさっ。

2018/02/15

[c/c++]getopt_long()のlongindexが返るのは戻り値が0のときだけ?→ときだけだった

(2018/02/17更新)

ずっと先延ばしにしてきた、getopt_long()の勉強をすることにした。

組み込みだと、引数とか設定できないじゃないか。
値はNVに保存して、起動時に読み出す。

そういう生活をしていたので、Linuxでツールを作るときも、設定ファイルを読ませるようにしているのだが、これの評判が悪い。。
「ファイルでやる意味が分からん」とまでいわれてしまい、しぶしぶ引数を列挙するタイプで対応していた。

が、似たような機能を同じツールで実現したいことが多くなり、引数で見分ける必要性が出てきた。
しぶしぶgetopt()で対応していたのだが、さらに引数が増え、使える文字が減り、自分でも訳がわからなくなってきたのだ。


以上、自分の中で、どうしてもやらないといけないという動機づけを行う作業でした。


最初に見たのが、こちら。

getopt_long関数の利用 - コマンドラインオプションの処理 - 碧色工房

まずは動かして考えよう、とWindowsのWSLでやってみたのだが、なんか、ときどきSegmentation Faultが発生する。


よくわからんので、こちらのサンプルも動かしてみた。

Man page of GETOPT

こちらは動いたので見比べたのだが、Man pageの方はlongindexを初期化しているし、戻り値が0のときにしかlongindexを使っていない。
最初の方は未初期化で、getopt_long()に任せている。


しかし、説明文を読む限りでは、longindexをNULL以外で指定しておけば、正常な場合はインデックス値が戻ってきそうである。
うーん、WSLの不具合かなぁ。。。


2018/02/17

Azureで動かしているUbuntuで確認したが、やはり同じ動作になった。
WSLよ、疑って済まん。。。


よく読めば、こちらのサイトにもちゃんと書かれていた。
getopt_long関数の利用 - コマンドラインオプションの処理 - 碧色工房


manとしては、こう書かれている。

longindex は、NULL でなければ、 長いオプションのインデックスを longopts からの相対位置として保持している変数へのポインターとなる。

つまり、「長いオプションのインデックスを」だから、短いオプションのときには更新しない、ということか。
読み取るのが難しいですよ。。。


長いオプションかどうか見分けたい場合は、longindexを-1などに初期化してからgetopt_long()を呼び出して、更新されたかどうか確認すればよいのかな。
そういうシーンは少ない気がするので、私の場合はNULLにしてしまうだろう。


せっかくなので、オプションの使い方で気になるところも見ておこう。
「値付きのオプションに、イコールを付けてよいのかどうか」問題だ。
ソースファイルは、工房さんのサンプルをそのまま使っている。

$ ./tst --clear 100
c = 1, 100


$ ./tst --clear=100
c = 1, 100


$ ./tst -c=100
c = 1, =100


$ ./tst -c 100
c = 1, 100


./tst -c100
c = 1, 100

あー、こうなるんだ。

長いオプションの方はイコールを付けて値、短いオプションの方はスペースを入れるか入れないかで値、という使い方が一般的なようだ。
短いオプションでイコールを使ってもうまくいくやつがあるけど、あれはアプリ側で取り除いてるのかな。

2018/02/14

[c/c++][make]dependを作成せずにcleanしたい

例えば、test.cとtest.hがあり、test.cはtest.hをincludeしていたとしよう。
依存関係を設定せずにMakefileを作ると、一度ビルドしてからtest.hを変更しても、makeで作り直してくれない。


では、依存関係を"gcc -MM"で作ってincludeさせるとしよう。

01: SRC=test.c
02: OBJ=test.o
03: BIN=tst
04: 
05: all: $(BIN)
06: 
07: $(BIN):$(OBJ)
08: 	gcc -o $(BIN) $(OBJ)
09: 
10: $(OBJ):
11: 	gcc -c $(SRC)
12: 
13: clean:
14: 	-rm -rf $(OBJ) $(BIN) .Depend
15: 
16: .Depend:
17: 	gcc -MM $(SRC) > .Depend
18: 
19: -include .Depend


これで初回ビルドすると、こう。

$ make
gcc -MM test.c > .Depend
gcc -c test.c
gcc -o tst test.o

2回目は、こう。

$ make
make: Nothing to be done for 'all'.

test.hだけ変更すると、こう。

$ make
gcc -c test.c
gcc -o tst test.o

言うことなしだ。


が、cleanする。

$ make clean
rm -rf test.o tst .Depend

ここまではよいのだが、続けてcleanする。

$ make clean
gcc -MM test.c > .Depend
rm -rf test.o tst .Depend

そう、一度.Dependを生成してから削除するのだ。。。

細かい動作は知らないのだが、.Dependをincludeするときに、依存を調べてしまうんじゃないだろうか。


GNU make 日本語訳(Coop編) - makeの実行方法

$(MAKECMDGOALS)などというものがあるのか!

01: SRC=test.c
02: OBJ=test.o
03: BIN=tst
04: 
05: all: $(BIN)
06: 
07: $(BIN):$(OBJ)
08: 	gcc -o $(BIN) $(OBJ)
09: 
10: $(OBJ):
11: 	gcc -c $(SRC)
12: 
13: clean:
14: 	-rm -rf $(OBJ) $(BIN) .Depend
15: 
16: .Depend:
17: ifneq ($(MAKECMDGOALS),clean)
18: 	gcc -MM $(SRC) > .Depend
19: endif
20: 
21: -include .Depend

$ make clean
rm -rf test.o tst .Depend
$ make clean
rm -rf test.o tst .Depend

そうそう、それでいいんだよ、それで!

2018/02/11

[btc]デフォルトのdust limitは546(2018/02/11)

Bitcoinで使用できる、voutのamount下限はいくつだっけ、といつも考えてしまう。
通常は気にしないくらいの値だが、2nd Layerとかやってると、ね。


今の時点のmasterのデフォルトでは、546satoshiでいいだろう。

https://github.com/bitcoin/bitcoin/blob/fe53d5f3636aed064823bc220d828c7ff08d1d52/src/test/transaction_tests.cpp#L695

BOOST_CHECK_EQUAL(nDustThreshold, 546);



固定値なら「546」で検索すれば出てきたのだろうが、どうやら計算して得るものらしい。
将来変わるかもしれんので、masterの方のリンクも載せておこう。
行は変わるかもしれんが、nDustThresholdで検索すればいいんじゃないかね。

https://github.com/bitcoin/bitcoin/blob/master/src/test/transaction_tests.cpp#L695



APIとしてはこれだろうか。
https://github.com/bitcoin/bitcoin/blob/5961b23898ee7c0af2626c46d5d70e80136578d3/src/policy/policy.cpp#L18

推測に推測を重ねてしまうが、判定はここか。

https://github.com/bitcoin/bitcoin/blob/5961b23898ee7c0af2626c46d5d70e80136578d3/src/policy/policy.cpp#L52-L55

bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
{
    return (txout.nValue < GetDustThreshold(txout, dustRelayFeeIn));
}

だとすると、546未満がNG、ということになるな。

Gmailのaddonで返信文を作る(が、Subjectが日本語だと化ける)

Gmailをブラウザで見ると、アドオンというものがあることに気付いた。
自分でも作ることができるらしい。


このクイックスタート通りにやれば、取りあえず何か動くものができる。
https://developers.google.com/gmail/add-ons/guides/quickstart

メールを1つ選択すると、その人からの直近のメールが5つ分リストに出てくるようだ。



ブラウザでGmailを使うことはほとんど無いのだが、あったらよいと思うのは文章のテンプレートだろうか。
「お世話になっております」みたいな、どうでもいいような文章を手で打ちたくないのだ。
Chromeのアドオンにそういうものがあったような気がするが、内部で実現できた方が安心だ。


サンプルがあるので、これの文面を入れ替えればよかろう。
https://developers.google.com/gmail/add-ons/how-tos/compose

・・・と思ったのだが、これの動かし方がわからない!
中を知らずに貼り付けるだけで動かそうという考えが甘かったようだ。


こんな感じにすると、動いた。

oauthScopesはいらないものがあるかも。
logoUrlはクイックスタートそのままだ。。。


image


もっと格好良く書けるのだろうが、文法を分かってないのでこの辺が限界だ。


やってみて分かったけど、ボタンを押してから文章が出てくるまでに、まあまあ時間がかかる。
もうちょっと長い文章のテンプレートだったらありがたみがあるけど、今回作った程度の長さだったらキーボードから入力してしまいそうだ。

まあ、動いたので私は満足である。


作るだけで満足してしまったが・・・送信するとタイトルが化けていることに気付いた。

image

なんだろう??


どうも、Subjectが日本語だと化けているようだ。
メーラのせいかと思ったが、ブラウザGmailで送信しても化けた。
JISをうまく処理できてない??

と思ったが、message.getSubject()のようにしてSubjectを取得し、それをbodyにするとちゃんと見えている。

URLエンコードでもダメ。
"あいうえお"と指定すると「BDFHJ」と出てきた。
"あ"が0x3042だから、そういう化け方をしているようだ。

"0u3042"にしても「B」だし、"0x42"でも「B」だから、こりゃどうしようも無いのかも。

2018/02/04

[github]wikiのタイトルにハイフンを使いたい場合は、‐(U+2010)を使う

会社に有料のgithubを持っているので、Privateでwikiを更新していた。
そこでハイフンを使ったタイトルにしたくてマイナス(-)を入力したのだが、Saveすると半角スペースになってしまう。

ダブルクォーテーションで囲もうと、逆ダッシュで囲もうとダメ。
\でエスケープしてもダメ。
どうなっとるんじゃー!


よくわからんが、Unicodeの U+2010を使うとよいらしいことがわかった。

https://github.com/gollum/gollum/issues/764#issuecomment-159334607

文字としては「‐」。

見て分からないので、我ながら正しく入力できているか不安だし、ブログにアップする時点で変換されてしまうかもしれん。


これね。

image

[c/c++]gccでvoid*の演算はwarningにならないの

lmdbを使っていて、何気なく自分でコーディングしていて気になることがあった。

mdb_get()などでデータを取得すると、MDB_val型に結果が入っている。
構造体の中身は、mv_sizeとmv_dataで、mv_sizeにはmv_dataのデータ長が入っている。

使うときはmv_dataをキャストするなりコピーするなりしていたのだが、バイトデータを扱うことがあってmv_dataに足し算をしていた。
しかし、特に何も言われなかったので、char*なりuint8_t*なりの型になってるのだろうと思い込んでいた。

が、調べてみるとvoid*型だったのだ。
あれ、C言語ってvoid*型に直接演算することができるんだっけ??


試しに、こういうコードを書いた。

01: #include <stdio.h>
02: 
03: int main(void)
04: {
05:     char str[20] = "abcdef";
06:     void *p = str;
07:     char *s = p + 3;
08:     *s = 'D';
09:     printf("%s\n", str);
10:     printf("str=%p\n", &str);
11:     printf("p  =%p\n", p);
12:     printf("s  =%p\n", s);
13: 
14:     return 0;
15: }

$ gcc --version
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.6) 5.4.0 20160609
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ gcc -Wall -Wextra -o tst voidptr.c
$ ./tst
abcDef
str=0x7fffe31467e0
p  =0x7fffe31467e0
s  =0x7fffe31467e3

やはりwarningも出ない。
昔は出ていたような気がするのだが、自分の記憶は当てにならんし、振り返っても意味が無かろう。


https://stackoverflow.com/questions/3922958/void-arithmetic

`-pedantic`を付けると、警告するそうだ。

$ gcc -pedantic voidptr.c
voidptr.c: In function ‘main’:
voidptr.c:7:17: warning: pointer of type ‘void *’ used in arithmetic [-Wpointer-arith]
      char *s = p + 3;
                  ^
voidptr.c:10:12: warning: format ‘%p’ expects argument of type ‘void *’, but argument 2 has type ‘char (*)[20]’ [-Wformat=]
      printf("str=%p\n", &str);
             ^

なるほど。
-pedanticを付けた方が私の認識に近いといえば近いのだが、そんなにC言語の定義に詳しいわけでもないし、他のコンパイラにする予定も無いのであれば、このまま「知っているが放置する」というのがよいかもしれん。

悩ましいですな。

2018/02/03

[vscode][c/c++]「すべての参照を検索」を使いたい

vscodeを使っているが、コンテキストメニューに「すべての参照を検索」という項目がある。
きっと、関数名を選択して実行すると、うまいことgrepして、呼び出し箇所の一覧を出してくれるに違いない!

そう思って実行するのだが、結果が見つからない、というようなことをいわれてしまう。
あの葡萄は酸っぱいんだ、そう思って忘れようとしていた。

が、放置していてもどうしようもないので、ちゃんと調べよう。
うちの環境は、こうだ。

  • Windows10 64bit(Fall Creator)
  • Visual Studio Code 1.19.3
    • 統合コンソールはWSL
  • C++ Intellisense 0.2.2



まず、vscodeに、拡張機能のC++ Intellisenseをインストール。

https://marketplace.visualstudio.com/items?itemName=austin.code-gnu-global

設定はここに書いてある。
拡張機能単体では動かず、GNU Globalというものがいるそうだ。

https://www.gnu.org/software/global/

ここのDownloadからたどっていって、6.5.6のWin32版をダウンロード。

http://adoxa.altervista.org/global/dl.php?f=win32

zipがとれるので、適当に展開。
PATHに追加するか、settings.jsonに追加すればよいそうだ。
コマンドラインからは使わないだろうから、settings.jsonに追加。

・・・vscodeから「そんな変数はない」という青い波線が表示されるのだが、大丈夫なんだろうか?


ここまでやって、実行してみた。
やはり、結果が出てこない。
そもそも、Globalが呼び出されているのかどうかが分からん。

vscodeのメニューを見ていると、「ヘルプ > 開発者ツールの切り替え」というものがあった。
選択すると、右側にブラウザの開発ツールのようなものが出てきた。
上のタブに「Console」があったので選択すると、実行したと思われるログが出ていた。

image

青い波線が出ていても、ちゃんと読んでるんだ。
そして、呼び出しているが、GTAGSがないとのこと。
`gtags` を実行するとよいらしいし、自動でやってほしいならオプションを付ければよいようだ。


せっかくなので、WSLにapt install globalでglobalをインストールし、gtagsした。
なんか、4つくらいファイルができたので、.gitignoreに追加しておいた。
そして、もう一度実行!

・・・GTAGSのファイルフォーマットが古いと言われた。
WSLにインストールされたのは、5.7.1だ。

こちらを読むと、バイナリをダウンロードできるらしい。
https://qiita.com/akegashi/items/042c659c856d2092c443

$ wget http://launchpadlibrarian.net/301614632/global_6.5.6-2_amd64.deb
$ sudo apt install emacsen-common libc6 libltdl7 libncurses5 libtinfo5
$ sudo dpkg -i global_6.5.6-2_amd64.deb

これで、gtagsしなおして、検索を実行すると、今度は出てきた。