I have this code, which is used to build a graphic interface on a LCD display on a controller; the code is compiled for 2 different architectures using both AVR and PIC32:>
As mentioned in the comment, until C++17, the order of evaluation of unsequenced subexpressions in function arguments is unspecified, so it's not a compiler bug: both orderings are allowed (even resulting in undefined behaviour if those expressions result in more than one read/write to the same scalar variable, eg as in f(i++,i++)
).
Since C++17, postfix expressions (like function calls) evaluate left-to-right; the evaluation order of function arguments is still unspecified, but cannot interleave. So your code will always give the wanted result since C++17.
As a workaround, you may let Label and friends also accept lambdas as parameters, to support lazy(and hence ordered) evaluation, something like:
template
auto constref_or_evaluate(T const& t) -> T const&
{ return t; }
template
auto constref_or_evaluate(T&& t) -> decltype(std::forward(t)())
{ return std::forward(t)(); }
// the type of FishinoTftGui
struct FishinoTftGuiType
{
// chainable members ...
template
auto Label(T&&... t) -> FishinoTftGuiType&
{
LabelImpl( constref_or_evaluate(std::forward(t))... );
return *this;
}
private:
// the original chainable member implementations ...
void LabelImpl(int,int); //whatever
};
// to be used as
FishinoTftGui
.Label(1,2)
.Label([]{return 3;},4);
here the lambda in the second Label() will be always invoked after the first Label() had been fully evaluated.
This has also the advantage of giving finer control on when a lazy expression is evaluated (say, the label could update the lazy parameter whenever the view is resized, etc...). So, it might be worth considering in >=C++17 code as well.
As far as I can tell, this is just C++11; anyway, if you also want to pass l/rvalue reference parameters you'll need to write a forward_or_evaluate()
function; this is perfectly doable in C++11, but it's a bit harder to implement.