英文:
std::apply-ing sscanf into a tuple, tuple not fully updating
问题
以下是您提供的内容的翻译部分:
我有一串结构化的数据字符串,我想将其解析为元组。对于每种不同的输入字符串“种类”,数据的类型和排列都可能不同,因此我想使用模板和scanf格式来避免管理各种变量和重复的代码。
下面的代码 *应该* 接受一个元组,将其成员展开为参数,然后根据“sscanf”的结果更新这些成员。
```cpp
#include <tuple>
#include <cstdio>
#include <cassert>
int main()
{
// 纯粹的 sscanf
const char msg[] = "000000000A200890";
const char fmt[] = "%04X%04X%04X%04X";
uint16_t a, b, c, d;
sscanf(msg, fmt, &a, &b, &c, &d);
assert(a == 0x0000);
assert(b == 0x0000);
assert(c == 0x0A20);
assert(d == 0x0890);
// 放入一个一元元组似乎可以工作
std::tuple<uint16_t> onetuple;
const char *shortmsg = "10";
const char *shortfmt = "%02X";
std::apply([&shortmsg, &shortfmt](auto &...args) {
sscanf(shortmsg, shortfmt, &args...);
}, onetuple);
assert(std::get<0>(onetuple) == 0x10);
// 放入一个元组
std::tuple<uint16_t, uint16_t, uint16_t, uint16_t> dest;
std::apply([&msg, &fmt](auto &...args) {
sscanf(msg, fmt, &args...);
}, dest);
assert(std.get<0>(dest) == 0x0000);
assert(std.get<1>(dest) == 0x0000);
assert(std.get<2>(dest) == 0x0A20);
assert(std.get<3>(dest) == 0x0890);
return 0;
}
这个示例不起作用。第一部分使用纯粹的 sscanf
是正常的,表现如预期。最后一部分,我尝试将其“apply”到一个元组中 不会改变元组。但如果元组是一个一元元组(只有在这种情况下),代码就可以正常工作。
这是在C++17下,使用GCC。
<details>
<summary>英文:</summary>
I have a structured string of data, and I want to parse it into a tuple. For each different *kind* of input string, the types and arrangement of the data can be different, so I want to use templates and scanf formats to avoid having to manage all sorts of variables and repetitive code.
The code below *should* take a tuple, explode its members out as parameters, and then update those members based on the results of `sscanf`.
#include <tuple>
#include <cstdio>
#include <cassert>
int main()
{
//pure sscanf
const char msg[] = "000000000A200890";
const char fmt[] = "%04X%04X%04X%04X";
uint16_t a, b, c, d;
sscanf(msg, fmt, &a, &b, &c, &d);
assert(a == 0x0000);
assert(b == 0x0000);
assert(c == 0x0A20);
assert(d == 0x0890);
//into a one-tuple seems to work
std::tuple<uint16_t> onetuple;
const char *shortmsg = "10";
const char *shortfmt = "%02X";
std::apply([&shortmsg, &shortfmt](auto &...args) {
sscanf(shortmsg, shortfmt, &args...);
}, onetuple);
assert(std::get<0>(onetuple) == 0x10);
//into a tuple
std::tuple<uint16_t, uint16_t, uint16_t, uint16_t> dest;
std::apply([&msg, &fmt](auto &...args) {
sscanf(msg, fmt, &args...);
}, dest);
assert(std::get<0>(dest) == 0x0000);
assert(std::get<1>(dest) == 0x0000);
assert(std::get<2>(dest) == 0x0A20);
assert(std::get<3>(dest) == 0x0890);
return 0;
}
This example doesn't work. The first block, using pure `sscanf` is fine, and behaves as expected. The last block, where I attempt to `apply` into a tuple *doesn't alter the tuple at all*. But if the tuple is a one-tuple (and only if it's a one-tuple), the code works fine.
This is in C++17, under GCC.
</details>
# 答案1
**得分**: 4
问题实际上是您的格式字符串错误,而且您可能会遇到未定义行为。 您应该使用`%04hX`。 请注意`h`,它表示`short`,而不是完整长度的整数。 您所有的格式字符串都需要类似地修改:
请参阅https://godbolt.org/z/38PEPW39a
<details>
<summary>英文:</summary>
Actually, the problem is that your format string is wrong, and you're probably hitting undefined behavior. You should be using `%04hX`. Note the `h` which indicates a `short`, as opposed to the full length integer. All of your format strings need to be similarly modified:
See https://godbolt.org/z/38PEPW39a
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论