Library: continue Investigation with workaround, inconclusive yet

A simple yet weird workaround (and basically equivalent to our helper function)
is to wrap the argument tuple itself into std::forward<Args> -- which has the
effect of exposing RValue references to the forwarding function, thus silencing
the compiler.

I am not happy with this result, since it contradicts the notion of perfect forwarding.

As an asside, the ressearch has sorted out some secondary suspicions..
- it is *not* the Varargs argument pack as such
- it is *not* the VerbToken type as such

The problem clearly is related to exposing tuple elements to a forwarding function.
This commit is contained in:
Fischlurch 2019-04-20 17:27:47 +02:00
parent 805d83c2db
commit 5191073558
3 changed files with 46 additions and 34 deletions

View file

@ -46,7 +46,14 @@
/** @file try.cpp /** @file try.cpp
* Research how to apply a tuple to a varargs function forwarder. * Research how to apply a tuple to a varargs function forwarder.
* The recent stadard library has a std::apply, which we can not yet use, unfortunately. * The recent standard library has a std::apply, which we can not yet use, unfortunately.
* @note this research remains inconclusive. As far as I can see, the simplified setup
* exactly mimics the problematic call situation; however, in the real use case,
* we need to std::forward<Args> the argument tuple object field while here in
* this simplified case, it compiles just fine without -- as it should after all,
* since that is the whole point of perfect forwarding; std::get should expose
* a LValue reference to the tuple element, and we pass that through a forwarding
* function into the double dispatch to the receiving visitor.
*/ */
typedef unsigned int uint; typedef unsigned int uint;
@ -73,7 +80,7 @@ using std::tuple;
template<typename FUN, typename...ARGS> template<typename FUN, typename...ARGS>
void void
forwardInvoker (FUN fun, ARGS&&... args) forwardInvoker (FUN& fun, ARGS&&... args)
{ {
cout << "forwardInvoker...\n" cout << "forwardInvoker...\n"
<< lib::test::showVariadicTypes(args...) << lib::test::showVariadicTypes(args...)
@ -81,28 +88,39 @@ forwardInvoker (FUN fun, ARGS&&... args)
fun (std::forward<ARGS>(args)...); fun (std::forward<ARGS>(args)...);
} }
template<typename FUN, class TUP, size_t...idx> template<typename FUN, typename...ARGS>
struct Holder
{
using Tup = tuple<ARGS...>;
Tup tup;
Holder (Tup& tup)
: tup{tup}
{ }
template<size_t...idx>
void void
unpack_and_forward (FUN&& fun, TUP& tup, lib::meta::IndexSeq<idx...>) unpack_and_forward (FUN& fun, lib::meta::IndexSeq<idx...>)
{ {
cout << "unpack_and_forward...\n"; cout << "unpack_and_forward...\n";
SHOW_TYPE (TUP) SHOW_TYPE (Tup)
forwardInvoker (std::forward<FUN>(fun), std::get<idx> (std::forward<TUP>(tup))...); forwardInvoker (fun, std::get<idx> (tup)...);
} }
template<typename FUN, typename...ARGS>
void void
applyTuple (FUN&& fun, tuple<ARGS...>& args) applyTuple (FUN& fun)
{ {
using Tup = tuple<ARGS...>;
cout << "applyTuple...\n"; cout << "applyTuple...\n";
SHOW_TYPE (Tup) SHOW_TYPE (Tup)
using SequenceIterator = typename lib::meta::BuildIdxIter<ARGS...>::Ascending; using SequenceIterator = typename lib::meta::BuildIdxIter<ARGS...>::Ascending;
unpack_and_forward (std::forward<FUN>(fun), args, SequenceIterator()); unpack_and_forward (fun, SequenceIterator());
} }
};
@ -110,13 +128,15 @@ applyTuple (FUN&& fun, tuple<ARGS...>& args)
int int
main (int, char**) main (int, char**)
{ {
auto tup = std::make_tuple(1,2,3u); auto tup = std::make_tuple(1,2,3);
auto fun = [](int a, int b, int c) auto fun = [](int a, int b, int c)
{ {
cout << a<<"+"<<b<<"+"<<c<<"="<<(a+b+c)<<endl; cout << a<<"+"<<b<<"+"<<c<<"="<<(a+b+c)<<endl;
}; };
applyTuple (fun, tup); using Hol = Holder<decltype(fun), int, int, int>;
Hol holder(tup);
holder.applyTuple (fun);
cout << "\n.gulp.\n"; cout << "\n.gulp.\n";
return 0; return 0;

View file

@ -70,21 +70,6 @@ namespace lib {
{ {
return argStorage + VERB_TOKEN_SIZE; return argStorage + VERB_TOKEN_SIZE;
} }
/**
* Helper: pass a reference to an tuple element into a "perfect forwarding" call
* This is the equivalent of calling std::forward<TY> (TY&)
* ////////////////////////////////////////////////////////////////////TODO verify this reasoning is sound!!
*/
template<size_t idx, typename...ARGS>
decltype(auto) constexpr
get_ref (std::tuple<ARGS...>& tuple)
{
using Elm = std::remove_reference_t<
std::tuple_element_t<idx, std::tuple<ARGS...>>>;
return static_cast<Elm&&> (std::get<idx> (tuple));
}
} }
template<class REC, class RET> template<class REC, class RET>
@ -126,8 +111,8 @@ namespace lib {
template<size_t...idx> template<size_t...idx>
RET RET
invokeVerb (REC& receiver, meta::IndexSeq<idx...>) invokeVerb (REC& receiver, meta::IndexSeq<idx...>)
{ { //////////////////////////////////////////TICKET #1006 | TICKET #1184 why do we need std::forward here? the target is a "perfect forwarding" function, which should be able to receive a LValue reference to the tuple element just fine...
return verb_.applyTo (receiver, get_ref<idx> (args_)...); return verb_.applyTo (receiver, std::get<idx> (std::forward<Args>(args_))...);
} }
}; };
@ -150,6 +135,13 @@ namespace lib {
VerbPack (Handler<ARGS...> handler, Literal verbID, ARGS&&... args) VerbPack (Handler<ARGS...> handler, Literal verbID, ARGS&&... args)
: PolyHolder(PayloadType<ARGS...>(), handler, verbID, std::forward<ARGS>(args)...) : PolyHolder(PayloadType<ARGS...>(), handler, verbID, std::forward<ARGS>(args)...)
{ } { }
RET
applyTo (REC& receiver)
{
VerbInvoker<REC,RET>& dispatch(*this);
return dispatch.applyTo (receiver);
}
}; };

View file

@ -161,11 +161,11 @@ namespace test{
render_verbose (TokenSeq& tokens) render_verbose (TokenSeq& tokens)
{ {
VerboseRenderer receiver; VerboseRenderer receiver;
// for (Token tok : tokens) for (Token tok : tokens)
// cout << "dispatching " << tok cout << "dispatching " << tok
// << " -> '" << " -> '"
// << tok.applyTo(receiver) << tok.applyTo(receiver)
// << "'\n"; << "'\n";
} }
}; };