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

2017/02/18

[勉]go (2)

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

組み込み go - Google 検索

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

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


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

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

 

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

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

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

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

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

 

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

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

2017/02/17

[c/c++]関数のvoidキャストでignoring returnは回避できない

昔・・・といっても半年程度だが、その頃に書いたARM向けのソースを改造しようとした。

Linux向けに書いていたから、コンパイラをarm-linux-gccをgccにすればよかろう、とコンパイルしたものの、エラーになった。

ignoring return value of 'read', declared with attribute warn_unused_result [-Werror=unused-result]

read()を使っているところがあり、そこはデータの読み捨てだったので、結果が正しかろうとどうだろうと気にしていなかったのだ。
だから、

(void)read(fd, dummy, sizeof(dummy));

のように書いていたのだが、戻り値を(void)してもダメなのだな。。。

splintなんかはprintf()でも警告が出ていたのだが、(void)でキャスト回避できたのでそうしていたが、いやはや時代は厳しくなったものだ。
半年前はコンパイルできていたのは、たぶん提供された開発環境がそんなに新しいものではなかったからだろう。


warning: ignoring return value of ‘int scanf(const char*, ...)’, declared with attribute warn_unused_result - 簡潔なQ

全部の戻り値がある関数で出るのではなく、attributeで指定しているものだけが出るようだ。
まあ、エラーメッセージもそう書いているしな。

回避は、チェックするか、コンパイルオプションで逃げるか。
ここだけのためにコンパイルオプションを指定するのもしゃくだが、不要なチェック処理を追加するのも腹立たしい。

int ret = read(fd, dummy, sizeof(dummy));

チェックしなくても、誰かに代入するだけで安心してくれるようだ。
まあ、あとはコンパイラかリンカが最適化して捨ててくれるだろうから、これでよいか。

2017/02/15

[c/c++]テストの分割(fff)

書くネタが無いときは、だいたい実装作業ではなく、テストをやっているような気がする。。。

 

私はCで書いたライブラリのテストをするとき、fffを使うことが多い。
meekrosoft/fff: A testing micro framework for creating function test doubles

gtestも入っているので、ASSERT_EQ()なんかを書いてそれっぽく(?)できる。
使いこなしているわけではないが、スタブというかドライバというかモックというか、そういうのを準備する手間が楽になるだけでもありがたい。


今までうまくできていなかったのが、テストのソースファイルを分割する作業だ。
テストの関数は、比較的大きくなりがちなので、1ファイルに対してテストを1つのファイルにまとめると、かなり長くなってしまう。
元のソースこそ分割すべきなのか?と悩まないでもないが、せっかくのstatic変数をファイル分割するだけのために公開するのも避けたい。

私は、テスト対象のstatic変数も見たいと思うことがあるので、ソースファイルごとincludeさせている。
https://github.com/hirokuma/fff_examples/blob/master/test_func.cpp#L12
邪道といえば邪道だが、テストだからいいと思っている。
こうしていると、テストファイルを分割するとリンク時に多重定義が発生してしまう。

それに、モック関数もある。
ファイルを分割しても、モック関数として同じ名前のものはしばしば使うことになる。
そこにFAKEマクロを使ってそれぞれのテストファイルにモック関数を作ると、これはこれで多重定義になる。
(namespaceの中に入れてもダメだったけど、なんでだろう?)


そこでようやく思い至ったのが、テスト関数もincludeしてしまえばいいやん、ということだ。
そして、モック関数もincludeする。
見た目上だけテストファイルを分割するけど、実質は1つのソースファイルとしてコンパイルしてしまえ、ということだ。

格好は悪いが、テストだから、あまりそこに力を入れたくない。
懸念は、本体のソースファイルを変更してもテストでmakeしてビルドされないというところだったが、依存関係を書いてしまえばよい。
その編を解決しようとしていたのが、数日前の依存関係のパスの書き方についてだ。
http://hiro99ma.blogspot.com/2017/02/ccmakefiledepend.html

 

うちでは、gcc+fff+gtest+lcovという構成でテストをすることが多い。
lcovはHTMLでカバレッジを見るだけだが、目視できるところがよい。
「100%にしてやるぞー!」と思ってしまい、やらなくてもよいテストまでやってしまいがちなところが難点だが、まあこれは私の課題だな。

2017/02/14

Azure無料試用版の最終日

ごめんなさい、今日はネタが思いつかなかったです。。。

ちょうどAzure無料試用版の最終日なので、証拠を残しておきます。

image

 

ちょこちょこと、Ubuntu VMを動かしたりしましたが、一人で家で使う分にはVirtualBoxなどの方が小回りがきいてよいかも。
Azureがどうとかではなく、私だと使い道がなかった、ということだ。

image

 

複数箇所からアクセスするなら、家にサーバを立てて公開して電源を入れっぱなしにするのとどっちがよいか、というところですかね。
古いPCを利用するならまだしも、新しいのを買ってきてサーバにするのであれば、悩むかもしれん。
だって、PCがずっと動いていると、そこそこ音がするし、夏は暑くなるし。

 

覚えていたら、明日・・・というか今日の23時過ぎからVMにログインしして、時間切れになったらどうなるかを見てみたいところ。
あー、忘れるだろうなぁ。


2017/02/14 22:14

忘れていなかったので、SSHでログイン・・・できませんでした。
既に、停止していました。

image

こうなるのねぇ。
日本時間の今日じゃなくて、実は朝の9時には停止していたのかもしれない。
まずはVMを削除してしまおう。

無料でも、制限はありますがこれだけ使えるそうです。
https://azure.microsoft.com/ja-jp/free/pricing-offers/

ネットでお仕事してないので、ピンとこんのです。。。

2017/02/11

[c/c++]フリースタンディング環境

Cで書いていて、「これって、この環境で使える関数だっけ?」と考えないといけないことがときどきある。

たとえば、malloc()。
リンカで動的に使用するメモリを確保していなければ呼び出せても使えないし、malloc()自体を自分で定義しないといけないということもある。

そういう「標準じゃない」という環境を指す言葉があったのだけど、なんだっただろうか・・・。
オライリーの『Cクイックリファレンス』を読んでいて思いだした。
「フリースタンディング環境」だ。

 

本では、p.110などに説明がある。
詳細は本を読んでいただきたいが、「ホスト環境」は標準ライブラリがフルセットで使用でき、フリースタンディング環境は最小機能だけが使える、となっている。

最小機能ってなんだよ、ということになるのだが、p.276に使用できるヘッダが書かれている。
9つだ。
ただ、これも9つしか使えないわけではないし、この9つも必ずフル機能が使えるかといえば、そうでもないのだろう。
例えば、NordicのnRF51822みたいにOSが載っていない環境でgccを使っていたが、stdlib.hなんかを使った気がする。

 

一番わかりやすいのは、main()で始める必要がない、というところか。
混乱するし、他の名前を付ける理由もないからだいたいmain()にするとは思うが。

ただ、関数コールせずにジャンプで呼ばせることは多いかもしれん。
だって、main()を抜けて何か処理をさせたいわけでもないし、そもそも何か処理をしたいならmain()の最後に書いておいた方が自由度があるので、関数コールしてスタックを消費したくないのだ。

2017/02/10

[c/c++]Makefileのdependは名前一致なのか

久々にMakefileを使って、ちゃんとビルドさせようとしている。
何度やっても慣れないMakefileだ。。。

 

Makefileに慣れていないので、ほどほどに応用が利きそうなMakefileのテンプレートを作っておき、それをコピーして使い回している。
automakeなんかもやろうとしたけど、うーん、よくわからんかったのだ。
今は、Nordicのプロジェクトに入っていたMakefileを改造して使っている。

私が見てきたMakefileでは、コンパイルする対象の書き方が2パターンあった。

  • オブジェクトファイル名(.o)を列挙して、コンパイル時に.c/.cppに変換
  • ソースファイル名(.c, .cpp)を列挙して、target指定用は.oに変換

この辺は、オブジェクトファイルの置き場所や、Makefileを分散して書くかどうかというのと関係しそうな感じはする。
私は、オブジェクトファイルは1箇所にまとめて、Makefileも1ファイルで済ませたい。
ただ、ソースファイルはディレクトリごとに分かれていたりするので、ディレクトリを含めてソースファイル名を書いている。
NordicのMakefileもそうなっていたので、私のよりも出来が良いから使わせてもらっている。

 

構成は、こういう風になっている。

  • OBJECT_DIRECTORY : オブジェクトファイル置き場
  • TARGET_SRC : ビルド対象のソースファイルたち
  • OBJECTS : オブジェクトファイル名たち

TARGET_SRCは自分で記入し、OBJECTSは変換して作るようになっている。

SRC_FILE_NAMES = $(notdir $(TARGET_SRC))
OBJECTS = $(addprefix $(OBJECT_DIRECTORY)/, $(SRC_FILE_NAMES:.cpp=.o) )

もちろん、ビルドなんかは普通に動作する。


で、ここからが本題だ。

参考にしたMakefileは、依存関係を自動生成するようになっていなかった。
だから、ヘッダファイルを変更してもビルドしてくれず、cleanしてmakeしなおしていた。

こういうときは、makedependか、-MMとかだったな、と思って過去Makefileを探したところ、こういうのが見つかった。

.Depend:
    $(CXX) $(CXXFLAGS) -MM $(SRCS) > .Depend

include .Depend

ああこれこれ、と名前だけ変更して追加したのだが、動作が変わらない。
.Dependファイルを見ると、依存するファイル名の方はソースファイルやヘッダファイルにパス名が入っているものの、最初に各オブジェクト名(依存されている方だから、被依存?)の方はオブジェクト名が載っているだけで、OBJECT_DIRECTORYの場所になっていないのだ。

探すと、出てきた。
GCC makefile dependency generation path - Stack Overflow
-MTに続けてパス付きでオブジェクトファイル名を書いておけば、それがそのまま載ってくるようだ。
書き方の例もあったので、foreachするやつを作った。

 

が、全然効かない。
.Dependファイルは、ちゃんとパス付きでオブジェクトファイル名が載っているし、lsで見てもオブジェクトファイルは存在する。
なぜだ。。

もう一度.Dependを見てみると、オブジェクトファイル名は「obj/./test.o」のように、1つカレントディレクトリが挟まっていた。
これは、ソースファイルが「$(XX)/test.c」のように、ディレクトリ名が変数になっていて、それがカレントディレクトリなので「XX=.」としたからだった。

試しに外して「obj/test.o」の形式に書き直すと、効いた。
そうか、そういうしくみだったのか。
今回はBash on Windowsなので、その影響もあるかもしれんが、気をつけておこう。

2017/02/09

[c/c++]0を代入したグローバル変数は初期値ありと見なされないようだ

以前から気になっていた、Cのグローバル変数を0で初期化したときの扱いだ。

01: #include 
02: 
03: static int a;
04: 
05: int main(int argc, char *argv[])
06: {
07:     printf("a=%d\n", a);
08: }

こう書いても、

01: #include 
02: 
03: static int a = 0;
04: 
05: int main(int argc, char *argv[])
06: {
07:     printf("a=%d\n", a);
08: }

こう書いても、普通のCコンパイラならaはmainが始まる前に0で初期化されるようにビルドする。
「普通は」と書いたのは、組み込み環境で、ものすごくメモリが少なかったら、そういうmainが始まる前の処理を呼ばないようにしてしまうという選択ができなくもないからだ。
コンパイラがやると言うよりも、実装した人がいじくることが多いだろうから、そういう環境は省略だ。

 

ただ、今回の話は、そのmainが始まる前の部分になる。
プログラムを実行すると、C言語ではmainから始まると表現しているが、グローバル変数なんかはmainに呼ばれる前に初期化されるので、当然mainより前が存在する。

その、main前の処理(C Runtimeとか呼ぶのか?)の中であれこれやっていたのだが、グローバル変数の初期化も行われていた。
やるのは、

  • 初期値無しグローバル変数を0でクリア
  • 初期値あり変数に初期値代入

だ。
その実装がどうなっていたか覚えていないが、memset()みたいなアセンブラ処理と、memcpy()みたいなアセンブラ処理があったような気がする(まだC89やANSI Cより前のコンパイラだった。。。)。


さて、昔話はここまでにして、気になっているのが何かを説明する。

上に2つソースを載せたが、どちらもaは0になる。
なるのだが、上は初期値無しなのだが、下は初期値あり(0)とみなすのか、0になるのがわかっているから初期値無し扱いなのか、だ。
初期値あり扱いになると、初期値をROMに持っておいて、それを初期値あり領域にコピーするような作りになってしまうので、できれば0初期値の場合は初期値を持っていないという扱いにしてくれるとうれしい。

じゃあ、最初から初期値を書かなければよいではないか、となるのだけど、初期値がある変数をずらずら書いているのに、0のところだけ何も書かないと、すっきりしないのだ。
そう、気分の問題である。
ソースファイルって、リズミカルに書くようにして、リズムが悪いところにバグがあるかも、というチェックができたらうれしいじゃないか。

 

■初期値無し
$ gcc -o tst -O0 -Xlinker -Map=map.map test.c
$ size tst
   text    data     bss     dec     hex filename
   1226     560       8    1794     702 tst

■初期値0
$ gcc -o tst -O0 -Xlinker -Map=map.map test.c
$ size tst
   text    data     bss     dec     hex filename
   1226     560       8    1794     702 tst

よかった、同じようだ。

試しに、初期値として3を代入させた。

text    data     bss     dec     hex filename
1226     564       4    1794     702 tst

sizeコマンドでは以下が出力される。

    • text : コード
    • data : 初期値あり変数
    • bss : 初期値無し変数

初期値があると、ちゃんと初期値あり変数の方に4byteが移動している。
じゃあ、初期値自体はどこにあるのかというと、textのはずだ。
textに変化が見られないのは、alignmentの波に取り込まれてしまったからだろうか。。。

 

ただ、これはグローバル変数にべたっと書いた場合で、構造体の場合はまた事情が違うだろう。
構造体の中のメモリが、初期値ありとなしで別になっていたら困るからね。。。

2017/02/07

[c/c++]未だに「配列のポインタ」「ポインタの配列」に怯える

愚痴だ。
こう何十年もC/C++を使っているにもかかわらず、「配列のポインタ」「ポインタの配列」が、どっちがどっちかわからなくなる。
ネットで説明が出てくるので、困ったときには調べれば良いのだが、どうも身になっていない。

 

ポインタの配列は使うことが多いと思う。
文字列の配列なんかは、ポインタの配列扱いしてよいだろう。

const char *STRINGS[] = {
  "Hello", "my", "name", "is", "Taro", "Hara"
};

あまり意識したことがないのだが、この「*」は、const charの方にひっついているはずだ。

for (int lp = 0; lp < sizeof(STRINGS) / sizeof(STRINGS[0]); lp++) {
    printf("%s\n", STRINGS[lp]);
}

これで全部の文字列が出力されたので、まあよかろう。

 

優先順位が心配だから括弧で強制してしまえ!、という技が、型については使えないのが残念だ。
これができれば、優先順位を覚えていなくてもよいのだが。。。コンパイルエラーだ。

(const char *)STRINGS[] = {
  "Hello", "my", "name", "is", "Taro", "Hara"
};

だから、逆に考えて、括弧を付けてコンパイルエラーになる方が優先順位が高い、と覚えれば良いのか。
つまり、「ポインタの配列」の方が優先順位が高いことになる。

 

ポインタの配列を関数の引数にしたいことがあるけど、ダブルポインタにキャストして渡している。
他に方法が思いつかないのよねぇ。


では、括弧を付けられる方を見ていこう。
こっちが「配列のポインタ」のはずだ。

const char (*STRINGS)[];

でも、配列って、そもそもポインタと同じ扱いなんじゃないのか?
なんでわざわざ「配列のポインタ」なんてことにしないといかんのだろう。

const char a1[] = "Hello";
const char (*STRINGS)[] = &a1;
printf("%s\n", *STRINGS);

一応、これで動く。
例として意味は無いので、何か有効な使い道を思いつけば良いのだが、使い道が思いつかないから普段使わないのだ。

 

でも、&a1って、a1と同じ値だよね?

printf(" a1 = %p\n", a1);
printf("&a1 = %p\n", &a1);
a1 = 0x7fffe8c5b100
&a1 = 0x7fffe8c5b100

まあ、配列はアドレスに展開されるから、それに&をつけても同じアドレスになりますわな。
だからといって、代入するときに&を取り外すとコンパイルエラーになる。
数字の問題じゃなくて、文法の問題だからだ。
レジスタのアドレス値が分かっていたとしても、ポインタとしてキャストして代入しないとエラーになるのと同じだ。


これは、なんだろう?

const char (*STRINGS[]);

gccだと「invalid initializer」になるので、文法エラーじゃなくて、初期化子の書き方が間違っている、といっているような気がするのだが。。。

const char (*STRINGS[]) = {
    "Hello", "my", "name", "is", "Taro", "Hara"
};

ちっ、そんだけかい。

[c/c++]メモリのフラグメンテーションを減らしたい

久々に、Cで実装している。

今回、アプリから受け取ったデータをプロトコルに変換するようなライブラリを作っている。
アプリのデータサイズは想定できないし、プロトコル長の最大も決めづらい。
また、いくつプロトコルデータをアプリが持ちたいかも決められない。

となると、ヒープを使うしか無かろう。
malloc/freeだ。
あー、苦手なのよねぇ。。。

C++だとデストラクタでメモリ解放する、ということもできそうだが、Cだと思いつかない。
メモリの管理だけのためにまたメモリの使用量を増やすというのは、やりたくない。


まあ、そういうのは私が解決せんといかんことだろうが、別の点で気になることがある。
メモリのフラグメンテーションだ。

10年前、いや、もう少し前になるだろうか。
今よりも多少冴えていて、体力もあった私は、C++で組み込みアプリを実装していた。
画像データを扱ったり、画像をファイルに変換したりしていたので、new/deleteしていた。
デストラクタでメモリ解放漏れのチェックなんかもして、そういう方面は問題なかろうと思っていた。

が、だ。
テストのため、帰る前に自動で処理するようにしておき、翌日見に行くと、どうも処理が遅いような気がする。
気のせいかと思って、土日に連続運転させて月曜日出社すると、1秒もかからずに終わっていた処理が、分単位でスローモーションのように動いていたのだった。。。

メモリの解放漏れなどないのに!?と思ってあちこちの人に聞いたところ、メモリのフラグメンテーションではないか、という意見があった。
試しに、画像処理のメモリを毎回new/deleteしていたところを、最初にnewしたまま使い回すように変更したら、発生しなくなった。

いやぁ、そこそこ組み込みの世界を理解してきたと思い込んでいただけに、衝撃でしたわ。
Linuxだと、メモリが分断されても、うまいことOSがつながった大きい領域に見せかけて使うことはできるのだけど、あくまでソフトウェアとしてつながって見えるだけで、分断された部分を使う処理の負荷が大きくなったとか、そんなことだったと思う。
Linux 2.18とか、そのくらいだったと思うので、今はまた事情が違うかも。

 

それ以来、ヒープメモリの確保と解放に怯えるようになりましたとさ。
めでたしめでたし。


Boostには、メモリプールというやつがあって、そういうフラグメンテーションが置きにくいようにできる、というのを読んだ気がする。
やったことないので、リンクだけ載せておこう。

Chapter 4. Boost.Pool

2017/02/05

[c/c++]mallocと代入の狭間で

可変長のバッファを作ろうとした。
Cなので、バッファ長も一緒に管理したいので、ありがちな構造体にした。

struct {
  uint8_t   len;
  uint8_t   *buf;
};

まあ、先頭アドレスとサイズがわかればよいので、こうすることが多いだろう。

 

さっきまで、malloc()して可変のバッファを作っていたのだが、ときどきソースコードに埋め込むバッファも使うことがあった。

const uint8_t DATA[] = {
  0xaa, 0xbb, ....
};

値を変更する予定がないので、constを付ける。
付けなかったら、初期値ありのRAMデータになるので、ROMにもRAMにもメモリがいるようになるので、constは付けておきたいところ。

 

が、constポインタのものを非constポインタに代入するというのは、やはりよろしくない。
では、これをwarningするのをキャストで回避すべきか?
そこは悩ましいところだ。
今は自分で分かって実装しているから良いものの、何か事情が変わってしまうと、安全では無くなるかもしれない。

じゃあ放置するか?
そうすると、warningがうっとうしい。
警告はしてほしいけど、今は私が分かっているから警告してほしくない、という、コンパイラには理解できない状況だ。
もし、コンパイラがスルーすると、タイミングによっては「なんで警告しないんだ!」と私が怒るかもしれない。

 

では、構造体の方をconstポインタにすれば良いかというと、そういうわけでもない。
今度はfree()の方で警告されてしまう。
free()自体はポインタ先を書き換えることはないのだろうが、free()するのはmalloc()したところであり、malloc()はconstポインタを返さないので、free()の引数がconstポインタなのはおかしい、ということか。
まあ、メモリを解放してほしいのに、constもなにもあったもんじゃなかろう。


まだ、これについて私の中で解決ができていない。
標準関数を優先して、自分では書き換えないように意識し、constポインタを非constポインタに置き換えて使う、というところか。

回避しているだけよねぇ。。。
でも、私が使う場合にはコンパイルレベルで解決してほしいところなので、こうなってしまいそうに思う。
RAMに空きがあっても、crtなどで初期値をRAMに転送する、などと思ってしまうと、実装の危険があったとしてもキャストしてしまいそうだ。
だって、ROMに書込もうとしたら、OSがあっても無くても、だいたい教えてくれるだろう?

typedef struct {
  uint8_t len;
  uint8_t *buf;
} buf_t;

const uint8_t DATA[] = { 0, 1, 2 };
const buf_t buf = { sizeof(DATA), (uint8_t *)DATA };
buf.buf[0] = 5;

Linuxだと、このくらいは動いてしまうし、Segmentation Faultなどが発生しないのだ。
まあ、文句は言えないのだけど、定数値がコード領域に置かれて、コード領域へのメモリ書込みで例外が発生するのだったら、実行時に例外が発生するかと思ったのだ。


構造体の構成は同じで、ポインタにだけconstをつけた。

struct {
    uint8_t len;
    const uint8_t *buf;
};

そして、値を変更しない関数はそっちを引数に取るようにした。
constが付いていないデータを使いたいなら、こっちの構造体でキャストして使う。
キャストで回避するしかないのだが、constが付く方にキャストするのだったら、まだよいのではなかろうか。

 

だいたい、メンバにポインタを含んだ構造体にconstを付けても、そのメンバがconstになるわけではないから、もうこういう手段しかないような気がする。
しかし、構造体のメンバが構造体で、そっちにもポインタがあったらどんどんと連鎖していってしまう。。。

かといって、自動的にconstが内部メンバに波及してしまうという言語仕様になってしまうと、それはそれで自由度がなくなる。
案外、gcc拡張とかで「波及的const」なんてものがあるのかもしれん。ないか。

__attribute__(const)とか__attribute__(pure)で変更しないことを表せるかと思ったが、関数内で引数の構造体メンバが指している先を書き換えてもエラーにならないし、実行しても動いた。
単に最適化だけの話か。
結局、運用でカバーしよう、で終わってしまいそうな気がする。

2017/02/04

[bow]valgrindがBash on Windowsでうまく動かない

久々にCで作っているのだが、malloc()なんかを使うので、メモリリークが心配。。。
そんなときは、valgrindだ!

という知識はあるものの、使ったことは無かった。
せっかくなので、Bash on Windowsで動かそうとapt installした。
・・・動かない?

Bash on Windows (Windows Subsystem for Linux) でvalgrindを動かす - wagavulin's blog

ああ、そうなんですか。
ソースからビルドすると、確かに動いた。


というところまでよかったのだが、今度は動作自体がおかしい。
valgrindなしで動かすと正常に動くのだが、valgrindで動かすとSegmentation Faultが発生する。
どうも、valgrind自体が落ちているような感じがする。
「General Protection Fault」などとも出てくる。

Running valgrind throws General Protection Fault on dl-misc.c · Issue #1295 · Microsoft/BashOnWindows

これかな?
まだopenだから、直ってないということか。

試しに、同じソースを普通のUbuntu16.04でvalgrindしたら、ちゃんと動いてくれた。
ふっ、Bash on Windowsもまだまだじゃのぅ。

2017/02/02

[n5]Nexus5から搬送波が出なくなった→復活!

開発関係の話じゃ無くて済まないが、私にとっては緊急事態に等しい。
Nexus5から搬送波が出なくなったのだ。

数日前から発生している。
まったく出ないわけでは無く、弱々しく出るときもあれば、出なくなるときもある。
タグの読込はできないし、SNEPもしない。
うーむ。。。

もう、フルバックアップして、端末リセットするしか無いか。。。
Nexus7もがんばってはくれているのだが、やはりこのサイズでNFCが載っているのはありがたいのだ。
もしリセットで復旧しなかったら、買い直すことになるのだろうか。

 

しかし、nRF52でType4に対応したというのに、何というタイミングか・・・。
New NFC feature on nRF52 series: Read/write capable Type 4 Tag NFC library in nRF5 SDK


2017/02/03

Androidの設定画面から端末リセットを行ったが、復帰せず。。。
搬送波の出方が、相変わらず微妙で、位置がいつもの背面のところよりもかなり下の方でしか確認できない。
落下したことで、なんか部品がずれたりしてるとか?
まさかなぁ。

 

せっかくだから、ファクトリーイメージを焼くところまでやっておこう。
https://developers.google.com/android/images

焼けない?

FAILED (command write failed (No error))

やり方を間違ったのかと思ったが、手順はそんなに変わらんし。
・・・USBハブ経由で挿していたのだが、PCに直接挿すとflash-all.batが動いた。
なんだったんだ。。。

そして、焼き直したけど・・・ダメ。
そうよねぇ、ダメよねぇ。

 

ちょうど今日は日本では節分という行事がある日なので、そういうものにでもすがりつきたい心境だが、買い換えを検討するのが妥当だろうな。


毒くらわば皿まで、という言葉が日本にはあります。
あきらめろ?
中途半端で終わらせるな?
そんな意味合いなのでしょうか。

ここまでやったので、裏蓋を開けてみました。

image

爪で開けようとして失敗したので、ビックカメラのカード(10年くらい前に作ったやつなので、今は違うかも)が薄かったので、それを電源ボタン辺りに差し込んで、ぐいっとひねったところ、ようやく1箇所だけ開いた。
あとは、Tポイントカード(これも10年くらい前)が厚めだったので、使い分けながらやっていくと、なんとか開いた。

左側の黒いやつが、NFCアンテナなのかな?
ということは、その上にある4つの接点で本体と接しているのか。

アンテナの右上にある丸いのは、ボタン電池かと思ったが、たぶんセンサだろう。
なんだろう、磁気とかか?

蓋を外した状態で立ち上げ、搬送波を確認したがまったく出ていなかった。
やはり、蓋の裏にあるやつがNFCアンテナか。
運が良ければ、この接点の接触が悪いために出ていない、というだけかもしれん。
アルコールで拭いて、カッターの先っちょで本体側の接点をもう少し持ち上げて、蓋を閉じる。

そして端末を起動すると・・・搬送波復活!
タグも読めるし、SNEPもする!
よかった。。。。


そのあと、しばらくすると、また読めなくなった。
が、接点の辺りを蓋の上から押すと、読めた。

うちの場合は、黄色の丸で囲んだところ辺りに、NFCアンテナの接点があった。
ここが離れているとき、指で軽く押すと、カチッと接触するような音が聞こえた。

image

押すといっても、力を入れるか入れないか、という程度のものだ。
カチッという音も、ちょうど音が鳴るような距離が開いていたから聞こえたのだろう。
だから、押すとちょうど接触して読めたのだろう。

そういう状態であれば、蓋の外から押さえつけるか、蓋を変形してしまえば接触すると思う。
今回は、遠くからライターの火で炙って、少し温めてから少し曲げてみた。
今のところ、それで接触するようになったけど、湾曲しやすいところに接点を持ってくるというのは、あまりよくなかったんじゃなかろうか。
せめて、もう少し縁の方になっていれば、そういう影響はなかったんじゃなかろうか。
実際、NFC以外のアンテナは、縁の方に接点があったし。