-
Notifications
You must be signed in to change notification settings - Fork 165
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SetScrollInfo の呼び出しを抑制 #453
Conversation
@@ -335,8 +339,12 @@ void CEditView::AdjustScrollBars() | |||
si.nMax = (Int)GetRightEdgeForScrollBar() - 1; // 2009.08.28 nasukoji スクロールバー制御用の右端座標を取得 | |||
si.nPage = (Int)GetTextArea().m_nViewColNum; /* 表示域の桁数 */ | |||
si.nPos = (Int)GetTextArea().GetViewLeftCol(); /* 表示域の一番左の桁(0開始) */ | |||
si.nTrackPos = 1; | |||
::SetScrollInfo( m_hwndHScrollBar, SB_CTL, &si, TRUE ); | |||
si.nTrackPos = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1 → 0 に変わっていますが、意図してますか?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
意図したものです。tagSCROLLINFO
の nTrackPos
メンバーは、SetScrollInfo
関数では無視されます。そして、GetScrollInfo
で取得した値と比較する為、このメンバーの値をあえて 0 に揃えています。
::SetScrollInfo( m_hwndVScrollBar, SB_CTL, &si, TRUE ); | ||
SCROLLINFO siPrev = si; | ||
::GetScrollInfo( m_hwndVScrollBar, SB_CTL, &siPrev ); | ||
siPrev.nTrackPos = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GetScrollInfo した結果で siPrev.nTrackPos
が 0 以外でそれ以外が
一致していた場合、元のコードと振る舞いが異なりますが、
意図していますか?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nTrackPos
メンバーは取得のみで設定は出来ないので問題ないという認識です。
実際に操作して問題は無いように見えます。色んなケースを本当に確認できているのか?
という疑問は常に付きまといますが。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
下のコメントに書きましたが、memcmp を使おうとするからややこしくなるのだと思います。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
そう言われると記述量が多くなってもメンバー単位で比較した方が良いのかもしれないですね。
SCROLLINFO siPrev = si; | ||
::GetScrollInfo( m_hwndVScrollBar, SB_CTL, &siPrev ); | ||
siPrev.nTrackPos = 0; | ||
if (memcmp(&si, &siPrev, sizeof(si)) != 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
siPrev 全体と si を比較せずに、si.nPage や si.nPos など異なる可能性のあるメンバーを選んで比較した方がいいと思います。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
c言語は構造体メンバのビット数をある程度任意に決められるので、慣習的にこういう比較方法をとることが多いように思います。たとえば0~7の数値は3bitあれば足りるので、構造体の変数定義をint型にして3bit割り付ける、というような細かい調整ができます。メモリサイズの制約が厳しかった時代の遺産なので、現代では考慮する必要のないことなのかも知らんです。
どっちかというとこれが標準なので、必要なメンバだけ比較って方式に変えるのは大変かも知れないです。ぶっちゃけると、ここで細かい調整をしても、あとで大枠ごと入れ替える可能性があるので、現段階ではあまりこだわらないのが吉であるように感じております。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ビットフィールドが今回のケースにどう関わってくるのか良く分かりませんが、 memcmp で比較するコードにしたのはメンバ単位での比較を記述するのが手間に感じたからです。しかしこうして色々やり取りが発生する事を考えると memcmp さんは存在自体が罪深いなとしみじみと感じます…。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- 構造体のフィールド間にパディングが生じる場合に、memcmp での比較はパディングまで比較対象にしてしまいます。パディングの値は基本的に不定です。
- 構造体のフィールドに割り当てるビット数の指定やアラインメントの指定はパディングの制御を可能にします。しかし SCROLLINFO を定義しているのは……。
- memcmp で比較する場合は、両方のオペランドが memset により 0 埋め初期化されていることが必須ではないでしょうか。確かめたことがありませんが、不安になる&前提の確認が面倒なので、memcmp を比較には使わないようにしています。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
色々と解説ありがとうございます。
今回のケースで memcmp で実際に動作上問題があるのかどうかは各人のコメントを見ても良く分かりませんでしたが、横着せずにメンバーを選んで比較するように最初から書いた方が良かったですね。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
願わくば、絡まれるのを避けるためだけの事なかれ主義によるものではなく、C 時代の化石じみた問題含みの手法であることを理解した結果であることを。(別の場所でうんざりさせてしまった自覚があるので書いています)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
突き合わせしたいフィールド外の箇所の値に影響されてしまうというのは本当にそうですね。
それへの対処を別に入れるくらいなら使わない方が良い、というのもおっしゃる通りだと思います。
今回は楽できるかなという思い込みで書きましたが、色々と駄目でした。
水平方向と垂直方向の処理で、コードの重複を避けるため関数化できませんか? |
正論。 しかし難しかったようなうろ覚えの記憶があります。 テンプレじゃ無理なんで「やるならマクロ関数化?」みたいな修羅道だった気が。 |
sakura/sakura_core/view/CEditView_Scroll.cpp Lines 308 to 319 in 58d8bd9
で si.nMax, si.nPage, si.nPos, m_hwndVScrollBar の値を引数に渡すような関数を作って実現できないですか? sakura/sakura_core/view/CEditView_Scroll.cpp Lines 311 to 313 in 58d8bd9
sakura/sakura_core/view/CEditView_Scroll.cpp Line 316 in 58d8bd9
|
|| siPrev.nPos != nPos) | ||
{ | ||
if (nMax + 1 < (int)nPage) { | ||
nPage = (UINT)(nMax + 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
max に +1 しているのはなぜですか?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
動作確認した結果入れました。
ユーザー定義関数の引数 nPage
に来る値が、nMax
より結構大きかったりするケースが状況によってはあるのですが、その値をそのまま構造体に入れて SetScrollInfo
で設定しても、GetScrollInfo
で取った値ではクリッピングされているので、比較する時に一致しません。今回の変更の目的は呼び出しコストの大きい SetScrollInfo
をなるべく呼ばなくする事なので対処を入れています。
このコメントを書く際に公式のドキュメントを再度調べてみたら書かれてました。
https://docs.microsoft.com/en-ca/windows/desktop/api/winuser/nf-winuser-setscrollinfo#remarks
The SetScrollInfo function performs range checking on the values specified by the nPage and nPos members of the SCROLLINFO structure. The nPage member must specify a value from 0 to nMax - nMin +1. The nPos member must specify a value between nMin and nMax - max( nPage– 1, 0). If either value is beyond its range, the function sets it to a value that is just within the range.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sakura/sakura_core/view/CEditView_Scroll.cpp
Lines 289 to 291 in b178891
if (nMax + 1 < (int)nPage) { | |
nPage = (UINT)(nMax + 1); | |
} |
は以下のような感じにしたらいかがでしょうか?
// nPage を 0 から nMax - nMin + 1 の間に収まるようにする
// https://docs.microsoft.com/en-ca/windows/desktop/api/winuser/nf-winuser-setscrollinfo#remarks
const int nMin = 0;
if ((nMax - nMin)+ 1 < (int)nPage) {
nPage = (UINT)((nMax - nMin)+ 1);
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
↑ (nMax - nMin) の括弧は不要でした。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
そのように直しておきます。
元のコードからある問題なのですが、nMax に負の値を指定された場合に UINT へのキャストでとても大きい値になってしまうのがちょっと心配ですね。
あと nPos はクリッピングしなくて良いのか、とかも疑問があります。
まぁとりあえず動いているから良いかな。。
この PR の効果を調べるにはどうやってテストしたらわかりますか? |
下記の手順で自分は確認しました。
master の場合と比べてこのPRのビルドで数値が大きく減少する事が確認出来れば効果が有りとみなせると思います。 |
ありがとうございます。今夜 or 明日動作確認してみようと思います。 |
以下のようなコードで setScrollInfoIfNeeded が呼ばれた回数に対して SetScrollInfo が呼ばれた回数を調べてみました。→ g_total = 970, g_called = 470 でした。SetScrollInfo 呼び出しを半分程度にできるようです。
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ありがとうございます。
レビューありがとうございます。Merge します。 |
SetScrollInfo の呼び出しを抑制
CEditView::AdjustScrollBars
で呼び出しているSetScrollInfo
Windows API 関数の負荷が高いようなので、必要な場合にのみ呼び出しを行うように判定を追加しました。