9 comments

  • zengid 2 hours ago

    Heard about this watching Casey Muratori's "The Big OOPs" talk [0]. Thought it couldn't be _that_ Hickey, but turns out it was!

    [0] https://youtu.be/wo84LFzx5nI?si=SBv1UqgtKJ1BH3Cw&t=5159

  • mwkaufma an hour ago

    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?

    • tcbawo 32 minutes ago

      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.

      • mwkaufma a minute ago

        >> 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...?

  • drnick1 an hour ago

    C++ was so much cleaner in the 90s, when it was still essentially "C with classes," which is how I like to use the language. Modern standards have turned it into an ugly mess.

    • pton_xd 27 minutes ago

      I also use C++ as "C with classes," however I will concede that many of the modern C++ additions, particularly around templating, are extremely convenient. If you haven't had a chance to use requires, concepts, "using" aliases, etc I'd recommend giving them a try. I don't reach for those tools often, but when I do, they're way nicer than whatever this article is demonstrating from 1994! Oh yeah, also lambdas, those are awesome.

    • MomsAVoxell 7 minutes ago

      >ugly mess

      That may be the case, but there are plenty of examples of elegant implementations.

      JUCE, for instance:

          #include <juce_core/juce_core.h>
      
          class MyComponent {
          public:
              void doAsyncOperation(std::function<void(int)> callback) {
                  // Simulate async work
                  juce::MessageManager::callAsync([callback]() {
                      callback(42); // Call the functor with result
                  });
              }
          };
      
          // Usage
          MyComponent comp;
          comp.doAsyncOperation([](int result) {
              juce::Logger::writeToLog("Callback received with: " + juce::String(result));
          });
      
      .. I think that's kind of clean and readable, but ymmv, I guess?
    • lbalazscs 28 minutes ago

      A sentence from the article: "Given the extreme undesirability of any new language features I'd hardly propose bound-pointers now."

      It shows that C++ was considered too complex already in the 90s.

    • drob518 an hour ago

      Amen. The syntax just kept getting more and more complicated. I gave up in the late 1990s. Ironically for this post, I now prefer to write everything in Clojure. It seems like my own journey has paralleled Rich’s journey. Maybe that’s why I appreciate so many of the design choices in Clojure. It’s not perfect, but it’s really, really good.