
On Mon, Apr 4, 2011 at 3:33 PM, Dave Abrahams <dave@boostpro.com> wrote:
At Mon, 4 Apr 2011 10:36:29 -0700, Jeffrey Lee Hellrung, Jr. wrote:
[...]
an alternate rvalue reference emulation, one which allows rvalues ***of any arbitrary (move-emulation-enabled) type*** to be captured in C++03 and identified as rvalues, rather than references-to-const (there is a drawback, though, which I'll get to below; as usual, there's no "free lunch").
I'm actually not sure there's no free lunch...
I also forgot to mention that (obviously) this precludes polymorphic return types :( At least, I don't see how they can be supported...
Instead of using rv<T>& to capture arbitrary rvalues, I'll use a
kind of type-erased emulated rvalue reference, generic_rv<...> (for
I think you should spell out "..." as "F, Result" in any descriptions or it just gets confusing. "Is he talking about variadic templates," I wondered?
Ai, good suggestion...I have yet to move to a compiler supporting variadic templates.
lack of a better name at the moment), which will just store a void
pointer to the original, pre-type-erased object. Of course, we need some way to recover the object back from the void pointer, and that's what the "..." template parameters are for. This is where the (unfortunate) runtime overhead comes in, as some dispatching mechanism (e.g., function pointers) needs to be used to effect the recovery of the original, pre-type-erased object.
First, did you try this with function references instead of function pointers?
Now I did. Will provide assembly listing below. It looks like it doesn't get inlined :( Next, which compilers did you try this on?
MSVC9 only. I've found that when everything is inlined and flow analysis allows
the compiler to "see" the source of function pointers/references, many compilers can turn an indirect call into a direct one.
Perhaps GCC will do better. I'll try to generate the assembly output in that case...
The important
difference between rv<T> and generic_rv<...> is that there's no T in the template parameter list "..." of generic_rv; more to the point, its template parameters are not deduced within the context of binding function arguments of some_fn, hence the compiler will be allowed to look for conversions from arbitrary rvalues to generic_rv<...>. Hopefully that gives enough of an introduction to the idea. Further details are probably best left to a code snippet.
Comments? Specifically, should this be considered as an addition to Boost.Move's current emulation framework?
Yes!
I appreciate your enthusiasm, Dave :) I've attached generic_rv_test.cpp and generic_rv_test.asm. I've also reproduced the relevant assembly listings below, as generated by MSVC9. First, the assembly listing for the member function generic_rv<some_fn,void>::cast_forward<X>: ; COMDAT ??$cast_forward@UX@@@?$generic_rv@Usome_fn@@X@@SAXUsome_fn@ @QAX@Z _TEXT SEGMENT _f$ = 8 ; size = 1 _p$ = 12 ; size = 4 ??$cast_forward@UX@@@?$generic_rv@Usome_fn@@X@@SAXUsome_fn@@QAX@Z PROC ; generic_rv<some_fn,void>::cast_forward<X>, COMDAT ; 41 : static Result cast_forward(F f, void* const p) mov eax, DWORD PTR __imp_?endl@std@@YAAAV?$basic_ostream@DU ?$char_traits@D@std@@@1@AAV21@@Z mov ecx, DWORD PTR __imp_?cout@std@@3V?$basic_ostream@DU ?$char_traits@D@std@@@1@A push eax push OFFSET ??_C@_0CC@IOIEODGA @some_fn?3?3operator?$CI?$CJ?$CIrv?$DMT?$DO?$CG?$CJ?5cons@ push ecx call ??$?6U?$char_traits@D@std@@@std@@YAAAV?$basic_ostream@DU ?$char_traits@D@std@@@0@AAV10@PBD@Z ; std::operator<<<std::char_traits<char>
add esp, 8 mov ecx, eax call DWORD PTR __imp_??6?$basic_ostream@DU?$char_traits@D@std@@@std@ @QAEAAV01@P6AAAV01@AAV01@@Z@Z ; 42 : { return f(static_cast< rv<T>& >(*static_cast< T* >(p))); } ret 0 ??$cast_forward@UX@@@?$generic_rv@Usome_fn@@X@@SAXUsome_fn@@QAX@Z ENDP ; generic_rv<some_fn,void>::cast_forward<X> ; Function compile flags: /Ogtpy _TEXT ENDS Looks like a direct to call to printing to std::cout. Next, the assembly listing for the expression some_fn()(make<X>()): lea ecx, DWORD PTR $T25928[esp+8] mov BYTE PTR $T25927[esp+8], 0 mov edx, DWORD PTR $T25927[esp+8] push ecx push edx call ??$cast_forward@UX@@@?$generic_rv@Usome_fn@@X@@SAXUsome_fn@ @QAX@Z ; generic_rv<some_fn,void>::cast_forward<X> This appears to call generic_rv<some_fn,void>::cast_forward<X>. I don't know, is this basically the code that invokes the function reference? (Might have to dig through the asm file to answer that.) Lastly, the assembly listing for the expression some_fn()(static_cast< rv<X>& >(make<X>())): mov eax, DWORD PTR __imp_?endl@std@@YAAAV?$basic_ostream@DU ?$char_traits@D@std@@@1@AAV21@@Z add esp, 8 push eax push ecx mov ecx, DWORD PTR __imp_?cout@std@@3V?$basic_ostream@DU ?$char_traits@D@std@@@1@A push OFFSET ??_C@_0CC@IOIEODGA @some_fn?3?3operator?$CI?$CJ?$CIrv?$DMT?$DO?$CG?$CJ?5cons@ push ecx call ??$?6U?$char_traits@D@std@@@std@@YAAAV?$basic_ostream@DU ?$char_traits@D@std@@@0@AAV10@PBD@Z ; std::operator<<<std::char_traits<char>
add esp, 12 ; 0000000cH mov ecx, eax call DWORD PTR __imp_??6?$basic_ostream@DU?$char_traits@D@std@@@std@ @QAEAAV01@P6AAAV01@AAV01@@Z@Z Ouch...that gets completely inlined, unlike the previous case... - Jeff