Make our stringstream usage locale independent.
As discussed with laanwj in #18134 (comment). Fixes #18281.
Make our stringstream usage locale independent.
As discussed with laanwj in #18134 (comment). Fixes #18281.
1153 | @@ -1152,6 +1154,12 @@ TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_FORMAT_FUNCS) 1154 | template<typename... Args> 1155 | std::string format(const std::string &fmt, const Args&... args) 1156 | { 1157 | + // Numerous tfm::format(...) callers are implicitly assuming that the output 1158 | + // returned is locale independent. Notable examples include i64tostr, itostr 1159 | + // and ScriptToAsmStr. Make sure to do a thorough review of all call sites 1160 | + // (and other direct/indirect stringstream consumers) if this assumption is 1161 | + // ever removed in the future. 1162 | + assert(std::locale{} == std::locale::classic());
I'd rather just make this explicitly use the classic locale than forcing a global assumption on the code (i.e. oss.imbue(std::locale::classic());).
Fixed!
<!--e57a25ab6845829454e8d69fc972939a-->
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.
<!--174a7506f384e20aa4161008e828411d-->
Reviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.
1153 | @@ -1153,6 +1154,7 @@ template<typename... Args> 1154 | std::string format(const std::string &fmt, const Args&... args) 1155 | { 1156 | std::ostringstream oss; 1157 | + oss.imbue(std::locale::classic()); 1158 | format(oss, fmt.c_str(), args...); 1159 | return oss.str();
How does this have any effect? This is pretty much dead code (most of our format strings are c strings, so the other function is called). Might as well remove the code here to clarify this is not the primary implementation.
return format(fmt.c_str(), args...);
Ouch, good catch. I also thought this was the main entry point.
I've now given all functions the same treatment. Please re-review :)
I think this can still be replaced by a single line return format(fmt.c_str(), args...);.
Fixed!
243 | @@ -243,6 +244,7 @@ const std::string& ListBlockFilterTypes() 244 | static std::once_flag flag; 245 | std::call_once(flag, []() { 246 | std::stringstream ret; 247 | + ret.imbue(std::locale::classic());
Would it be possible to add a static std::stringstream ClassicStringStream() { std::stringstream s; s.imbue(); return s; } to our str util library, so that call sites have a lower LOC-overhead?
Would it be possible to add a
static std::stringstream ClassicStringStream() { std::stringstream s; s.imbue(); return s; }to our str util library, so that call sites have a lower LOC-overhead?
Added! Please re-review :)
2 | @@ -3,12 +3,12 @@ 3 | // Distributed under the MIT software license, see the accompanying 4 | // file COPYING or http://www.opensource.org/licenses/mit-license.php. 5 | 6 | -#include <wallet/db.h>
The module header is always included first, I think
Fixed!
@MarcoFalke Feedback addressed. Please re-review :)
@MarcoFalke Would you mind reviewing again? Your feedback has been addressed :)
1130 | @@ -1126,6 +1131,7 @@ template<TINYFORMAT_ARGTYPES(n)> \ 1131 | std::string format(const char* fmt, TINYFORMAT_VARARGS(n)) \ 1132 | { \ 1133 | std::ostringstream oss; \ 1134 | + oss.imbue(std::locale::classic()); /* Added for Bitcoin Core */ \
Have you tried submitting this upstream? Other users might be interested in this, but it might have to be hidden behind a compile time flag.
My plan is to do that if/when we get this in to Core :)
Rebased :)
Any interest in avoiding locale dependence? If not I'll close :)