Open this wandbox
(backup, backup).
The file "unique-function.h" contains a complete implementation of
unique_function<R(A...)>
.
1. Read through the code. (It's only 128 lines! It should take about 10 minutes.) Here are some questions to guide your study:
sizeof(ContainerVtable<R(A...)>)
? Does it depend on
the types involved in R(A...)
? (24 bytes. No.)move_to
end with a call to self.reset()
? (To preserve
an ownership invariant.)sizeof(unique_function<R(A...)>)
? Does it depend on
the types involved in R(A...)
? (8 + SIZE bytes. No.)unique_function::swap
? Could we conceivably
do "better"? (Consider what happens when we swap
two unique_function objects
where one object's HeldType
is unique_ptr<BigWidget>
and
the other's is unique_ptr<BigGadget>
. Does swap
cause any
calls to either ~BigWidget
or ~BigGadget
?)2. (Optional) Implement the "type-unerasure" primitive
const std::type_info& unique_function::type() constby adding a new entry to
ContainerVtable
. It should take you 9 lines,
or maybe 10 depending on your preferred brace style.
Open this wandbox
(b, b, b).
The file "unique-function.h" contains a complete implementation of
unique_function<R(A...)>
. The file "old-unique-function.h"
is just a copy of the version from Exercise 8a, so that you have it handy for
comparison.
1. Read through the code. How does it differ from the code in Exercise 8a?
vtable_in_code
is new; it replaces the old code's
array-of-function-pointers vtable
. Its parameter list is basically
the union of all the parameter lists of the old vtable methods... except for
the old method R call(A...)
. Could vtable_in_code
take A...
and return something like optional<R>
?
(Not really. Consider what happens if A...
are reference types,
for example.)
MOVE_TO
and DESTROY
do not return
function pointers, but you could imagine that they once did, before we applied some
optimization. By not returning function pointers, and just doing the work
directly inside vtable_in_code
, we are basically inlining one function call.
void(*)()
instead of TrampolineType*
if we wanted, and
just reinterpret_cast
it back in the caller.
vtable_in_code
mechanically, by always returning
function pointers (cast to void(*)()
and cast back in our caller),
and then mechanically "inlining" any functions whose signatures involve only
POD or pointer types.
2. (Optional) Implement the "type-unerasure" primitive
const std::type_info& unique_function::type() constby adding a new entry to
ContainerVtableImpl
. It should take you about 8 lines.
You'll have to add a new enumerator to VtableIndex
. You can either return a
function pointer using the casting trick above, or you can add a new POD parameter to
vtable_in_code
.
static auto vtable_in_code(UF& self, VtableIndex vtable_index, UF *dest, const std::type_info **outp) -> typename UF::TrampolineType*
3. (Optional) Implement unique_function::type()
by giving unique_function
a non-static data member of type const std::type_info *
. Was that easier?
What are the pros and cons of this approach?
Consider the "don't pay for what you don't use"
philosophy of C++. Should people be "using" the type()
method? Why or why not?
You're done with this set of exercises! Sit back and relax, or optionally, browse the following resources.