Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Order of parameter pack expansion

I have 2 functions to read binary file.

1st function reads sizeof(T) bytes from file:

template<typename T>
T read() { ... some IO operations ... };

2nd function calls first one multiple times with each template parameter:

template<typename... Ts>
std::tuple<Ts...> read_all() {
    return std::make_tuple(read<Ts>()...);
};

Everything works fine except of 1st function call order. For something like

uint32_t a;
uint8_t b;
std::tie(a, b) = read_all<uint32_t, uint8_t>();

the first will be called read<uint8_t>() and after that read<uint32>() which reverses order of passing template parameters and messes up with order of bytes in file.

Sure, I can call read_all with reversed order of template arguments and get right order in the end, but is there a more obvious way to do so?

like image 312
Semyon Burov Avatar asked Feb 05 '17 01:02

Semyon Burov


2 Answers

C++ does not specify the order in which a function's arguments are evaluated. If the expressions to a function all consume data from a stream, you can get behavior where objects are read in the wrong order.

Braced initializer lists are evaluated from left to right, however, so you should get better results if you try something like:

template<typename... Ts>
std::tuple<Ts...> read_all() {
    return std::tuple<Ts...>{read<Ts>()...};
}
like image 140
Azure Avatar answered Sep 23 '22 13:09

Azure


I would keep it a bit simpler and do this:

uint32_t a;
uint8_t b;
std::tie(a, b) = read<std::tuple<uint32_t, uint8_t>>();

This way there's only a single read() and you can even skip the tie() if you use the tuple (or struct) fields directly.

like image 39
John Zwinck Avatar answered Sep 25 '22 13:09

John Zwinck