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
* 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;
@ -73,7 +80,7 @@ using std::tuple;
template<typename FUN, typename...ARGS>
void
forwardInvoker (FUN fun, ARGS&&... args)
forwardInvoker (FUN& fun, ARGS&&... args)
{
cout << "forwardInvoker...\n"
<< lib::test::showVariadicTypes(args...)
@ -81,28 +88,39 @@ forwardInvoker (FUN fun, 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
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";
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
applyTuple (FUN&& fun, tuple<ARGS...>& args)
applyTuple (FUN& fun)
{
using Tup = tuple<ARGS...>;
cout << "applyTuple...\n";
SHOW_TYPE (Tup)
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
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)
{
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";
return 0;

View file

@ -70,21 +70,6 @@ namespace lib {
{
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>
@ -126,8 +111,8 @@ namespace lib {
template<size_t...idx>
RET
invokeVerb (REC& receiver, meta::IndexSeq<idx...>)
{
return verb_.applyTo (receiver, get_ref<idx> (args_)...);
{ //////////////////////////////////////////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, std::get<idx> (std::forward<Args>(args_))...);
}
};
@ -150,6 +135,13 @@ namespace lib {
VerbPack (Handler<ARGS...> handler, Literal verbID, 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)
{
VerboseRenderer receiver;
// for (Token tok : tokens)
// cout << "dispatching " << tok
// << " -> '"
// << tok.applyTo(receiver)
// << "'\n";
for (Token tok : tokens)
cout << "dispatching " << tok
<< " -> '"
<< tok.applyTo(receiver)
<< "'\n";
}
};