Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parameter pack expansion within parentheses gives bizarre output

I am trying to implement a function which accepts a variable number of strings and forwards to a print function, which expects a char pointer and size for every string, interleaved.

Example:

std::string a = "123";
std::string b = "1234";
forward(a, b); // should call doPrint(a.c_str(), a.size(), b.c_str(), b.size())

I thought that the following should be a correct implementation, but even though it compiles the behavior is very surprising to me.

template <class ...Args>
void forward(const Args & ... args) {
  doPrint( (args.c_str(), args.size())...);
}

forward(a, b) calls doPrint(3, 4), and not doPrint("123", 3, "1234", 4), as if I had written doPrint((args.size())...). The call to c_str() is ignored completely by the compiler.

I tried g++, clang, and icc with all yielding the same output. What is wrong with (args.c_str(), args.size())...?

Indeed, std::make_tuple(args.c_str(), args.size())... works as expected, but let's say I cannot change doPrint to accept and process tuples.

like image 436
Georgios Bitzes Avatar asked Dec 04 '22 00:12

Georgios Bitzes


2 Answers

The comma operator is an expression whose value is the value of the last expression.
For example:

    int a = (1, 2, 3, 4, 5, 6);
    assert(a == 6);

What you can try instead is using tuples:

    doPrint(std::tuple_cat(std::make_tuple(argc.c_str(), args.size())...));

Then doPrint will need to be changed to work with a tuple; it could unpack the tuple back into a parameter pack if desired or just work with the tuple directly.

Example unpacking tuple:

    template <class Tuple, std::size_t ... indices>
    doPrint(Tuple t, std::integer_sequence<size_t, indices...>)
    {
        doPrint(std::get<indices>(t)...);
    }

    template <class Tuple>
    doPrint(Tuple t)
    {
        doPrint(t, std::make_index_sequence<std::tuple_size<Tuple>::value>());
    }

There could be some problems with ambiguous function names so you may need to change the names of these helper functions, but hopefully this is enough for you to get going.

like image 174
SirGuy Avatar answered Dec 06 '22 15:12

SirGuy


(args.c_str(), args.size()) is a comma-separated expression, meaning that only the last part (args.size()) will be passed to the function.

It will then repeat this for each parameter, so it will actually call doPrint just with the strings sizes!

You should change doPrint to use tuples instead, otherwise you have to use some crazy template meta-programming stuff.

like image 21
Mattia F. Avatar answered Dec 06 '22 13:12

Mattia F.