2017/02/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/02/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/02/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