C++でC風ライブラリを作る(環境変数編)
高木です。こんばんは。
2月に始めたこの連載も、気がつくともう3ヶ月半もやっているんですね。
連載というのはネタ切れ防止にはいいので、当面は余裕だと思います。
一方でいい加減この連載も飽きてしまいそうです。
楽しみにしてくれている方が果たしているのかどうかわかりませんが、私自身が飽きてしまうのが一番の懸案です。
今後は別の連載も開始するなどして、ブログの内容にバリエーションを持たせていくことも考えています。
若手の社員がどの程度頑張ってブログを書いてくれるかにもよるので、もう少し様子を見ることにしましょう。
さて、今回は環境変数を取得するgetenv
関数の置き換えを考えてみたいと思います。
標準規格のgetenv
関数は使いにくい関数のひとつで、関数が内部的に管理している記憶域へのポインタを返します。
なので、スレッドセーフではありませんし、あとからもう一度getenv
関数を呼び出すと、先ほど取得したポインタが指す内容は保証されなくなります。
getenv
関数が返したポインタが空ポインタではないことを確認したら、ただちにstd::string
型の変数に文字列をコピーしてしまうという手はすぐに考えつきます。
しかし、この方法ではスレッドセーフになりません。
今回作成するgetenv
の代替関数で排他制御を行う手もありますが、何の支障もなくstd::getenv
関数を呼べてしまうのでダメです。
標準規格の範囲内で実装することを諦めるなら、グローバル変数environ
または_environ
を使って自力で環境変数を探索すれば何とかなりそうです。
しかし、putenv
関数やsetenv
関数を使う場合にはスレッドセーフではなくなってしまいます。
どうせ標準規格の範囲から外れるのであれば、Visual C++のgetnenv_s
関数や一部処理系で使えるgetenv_r
関数に依存する手もあるのですが、処理系の網羅範囲に問題があります。
なかなか完璧な方法はないので、今回は可能な限り安全な実装方法を採用することで済ませましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | namespace cloverfield { extern "C" char **environ; inline bool getenv(char const* name, std::string& value) { auto n = std::strlen(name); for (char** envp = environ; auto e = *envp; ++envp) { if (std::strncmp(name, e, n) == 0 && e[n] == '=') { value = &e[n + 1]; return true; } } return false; } } |
今回実装したgetenv
関数を呼び出している最中に、別のスレッドでputenv
関数やsetenv
関数のような環境変数を変更する処理を行った場合は知りませんが、それ以外ならどうにかなりそうです。