Red flags for me when I see nonstandard functors in a c++ codebase (esp if the "glue" is in a setup function independent of the objects):

(i) Have they thought about the relative lifetimes of the sender and receiver?

(ii) Is the callback a "critical section" where certain side-effects have undefined behavior?

(iii) Does the functors store debugging info that .natvis can use?

(iv) Is it reeeeeeeally that bad to just implement an interface?

> Red flags for me when I see nonstandard functors in a c++ codebase

Even if it's 1994???

Yes in 1994 I had these exact judgements, at age 11 :P

Clever kid

Can you elaborate on your third point? What would a class need to do to affect debugging info?

Regarding your fourth point, sometimes an architecture can be vastly simplified if the source of information can abstracted away. For example, invoking a callback from a TCP client, batch replay service, unit test, etc. Sometimes object oriented design gets in the way.

To your first point, I think RAII and architecture primarily address this. I'm not sure that I see callback implementation driving this. Although I have seen cancellable callbacks, allowing the receiver to safely cancel a callback when it goes away.

>> Can you elaborate on your third point? What would a class need to do to affect debugging info?

Common implementations are a function pointer + void* pair, which in most debuggers just show you two opaque addresses. Better to include a info block -- at least in debug builds -- with polymorphic type pointers that can actually deduce the type and show you all the fields of the receiver.

>> sometimes an architecture can be vastly simplified if the source of information can abstracted away.

"sometimes" is doing a lot of heavy lifting here. That's my whole point -- more often than not I see some type of homespun functor used in cases that are _not_ simplified, but actually complicated by the unnecessary "plumbing."

>> RAII and architecture primarily address this

If the receiver uses RAII to clean up the callback, then you've reintroduced the "type-intrusiveness" that functors are meant to avoid...?

> most debuggers just show you two opaque addresses

This has not been my experience. But I haven't needed to deal with RTTI disabled.

By RAII, I mean using destructors to unregister a callback. This covers 99.9% of use cases. Generally callback registration is not where you really want type erasure anyways.

>> By RAII, I mean using destructors to unregister a callback.

_Whose_ destructor, if not the receiving-type? Is there a third "binding" object, because then you have three potentially-unrelated lifetimes.

>> Generally callback registration is not where you really want type erasure anyways.

I'm responding to the article: "Some mechanisms for doing callbacks require a modification to, or derivation of, the caller or callee types. The fact that an object is connected to another object in a particular application often has nothing to do with its type. As we'll see below, mechanisms that are type intrusive can reduce the flexibility and increase the complexity of application code. "

> Whose_ destructor, if not the receiving-type

The receiving type should control the lifetime of any callbacks to itself that it gives away. The destructor is the best place to ensure this gets properly cleaned up.

Like anything, custom callbacks can be used well or misused. Design is a matter of expertise and taste bordering on an art form. Connecting framework implementation and business logic can be done cleanly or clumsily. I am skeptical of an argument that callbacks have a code smell prima facie.

I don't disagree, but the article does. RH describes an architecture where setup functions create callbacks, independent of the receiving type. If I were to steelman him, it would be something like this: "in a pedantic MVC system, model objects don't depend on view objects by design, and therefore should not be aware that their methods are used as 'click callbacks'"

The only "standard" in 1994 was the C++ARM book (filled a similar role to the K&R C book) that served as basis for the ongoing standardization process, to be done in 1998.

Apologies, I didn't mean "standard" as in the C++ language standard, but standard as in provided-by-your-app-framework's-foundation-library.