質問編集履歴
4
説明の改善、execute_before_mainテンプレート化案の追記
test
CHANGED
File without changes
|
test
CHANGED
@@ -73,16 +73,40 @@
|
|
73
73
|
この考えがあっているか、もしくはこれ以外にもないか教えてくださると幸いです。
|
74
74
|
|
75
75
|
#### 1. [グローバル変数の初期化の実行順によるバグ](https://isocpp.org/wiki/faq/ctors#static-init-order)がありそう
|
76
|
+
今回の場合、コンストラクタで処理を実行するために(static)変数を定義しています。
|
77
|
+
その処理の中で、コンストラクタがまだ実行されていないグローバル変数を参照したり書き換えてしまうとバグが発生するというものです。
|
76
|
-
|
78
|
+
しかし、設定コードとして書くものは基本的にboolやenum型のグローバルなinline変数に代入するというもので、**他のクラスのメンバを書き換えるみたいなことはしないので大丈夫**だと思っています。
|
77
|
-
また、グローバル変数は定義順に初期化が実行されるという観点でも、ヘッダーオンリーライブラリだから、必ず定義が先に来て、その後に`execute_before_main`のコンストラクタが呼ばれるようになってい
|
79
|
+
また、グローバル変数は定義順に初期化が実行されるという観点でも、ヘッダーオンリーライブラリだから、必ず定義(初期化)が先に来て、その後に`execute_before_main`のコンストラクタが呼ばれるようになっています。
|
78
|
-
また、ユーザーにもこの問題のことは伝えておいて、ライブラリの設定コード以外は書かないように注意してお
|
80
|
+
また、ユーザーにもこの問題のことは伝えておいて、ライブラリの設定コード以外は書かないように注意しておきます。
|
79
81
|
|
80
|
-
#### 2.
|
82
|
+
#### 2. コンストラクタで処理を実行するためだけに参照されない変数を新たに定義するのは名前空間が汚れる
|
81
|
-
ユーザーに新しく名前空間を作らせてもよいが、`execute_before_main`のstaticメンバの定義とすると名前空間を作らずに変数をネスト出来て、手軽でよいかなと思
|
83
|
+
ユーザーに新しく名前空間を作らせてもよいですが、`execute_before_main`のstaticメンバの定義とすると名前空間を作らずに変数をネスト出来て、手軽でよいかなと思いました。
|
82
84
|
|
83
|
-
#### 3. このライブラリは設定コードを書かなくても使用できるようにしたいが、その場合`execute_before_main`のstaticメンバは宣言のみで、定義がなくなってしまうことについて
|
85
|
+
#### 3. このライブラリは設定コードを書かなくても使用できるようにしたいが、その場合`execute_before_main`のstaticメンバ(perform)は宣言のみで、定義がなくなってしまうことについて
|
84
|
-
ODR-usedされていない
|
86
|
+
performはODR-usedされていないので、定義がなくてもコンパイラはNDR(診断不要)。でも、NDRはコンパイラはエラーをはかないけど、未定義動作を引き起こす可能性は否定していないから避けた方がいいか?でもODR-usedされていない変数が定義されてないだけだから、実質未定義動作は起こらない気がするけど、、
|
85
87
|
もしダメな場合はstaticメンバは削除してグローバル変数を定義してもらう方針に変える。
|
88
|
+
|
89
|
+
追記: 後で思ったのですが、`execute_before_main`をテンプレートにして、実体化を遅らせればいいのではと思いました。つまり以下のような形です。(これで実際に動きました)
|
90
|
+
|
91
|
+
```cpp
|
92
|
+
template<typename = void>
|
93
|
+
struct execute_before_main {
|
94
|
+
template<typename Func>
|
95
|
+
execute_before_main(Func func) {
|
96
|
+
func();
|
97
|
+
}
|
98
|
+
static execute_before_main perform;
|
99
|
+
};
|
100
|
+
|
101
|
+
// perform変数の定義。それと同時にテンプレート引数に
|
102
|
+
// voidを取った時のexecute_before_mainのテンプレート実体化
|
103
|
+
// 文法の解釈が難しいですが、テンプレートの実体化をしているとだけ思ってほしいです
|
104
|
+
// ソースファイルに書く場合はinlineは不要
|
105
|
+
template<>
|
106
|
+
inline execute_before_main<> execute_before_main<>::perform([]{
|
107
|
+
// ここにライブラリの設定コードを書く
|
108
|
+
});
|
109
|
+
```
|
86
110
|
|
87
111
|
#### 4. 使用されないstaticメンバ変数の定義文として書いているので、最適化などでコンパイラに削除されてしまわないか
|
88
112
|
これに関しては実際に試してみましたが、手元の環境のclang++とg++のO2、O3、リンク時最適化、MSVCのO1、O2の最適化では削除されることはありませんでした。
|
3
typo修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -45,7 +45,7 @@
|
|
45
45
|
// ライブラリ側で用意する ----------------------------------------------
|
46
46
|
// ※namespaceは省略します
|
47
47
|
struct execute_before_main {
|
48
|
-
template<tye
|
48
|
+
template<typename Func>
|
49
49
|
execute_before_main(Func func) {
|
50
50
|
func();
|
51
51
|
}
|
@@ -72,7 +72,7 @@
|
|
72
72
|
以下にバグになりうりそうな要因を自問自答してみた内容を書きます。
|
73
73
|
この考えがあっているか、もしくはこれ以外にもないか教えてくださると幸いです。
|
74
74
|
|
75
|
-
### 1. [グローバル変数の初期化の実行順によるバグ](https://isocpp.org/wiki/faq/ctors#static-init-order)がありそう
|
75
|
+
#### 1. [グローバル変数の初期化の実行順によるバグ](https://isocpp.org/wiki/faq/ctors#static-init-order)がありそう
|
76
76
|
今回の場合、設定コードとして書くものは基本的にboolやenum型のグローバルなinline変数に代入するというもので、**他のクラスのメンバを書き換えるみたいなことはしないので大丈夫なはず。**
|
77
77
|
また、グローバル変数は定義順に初期化が実行されるという観点でも、ヘッダーオンリーライブラリだから、必ず定義が先に来て、その後に`execute_before_main`のコンストラクタが呼ばれるようになっている。
|
78
78
|
また、ユーザーにもこの問題のことは伝えておいて、ライブラリの設定コード以外は書かないように注意しておく。
|
2
文法ミス修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -96,7 +96,7 @@
|
|
96
96
|
// ライブラリ内
|
97
97
|
namespace my_library {
|
98
98
|
// このライブラリがログを出力するときの1行当たりの文字数のデフォルト値
|
99
|
-
inline max_line_width = 80;
|
99
|
+
inline int max_line_width = 80;
|
100
100
|
}
|
101
101
|
|
102
102
|
// ユーザーコード内
|
1
補足3の追加
test
CHANGED
File without changes
|
test
CHANGED
@@ -87,4 +87,22 @@
|
|
87
87
|
#### 4. 使用されないstaticメンバ変数の定義文として書いているので、最適化などでコンパイラに削除されてしまわないか
|
88
88
|
これに関しては実際に試してみましたが、手元の環境のclang++とg++のO2、O3、リンク時最適化、MSVCのO1、O2の最適化では削除されることはありませんでした。
|
89
89
|
|
90
|
+
### 補足3: コメントを受けて
|
91
|
+
設定コード用の変数の初期化自体はライブラリで行っています。
|
92
|
+
その値をカスタマイズしたいという場合に、main関数外で値を変えられたらなと思っています。
|
93
|
+
また、オプションの内容はユーザーの好みレベルのもので、プロジェクト全体を通して変わらない定数と考えてもらって構いません。
|
90
94
|
|
95
|
+
```cpp
|
96
|
+
// ライブラリ内
|
97
|
+
namespace my_library {
|
98
|
+
// このライブラリがログを出力するときの1行当たりの文字数のデフォルト値
|
99
|
+
inline max_line_width = 80;
|
100
|
+
}
|
101
|
+
|
102
|
+
// ユーザーコード内
|
103
|
+
// ソースファイルに書く場合はinlineは不要
|
104
|
+
inline execute_before_main execute_before_main::perform([]{
|
105
|
+
// このライブラリがログを出力するときの1行当たりの文字数を100に変更
|
106
|
+
my_library::max_line_width = 100;
|
107
|
+
});
|
108
|
+
```
|