c++scanfstdtuplestdapply

std::apply-ing sscanf into a tuple, tuple not fully updating


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.


Solution

  • 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