Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Combining auto, tuples and std::tie

28 views
Skip to first unread message

David Brown

unread,
Oct 12, 2015, 9:09:16 AM10/12/15
to
I've been looking at the possibilities for returning multiple values
from a function in convenient ways. If we start with a simple function
that returns multiple values:

auto square(double x) {
if (x >= 0) {
return std::make_tuple(true, x*x);
} else {
return std::make_tuple(false, -x*x);
}
}

We can get the data out of the returned pair with auto-typed variables
like this:


auto foo1(double x) {
auto v_r = square(x);
auto valid = std::get<0>(v_r);
auto res = std::get<1>(v_r);

if (valid) return res;
return 0.0;
}


Alternatively, we can define "valid" and "res" first and then use
std::tie to unpack the tuple:

auto foo2(double x) {
bool valid;
double res;
std::tie(valid, res) = square(x);

if (valid) return res;
return 0.0;
}

But there seems to be no way to combine "auto" and "tie", to get
something like:

auto foo(double x) {
auto valid, res = square(x);

if (valid) return res;
return 0.0;
}


Does anyone know of a /nice/ way to get this sort of behaviour? I have
a method using rather ugly macros that works (up to a given size of
tuple - 4 in the macros shown below), but I hope that there is a better
way to do it:

auto foo3(double x) {
autotie(square(x), valid, res);

if (valid) return res;
return 0.0;
}


The macro "autotie" is:

#define TOKENPASTE1(x, y) x ## y
#define TOKENPASTE(x, y) TOKENPASTE1(x, y)

#define autotie_1(v_, vt_, a1_) \
auto vt_ = v_; \
auto a1_ = std::get<0>(vt_)

#define autotie_2(v_, vt_, a1_, a2_) \
auto vt_ = v_; \
auto a1_ = std::get<0>(vt_); \
auto a2_ = std::get<1>(vt_)

#define autotie_3(v_, vt_, a1_, a2_, a3_) \
auto vt_ = v_; \
auto a1_ = std::get<0>(vt_); \
auto a2_ = std::get<1>(vt_); \
auto a3_ = std::get<2>(vt_)

#define autotie_4(v_, vt_, a1_, a2_, a3_, a4_) \
auto vt_ = v_; \
auto a1_ = std::get<0>(vt_); \
auto a2_ = std::get<1>(vt_); \
auto a3_ = std::get<2>(vt_); \
auto a4_ = std::get<3>(vt_)

#define autotie3(_1, _2, _3, _4, m_, ...) m_

#define autotie2(v_, vt_, ...) \
autotie3(__VA_ARGS__, autotie_4, autotie_3, autotie_2, \
autotie_1)(v_, vt_, __VA_ARGS__)

#define autotie(v_, ...) \
autotie2(v_, TOKENPASTE(v__, __COUNTER__), __VA_ARGS__)


(Yes, I know I could have picked better names for the macros here.)

mark

unread,
Oct 12, 2015, 10:59:33 AM10/12/15
to
You are not the first one looking for nicer ways to unpack tuples. There
are some fairly ugly suggestions in this thread:

<https://groups.google.com/a/isocpp.org/forum/#!topic/std-proposals/4yWRnzX7J7Y>

David Brown

unread,
Oct 13, 2015, 3:50:12 AM10/13/15
to
I was thinking here about a way to get something usable /now/, rather
than for a future C++ enhancement. So I am interested in ideas to
improve the method I suggested. From the thread you posted, the idea of
using Rvalue references looks like an improvement:

#define autotie_2(v_, vt_, a1_, a2_) \
auto vt_ = v_; \
auto a1_&& = std::get<0>(vt_); \
auto a2_&& = std::get<1>(vt_)




But it is also interesting to think how it would be possible to improve
C++ in order to achieve effects like:

auto valid, res = square(x);

I think that to get that, or something similar such as "auto (valid,
res) = square(x);", would require making tuples first-class objects in
C++. Tuples would have to become part of the language, as they are in
Python, rather than a template in the library. I don't think that is a
change C++ is like to want to make.

But if C++ were to allow code like this:

T foo(void);
void bar(void) {
auto x;
...
x = foo();
}

to be treated as:

void bar(void) {
T x;
...
x = foo();

or perhaps

void bar(void) {
...
T x = foo();
}

then we would have a big step towards a good solution - and another neat
tool for other purposes:

void bar(void) {
auto x;
if (test) {
x = foo1();
} else {
x = foo2();
}
doSomething(x);
}

At the moment, code like that needs "x" to have an explicit type, or we
must use ?: rather than "if".

As far as I can see, the compiler has all the information it needs to
carry out these transformations, so it should be perfectly possible to
implement.

And this would give us:

auto valid, res;
std::tie(valid, res) = square(x);

Possibly it could be extended to:

std::tie(auto valid, auto res) = square(x);

I am not sure it would be possible to get any nicer without making
tuples part of the language.

0 new messages