2017年2月27日

[doxy]typedefしたタグ無しstructは、struct扱い

C/C++で書く場合、doxygen形式でコメントを書くことが多い。
Cの場合で悩むのが、structをタグ無しで書いて、typedefして使っている場合だ。
これは、@typedefなのだろうか? それとも@structなのだろうか(Qt形式で書いてるので、@)。

なんとなく@structのような気がするけど、いつも悩むので結果を残しておこう。
doxygenは、Windows版の1.8.13だ。

01: /** @file   test.c
02:  *  @brief  テスト
03:  */
04: #include <stdio.h>
05: 
06: /** @typedef    IntType_t
07:  *  @brief      整数型
08:  */
09: typedef int IntType_t;
10: 
11: 
12: /** @typedef    StructA
13:  *  @brief      構造体A
14:  */
15: typedef struct {
16:     int     value1;     ///< 値1
17:     int     value2;     ///< 値2
18: } StructA;
19: 
20: int main(int argc, char *argv[])
21: {
22:     StructA a = { .value1 = 123, .value2 = 456 };
23:     printf("value1 = %d\n", a.value1);
24:     printf("value2 = %d\n", a.value2);
25:     return 0;
26: }

HTMLの出力。

image

というわけで、タグ無しでtypedefしたstructは、@typedefで書いていたとしても@struct扱いに近くなるのだ。
まあ、doxygenコンパイラもwarningを出しているので、わかりやすいな。

 

ちゃんと@structで書いておくと、briefを出してくれる。
だから、typedefしていたとしてもstructは@structで書こう。

image

2017年2月26日

[py]pywin32をWindows10 64bitにインストール

久しぶりに、pythonを使おうとした。
TCPで通信するアプリを作ろうとして、Twistedというライブラリが簡単そうだったのだ。

 

Linuxではpipでインストールできたのだが、Windowsではそれだけだとダメで、サンプルソースを動かそうとすると「import win32apiが無い」と言われる。
どうやら、pywin32というライブラリもいるらしい。

pyCharmに出てきたので追加しようとするも、エラー。
よくわからんが、Windows専用だからか。。。

検索すると、このページが出てきた。
pypiwin32 220 : Python Package Index
whl?
どうも、wheelという形式のパッケージで、バイナリなんかも入っているらしい。
うちのWindowsは64bitなので、2つファイルがあるうちのamd64の方をダウンロードした。

> pip install pypiwin32-220-cp36-none-win_amd64.whl
pypiwin32-220-cp36-none-win_amd64.whl is not a supported wheel on this platform.

エラー?
platformと書いてあるので、python.exeが32bitなのかと思い、win32の方も試したが、やはりダメだ。
なぜ・・・。

どうも、ファイル名の「cp36」は、python3.6の意味らしい。
うちはまだpython2.7を使っていたので、ダメなのだろう。
そういうplatformか。。。

1つ前のバージョンだとcp27もあるのだが、今さら古いのをインストールしても仕方ないので、sourceforgeからexeファイルをダウンロードしてインストールさせた。
Python for Windows Extensions - Browse /pywin32/Build 220 at SourceForge.net

 

https://twistedmatrix.com/trac/#pubsubserver
これを動かして、telnetで接続し、telnetクライアント側から文字を入力してEnterを押すと、エコーバックされるから、動いているのだろう。
最初、TeraTermで同じことをやって動かず、Windowsのtelnet.exeにしたら動いたのだが、これはEnterを押したときの設定を「CR」にしていたためだった。
「CR+LF」にすると、同じ動きをした。

2017年2月23日

[esp8266][rtos]mbedtlsのconfigは、config_esp.h

やられたな。。。

ESP8266のRTOS版に同梱されているmbedTLSで、デフォルトでは入っていないライブラリがあったので、configで有効に使用とした。
include/mbedtls/config.hでコメントアウトを外して、third_partyに移動し、./make_lib.sh mbedtlsを実行。

ビルドは終わるものの、リンクしてもまだ関数が見つからない。
config.hを見直したが、間違っていない。
では、と該当するソースファイルで#ifdefの内側に#errorを書き、コンパイルされているかどうかを見てみた。
通ってない・・・。
config.hを見てない??

そんなばかな、とconfig.hに#errorを埋め込んだが、コンパイルが通る。
つまり、ESP8266 RTOS版のmbtdTLSはconfig.hで設定していないと言うことだ。。

Makefileを見ると、config_esp.hとのこと。
うん、確かにconfig.hの下にconfig_esp.hがあるね。
書き換えると、通った。
やられたな。。。

[esp8266][rtos]getaddrinfo()してconnect()

知ってるんだぞ、みんなESP8266をArduinoとして使っていることを。
でも、私はRTOS SDK版で使うのだ。

 

昨日の、getaddrinfo()をESP8266 RTOS版で書くと、こういう感じ。
クライアントとしてconnect()したかったので、見つかった順にconnect()する処理も一緒に。

01:     struct addrinfo hints;
02:     struct addrinfo *ainfo;
03: 
04:     //IPアドレス取得
05:     memset(&hints, 0, sizeof(hints));
06:     hints.ai_socktype = SOCK_STREAM;
07:     hints.ai_family = AF_INET;
08: 
09:     int ret = getaddrinfo(M_NODE_ADDR, M_NODE_PORT, &hints, &ainfo);
10:     if (!ret) {
11:         struct addrinfo *rp;
12:         for (rp = ainfo; rp != NULL; rp = rp->ai_next) {
13:             mFd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
14:             if (mFd == -1) {
15:                 printf("fail: sock\n");
16:                 goto ERR_EXIT;
17:             }
18: 
19:             struct sockaddr_in *in = (struct sockaddr_in *)rp->ai_addr;
20:             printf("addr : %s\n", inet_ntoa(in->sin_addr));
21:             ret = connect(mFd, rp->ai_addr, rp->ai_addrlen);
22:             if (!ret) {
23:                 //接続OK
24:                 printf("connected\n");
25:                 break;
26:             }
27:         }
28:         freeaddrinfo(ainfo);
29:         if (rp == NULL) {
30:             goto ERR_EXIT;
31:         }
32:     } else {
33:         goto ERR_EXIT;
34:     }
35: 
36: ERR_EXIT:
37:     ...

M_NODE_ADDRに接続先の文字列が M_NODE_PORTに接続したいポート番号の文字列が入っている。
最初、IPアドレスを見つける目的だけだったのでポート番号のところをNULLにしていて、connect()しても接続に失敗するのでかなり悩んでいた。
「サービス名」と書かれていたので、"http"みたいな名前の分かる文字列じゃないとだめかと思ったけど、"18333"みたいにポート番号を文字列にするだけでも良かったのだった。

 

どのincludeが役に立っているか分からないけど、たぶんこれらだろう。

#include "lwip/sockets.h"
#include "lwip/netdb.h"

機種依存部をヘッダファイルなどに限定できると、Linuxでも同じコードが使えるので実験が楽になるのだ。

2017年2月21日

[c/c++]getaddrinfo()はISO CではなくPOSIX2001か

前の記事で、gethostbyname()ではなく、getaddrinfo()を使った方が良いそうなので、使ってみた。
しかし、gccで-std=c99にするとコンパイルエラーになるのが気になる。

 

c - Why can't getaddrinfo be found when compiling with gcc and std=c99 - Stack Overflow

それはね、getaddrinfoはc99に属さないからだよ、とのこと。
リンク先の資料では、こうなっていた。

[Name]                   [Header]             [Standard]
getaddrinfo netdb.h P2001

リンク先には表が2つあり、[Standard]は、上側がC99など、下側がP90などになっていた。

どうも、C99みたいにPOSIXにも名前があるようで、P09やP2001がそれに当たるようだ。
C99みたいな方は、ISO Cと呼ぶらしい。
だから、上の表はISO C、下の表はPOSIXのようだ。

Man page of GETADDRINFO
こちらには「POSIX.1-2001. getaddrinfo() 関数は RFC 2553 に記載されている。 」と書いてあった。

 

gccはCコンパイラで、-stdはc89とかc99とかの指定をするだけ、POSIXはコンパイルしているLinuxの環境に依存している、と思っていたのだが、そうではないということか。
includeしてリンクするだけだから、関係ないと思っていたのだよ。
回答にあるように、「-std=c99」ではなく「-std=gnu99」にするとコンパイルできた。

 

Standards - Using the GNU Compiler Collection (GCC)

-std=c99は単にC99で、-std=gnu99は「C99 with GNU extensions」とのことだ。
GNU拡張というと、GNUの独自言語仕様ばかりに目が行ってしまうが、こういうところもGNU拡張なんだな。

 

まあ、ESP8266みたいなフリースタンディング環境だと、こういうのもどっからかライブラリを持ってくるので、あまり関係ない。
RTOS SDKだとlwip_getaddrinfo()が使えるし、マクロでgetaddrinfo()でも使えるようだ。
そんなことをいえば、別にgethostbyname()を使ってもよいのだけどね。。。

[c/c++]gethostbyname()は過去のものだったのか

いつもESP8266で作るときはローカルでサーバを立てていたのでIPアドレスを直接書いていたのだが、名前解決するようにしておいたほうがよいよな、と思った。
RTOS SDKを使うようにするつもりなので、Linuxで試しておこう。

 

確か、名前からIPアドレスを取得するのは、gethostbyname()とかいう関数だったよな、と検索した。
Man page of GETHOSTBYNAME

出てきたものの、説明の1行目にこう書いてあった。

関数 gethostbyname*(), gethostbyaddr*(), herror(), hstrerror は過去のものである。
アプリケーションは、代わりに getaddrinfo(3), getnameinfo(3), gai_strerror(3) を使用すること。

なんですってー!
おじさん、驚愕である。


gethostbyname()を使ってもよいけど、今から調べるのに過去の人になる必要もないので、getaddrinfo()を調べた。

//https://linuxjm.osdn.jp/html/LDP_man-pages/man3/getaddrinfo.3.html
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

int main(int argc, char *argv[])
{
    int ret;
    struct addrinfo hints;
    struct addrinfo *ainfo;

    //IPアドレスを知りたいだけなんだ!
    memset(&hints, 0, sizeof(hints));
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_family = AF_INET;

    ret = getaddrinfo("www.yahoo.co.jp", NULL, &hints, &ainfo);
    if (!ret) {
        struct addrinfo *rp;
        for (rp = ainfo; rp != NULL; rp = rp->ai_next) {
            struct sockaddr_in *in = (struct sockaddr_in *)rp->ai_addr;
            printf("addr    : %s\n", inet_ntoa(in->sin_addr));
        }
        freeaddrinfo(ainfo);
    } else {
        printf("ERR: %s\n", gai_strerror(ret));
    }
    return 0;
}

$ gcc -Wall -o tst getaddrinfo.c
$ ./tst
addr    : 182.22.40.240
addr    : 182.22.59.229
addr    : 182.22.63.230
addr    : 183.79.111.124

出たけどさ、やりたいことに比べて実装が多くないかい?
まあ、IPv6やらなんやらあるので、こうなったのかもしれんが。

 

これをコンパイルしているときに気付いたのだが、gccで「-std=c99」オプションを付けると、いろいろ見えなくなってコンパイルエラーになるのだ。
struct addrinfoが見えなくて、hintsがわからんとか言われるし。
C99でチェックが厳しくなったとかじゃないし、なんだろう?
またBash on Windowsだからとかだろうか?

2017年2月20日

[c/c++]丸められた誤差

これくらいは大丈夫かな、と思っていたことが、大丈夫じゃないことがある。

01: #include 
02: #include 
03: 
04: int main(int argc, char *argv[])
05: {
06:     uint64_t val = 0.006 * 100000000;
07:     printf("%llu\n", (unsigned long long)val);
08: }

600000
よし。

01: #include 
02: #include 
03: 
04: int main(int argc, char *argv[])
05: {
06:     uint64_t val = 0.007 * 100000000;
07:     printf("%llu\n", (unsigned long long)val);
08: }

700000
よし。

01: #include 
02: #include 
03: 
04: int main(int argc, char *argv[])
05: {
06:     uint64_t val = 0.008 * 100000000;
07:     printf("%llu\n", (unsigned long long)val);
08: }

800000
よし。

01: #include 
02: #include 
03: 
04: int main(int argc, char *argv[])
05: {
06:     uint64_t val = 0.009 * 100000000;
07:     printf("%llu\n", (unsigned long long)val);
08: }

899999
あー、だめかー。


いやあ、コンパイルの段階で解決するものは、このくらいは大丈夫だろうと思っていたのだ。

01: #include 
02: #include 
03: 
04: int main(int argc, char *argv[])
05: {
06:     uint64_t val = 0.009 * 100000000 + 0.5;
07:     printf("%llu\n", (unsigned long long)val);
08: }

900000
これでいいや。

2017年2月19日

[git]間違ったbranchをcloneしていないか心配

未だに、gitの操作をコマンドラインでうまくやれない。。。
GUIでやることが多いのだが、クラウド上で操作することもあるので、もうちょっと慣れておかないといかん。

 

まずcloneだが、決まったbranchを取ってきたいことがある。
その場合、-bを付ければよいことはわかった。
ただ、これでbranch名を打ち間違えたりして、実は違うものを(例えばmaster)を取ってきて、勝手に自分が思っているbranch名を作成してしまった、という現象が起きたりしないだろうか?

試そう。


ここを使わせてもらおう。
https://github.com/bitcoin/bitcoin

branchはこういう状況のようだ。
image

まずは、存在しないbranch名でcloneしてみよう。

$ git clone -b hehehe https://github.com/bitcoin/bitcoin.git
Cloning into 'bitcoin'...
fatal: Remote branch hehehe not found in upstream origin

おお、失敗してくれた。
念のため、「0.13.1」や「0.13.」なども試したが、失敗してくれる。

 

では、存在する名前「0.13」でcloneして、branchを確認しよう。

$ git branch -a
* 0.13
  remotes/origin/0.10
  remotes/origin/0.11
  remotes/origin/0.12
  remotes/origin/0.13
  remotes/origin/0.14
  remotes/origin/0.8
  remotes/origin/0.9
  remotes/origin/HEAD -> origin/master
  remotes/origin/master

うーん、0.13が現在のbranch名というのはわかるけど、origin/0.13と同じかどうか不安だ。
もし、ここでcheckoutさせて、origin/0.14を「0.15」という名前にできるだろうか?

$ git checkout -b 0.15 origin/0.14
Branch 0.15 set up to track remote branch 0.14 from origin.
Switched to a new branch '0.15'

できそう。。。

$ git branch -a
  0.13
* 0.15
  remotes/origin/0.10
  remotes/origin/0.11
  remotes/origin/0.12
  remotes/origin/0.13
  remotes/origin/0.14
  remotes/origin/0.8
  remotes/origin/0.9
  remotes/origin/HEAD -> origin/master
  remotes/origin/master

うーん、これではcheckoutしたbranchと、ローカルで付けたbranch名の対応がわからんな。
しかし、Git Extensionsで見ると、ちゃんとわかっているようだ。

image

 

これか。
追跡ブランチ (tracking branch) というブランチが何なのか調べた - snowlongの日記

$ git branch -vv
  0.13 59c37ae [origin/0.13] Uses built-in byte swap if available (Apple) and if bswap_XX is undefined.
* 0.15 2afefea [origin/0.14] boost: remove iostreams includes

今のbranchはアスタリスクがついている2行目の方だから、これで「0.15は、実はorigin/0.14と関連付けられていますよ」という情報が読み取れるということになるか。

helpには、upstream branchの名前を出力する、と書いてあった。
あれ、tracking branchじゃないの?
ローカルリポジトリから見ると、上流のbranch、ということか??
Git で「追跡ブランチ」って言うのやめましょう - Qiita
Gitのリモートブランチと追跡ブランチは違うよ
過去の経緯などもあり、混乱の元になっているようだ。。。

あまりgitを使っていないからかもしれんが、cloneするときと同じく、同じbranch名でしかcheckoutできないようにしておいた方がよかったんじゃなかろうか。
あるいは、オプションを指定しなければ同じbranch名でcheckoutされる、とか。
いや、もしかしたら、普通はそうするとか?

$ git checkout 0.11
Branch 0.11 set up to track remote branch 0.11 from origin.
Switched to a new branch '0.11'
$ git branch -vv
* 0.11 0cd4fb6 [origin/0.11] qt: Final translation update on 0.11 branch
  0.13 59c37ae [origin/0.13] Uses built-in byte swap if available (Apple) and if bswap_XX is undefined.

なんだよー、-b付けずにremoteと同じ名前を指定したら、自動的にupstreamも同じ名前のものになってるじゃないか。
では、checkoutでremoteに存在しない名前を指定したらどうなる?

$ git checkout 0.17
error: pathspec '0.17' did not match any file(s) known to git.

エラーなんだ。
私の場合は違うbranch名を付けたいわけでもないから、普段は-bを使わずにやったほうがよいかも。

 

ついでに、気になっていた「remotes/origin/HEAD -> origin/master」も調べよう。
いや、さっきの追跡リポジトリも、branch -aみたいなコマンドで、

$ git branch -??
* 0.15 -> [origin/0.14]

みたいに出てくるのだと思っていたのだよ。
今は[origin/0.14]にいるのだから、HEADも0.14になるのかと思ったが、そうではないのか。
むむ、revertするときにHEADを使ってたけど、別branchで使っているとまずいんだろうか?

こちらによると、HEADは「現在作業中のブランチを指す」となっている。
3.1 Git のブランチ機能 - ブランチとは
それなら、思っているのと同じだ。
でも、それだったらbranch -aで出てくるのも変わっていないと変な気がするが。。。

もう一度、出力を確認しよう。

$ git branch -a
  0.13
* 0.15
  remotes/origin/0.10
  remotes/origin/0.11
  remotes/origin/0.12
  remotes/origin/0.13
  remotes/origin/0.14
  remotes/origin/0.8
  remotes/origin/0.9
  remotes/origin/HEAD -> origin/master
  remotes/origin/master

ああ、自分のHEADじゃなくて、origin/HEADだ。
自分のところのgithubで確認してみよう。

https://github.com/hirokuma/esp8266_httpget

まず、普通にcloneしてbranchを見る。

$ git branch -a
* master
  remotes/origin/HEAD -> origin/master
  remotes/origin/master
  remotes/origin/opensdk

githubの設定画面で、Default branchをorigin/opensdkに変更。
そして、あらたにcloneしてbranchを見る。

$ git branch -a
* opensdk
  remotes/origin/HEAD -> origin/opensdk
  remotes/origin/master
  remotes/origin/opensdk

おお、変わった。

普通のHEADは現在のリポジトリの先頭だけど、remote/HEADはremoteがデフォルトにしているリポジトリということで良いのかな。
ただ、デフォルトを変更してからローカルにfetchしても変わらなかったのだ。


おまけ。

gitで~/.ssh/id_rsaではないファイルを指定したい場合で、コマンドラインだけで済ませたいなら、ssh-agentを使うとよいらしい。

git - Specify private SSH-key to use when executing shell command with or without Ruby? - Stack Overflow

[ubuntu]apt updateが終わらんのはipv6か

うちではXubuntu 16.04を使っているのだが、どうにもapt updateが終わらない。
最初は進んで、security.ubuntu.comのところで0%のまま進まない。

URLの後ろに、コロンで区切られた数字が出ていた。
たぶんIPv6のアドレスだろうから、「apt ipv6」で検索すると、IPv6だと遅いのでIPv6でやる、という項目が出てきた。
けっこういるんだ。。。

今回は、こちらを見て -oオプションでやってみた。
apt-get updateがIPv6を見に行って遅い件 - Tizen には moe ていない blog

うん、速い。
もうデフォルト設定にしておきたいので、こちらを見て設定だけやっておいた。

Convince apt-get *not* to use IPv6 method - Unix & Linux Stack Exchange

2017年2月18日

[勉]go (2)

休日だったので、goのことを少し調べることにした。
普通に調べるとたくさん出てきそうなので、組込み分野で使えるかどうかだけ見てみよう。

組み込み go - Google 検索

・・・検索キーワードが悪かったようだ。
「組み込み型」の方が色濃く出ている感じがする。

コンピュータ言語の仕様はともかくとして、内容のことを検索すると、好きな人は強く推すし、嫌いな人はかなりけなすので、なかなか状況が読み取りにくい。
大半の人は状況に合わせて使っていると思うので、そういう人はあまりそういう感想を書かないのだろう。
私も、使っている言語のことは書くけど、めったに使わない言語については良いも悪いも書かないので、同じようなものだ。


そもそも「組み込みで使える」とは何なのかを決めていなかったのが、よくなかった。
メモリ空間を自由に操作できる、かな?
となると、まずはポインタか。

そういえば、むかーし受けたセミナで「C言語は値渡ししかできません」という説明をされ、心の中で「ポインタでの参照渡しがあるやん!」と反論したことがあった。
でも、ポインタ渡しはアドレス値を渡すということなんですよ、と説明され、ああそうか、と納得した次第だ。
C++での参照渡しはそれっぽい気がするけど、結局ポインタでアドレスを渡しているのとあんまり変わらないだろうし、そもそも「何かを渡す」というのは、値を全部渡すか、値を指している場所を渡すかしかないと思うので、どういう言語もそんなに意味は変わらないと思うようになった。

 

と、昔話はここまでとしよう。

C++プログラマ向けGo講座 - golang.jp

>ポインタはありますが、ポインタの演算はありません

そうなのか。
場所を教えるのだから計算もできてよいやん、と思うけれども、計算ができてしまうと予期しない場所へのアクセスも許容することになるのだろう。
なんだかんだいって、Cで一番不具合が起きやすいのはメモリアクセスのところだろうから、そこに対策を打つのは自然ななりゆきだ。

それだったら、もうC#でいいんじゃないのか、という気もしなくはない。
C#にもgolangにも精通していないのでなんともいえんが、C#もアドレスが扱えるし、unsafeみたいな逃げ道もあったような気がする。

 

アドレスの扱いについて大きく見ると、アセンブラ/C/C++と、それ以外の言語、みたいな分かれ方になってしまうのだろうか。
というほど他の言語に詳しくないのだが、OSが管理するアドレスを自由勝手に扱える言語は、そんなに多くない気がする。

OSがメモリを管理しないのであれば、誰かが管理することになり、それが言語自体であっても不思議ではない。
もしGo言語が、スタンドアローン環境というか、誰も管理する人がいない状況でも動くというセットがあるのであれば、デバイスドライバのようなものも作ることができるのかもしれない。