>> I would have to create a base class for every unique type of the callback and then for every caller possibly a new class deriving.
An interface declaration is, like, two lines. And a single receiver can implement multiple interfaces. In exchange, the debugger gets a lot more useful. Plus it ensures the lifetime of the "callback" and the "context" are tightly-coupled, so you don't have to worry about intersecting use-after-frees.
This is pithy and clever, but in exchange makes the code less obviously self-documenting (implementation details creeping into type declarations) and complicates implementing multiple interfaces on the same receiver.