Skip to content

Feedback: Type Erasure (mainly), also need to read _together_ with source code #1

Description

@david-bakin

[I'm just getting started. I can tell it's going to be good material here, from Chapter 1. So, in the hopes of providing the feedback you requested, I'll post this (and possibly others soon)]

What did you look at?

I've read through the end of Chapter 3 (Type Erasure).

What worked well or felt clear?

Chapter 1 was great: Good material, organized well.

Also Author's Note very nicely explained the "Development Tools used in this Book". Nothing surprising to me (except possibly recommending QT Creator) but it was good to have it all spelled out.

W.r.t. Chapter 3 - everything except what I point out in point #3 below was well done - description of type erasure and the use cases (and non use cases) are easy to understand (and are, IMO, correct), as is the description of how it applies to the running example (the concrete measurement types and their use in the example app).

What was confusing, surprising, or could be improved?

  1. First thing noticed after finishing Chapter 3 - this chapter really needs to be read with the source code from the repo up on another window on your machine (i.e., you can't really read this chapter with an ebook reader unless you've printed a copy of the source code).

    Now, I want to make it clear: I like this. It's easy to arrange if you're reading on a PC in the first place (because you might very well want to compile/run the examples - though in fact I like reading this kind of book away from a machine and going to the machine later to run examples), and (for me anyway) it's easy to print the source code and have it available. And I seriously do not like ebooks with page after page of badly formatted code stuck in them (badly formatted because the ebook page width is narrow and varies according to the font size you've chosen). (And I don't like print books that do that for the same reason - just bloats the book and makes it more expensive, but that's a different thing.)

    Anyway, what I'm pointing out here is that the chapter is written as if it is self-contained, e.g., "In this chapter we will build ..." when actually that doesn't happen at all. It just jumps into a discussion involving types AnyMeasurement, IMeasurementModel, and Model<T> without even a diagram showing how they're related, or a summary of the actual interface, or anything like that. It's all clear if you are looking separately at the source code but not from the chapter. I kept thinking that the code would eventually appear, or a diagram, or something. So it's more like "In this chapter we will describe ..."

    Since this is a different way of writing a book of this kind than is usually seen (the first I've seen, actually) a sentence saying how the reader should be prepared - with the source code at hand and visible - would clue the reader in that the material is not self-contained and really needs the source code for understanding.

  2. Source code for Chapter 3 is quite clear - and nicely brief. Only modification I'd make is to highlight (with // ---- delimiters and comments the actual "measurement" interface that is the external polymorphism that is happening - i.e., the methods header() and operator <<(). All the rest is simply boilerplate for the wrapper type AnyMeasurement that any/all type-erased containers need. I'd actually move header()s and operator<<() to the top of the class with all else to follow - for this kind of thing that's a reasonable exception to the usual conventions about ordering methods in a class decl.

  3. (Here's the reason I was really motivated to write this) I consider this to be a "semantic" mistake - though this is my opinion and I'd like to hear your thoughts. First paragraph of Chapter 3, after talking about std::function as a type-erased type, you state:

    The classes std::any and std::shared_ptr are other good examples of type erasure.

    But (IMO) they're not. They're both containers - in the first case a container of "any type", in the second case of a pointer to an "any type". But there's no (external) polymorphism involved: Neither one actually forwards any behavior to the contained class. They each can cough up the contained instance to use directly (via any::any_cast and shared_ptr operator{*,->,[]}() but don't actually do anything with the contained type. (The shared_ptr situation is a bit obscured due to the syntax of using operator ->() but that's just an address redirection, not a behavior forwarding operation.)

    I think my interpretation does not match with what other people think - e.g. see https://cppcheatsheet.com/notes/cpp/cpp_type_erasure.html which was the first page offered by duckduckgo for the search "c++ standard library type erasure" - which explains about "hold[ing] any concrete object that satisfies a behavioral contract" (which I think is referring to the interface which is intended to become polymorphic via the wrapper) and then says that std::any, std::move_only_function, and the polymorphic allocators in <memory_resource> are "all type-erased wrappers". std::move_only_function is a type-erased type just like std::function. A std::polymorphic_allocator<> is too - the allocation protocol is the polymorphic part. But again, I disagree about std::any.

    (To be clear: std::any uses "type erasure" to hold an object of any type and be able to recover it as that type without any support from that type. It just doesn't support any polymorphic behavior on the contained object that I can see.)

Additional thoughts

Really looking forward to reading the rest... which will happen very shortly...

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions