C++ Weekly - Ep 441 - What is Multiple Dispatch (and how does it apply to C++?)

แชร์
ฝัง
  • เผยแพร่เมื่อ 11 พ.ย. 2024

ความคิดเห็น • 47

  • @anon_y_mousse
    @anon_y_mousse 2 หลายเดือนก่อน +8

    The way I've always dealt with these types of things, not that it comes up terribly often, is to use a bit pattern type ID for the various objects that need to be matched up, and to bitwise-or them together to switch on their meshed type ID's. This is actually one of those test cases where I know that people haven't read the AMD optimization manual. It has this technique in it to deal with condensing and optimizing giant if/else if/else chains in loops, amongst other uses.
    If anyone reads this, I'm going to recommend that you read both the Intel and AMD optimization manuals, because they don't merely give tips for writing better assembly, but also for implementing better and more optimized code patterns in all languages. If you can do it without thinking, then you've properly absorbed the lessons within.

    • @flemminggramchristensen6292
      @flemminggramchristensen6292 23 วันที่ผ่านมา +1

      Where do I find the AMD manual?

    • @anon_y_mousse
      @anon_y_mousse 23 วันที่ผ่านมา

      @@flemminggramchristensen6292 Ignore the date since most of it is still useful information, but try DDG with "amd software optimization guide". If you see a link to a PDF that has 47414 in its name, click that.

    • @anon_y_mousse
      @anon_y_mousse 23 วันที่ผ่านมา

      @@flemminggramchristensen6292 Never mind. Apparently TH-cam is just going to eat all of my posts.

  • @WilhelmDrake
    @WilhelmDrake 3 หลายเดือนก่อน +11

    Scott Meyers also covered the topic in his book "More Effective C++". Item 31: Making functions virtual with respect to more than one object.
    It was very interesting to get a modern take on the subject. Thank-you!

  • @broken_beyond_belief
    @broken_beyond_belief 3 หลายเดือนก่อน +5

    not even a cpp dev but still watching Jason Turner. Keep this up man ❤❤

  • @andrewduncan1217
    @andrewduncan1217 3 หลายเดือนก่อน +4

    A very interesting modern implementation of double-dispatch. Thanks.

  • @KhalilEstell
    @KhalilEstell 3 หลายเดือนก่อน

    This was great! I like the pattern and it looks very straight forward.

  • @DamianReloaded
    @DamianReloaded 3 หลายเดือนก่อน +6

    I've found that using overloaded constructors instead of functions makes the compiler generate less code.
    struct collide
    {
    collide(Craft &A, Craft &B) { A.x += B.y; }
    collide(Asteroid &A, Asteroid &B){ A.x += B.y; }
    collide(Craft &A, Asteroid &B){ A.x += B.y; }
    collide(Asteroid &A, Craft &B){ A.x += B.y; }
    };

    • @shipweck6253
      @shipweck6253 3 หลายเดือนก่อน +1

      wow this looks very clean

    • @ivanzhechev4215
      @ivanzhechev4215 3 หลายเดือนก่อน +3

      It generates 'less' code because the free functions must have an address. If you put them in an anonymous namespace it's the same code.

    • @DamianReloaded
      @DamianReloaded 3 หลายเดือนก่อน +2

      What about:
      template
      struct collide {
      constexpr collide(const T1 &A, const T2 &B) : m_a(A), m_b(B) { }
      constexpr operator int() const noexcept {
      return result_value;
      }
      int result_value = 0;
      const T1& m_a;
      const T2& m_b;
      };
      Or (edit: it's actually a bit larger)
      template
      struct collide {
      using vtype = std::variant;
      collide (const Craft &, const Craft &) noexcept {std::puts("C/C"); }
      collide (const Asteroid &, const Asteroid &) noexcept { std::puts("A/A");}
      collide (const Craft &, const Asteroid &) noexcept { std::puts("C/A");}
      collide (const Asteroid &, const Craft &) noexcept { std::puts("A/C");}
      collide(const vtype& v1,const vtype& v2) noexcept {
      std::visit(
      [this](const auto &lhs, const auto &rhs) {
      if (lhs.x == rhs.x && lhs.y == rhs.y) {
      collide(lhs, rhs);
      }
      }
      , v1, v2);
      }
      };
      std::vector get_objects()
      {
      return { {Craft(), Asteroid()} };
      }
      void process_collisions(const std::variant &obj) {

      for (const auto &other : get_objects()) {
      collide(obj, other);
      }
      }
      int main ()
      {
      for (const auto &obj : get_objects())
      process_collisions(obj);
      return 0;
      }

  • @ohwow2074
    @ohwow2074 3 หลายเดือนก่อน +11

    So this works when we don't have too many derived types and all are known at compile-time. It's cool. And I think it does not fully replace the first solution you had which was using dynamic polymorphism.

  • @pw1169
    @pw1169 2 หลายเดือนก่อน +1

    Very interesting! I hope i never see this in production code :D

  • @taw3e8
    @taw3e8 3 หลายเดือนก่อน +4

    Cool trick ^^

  • @TheSulross
    @TheSulross 3 หลายเดือนก่อน

    Swift enum and its pattern matching is one of the more attractive features of that language

  • @oschonrock
    @oschonrock 3 หลายเดือนก่อน +4

    very nice.. although If I needed this in such an example, I would seriously question if I have the right design?

    • @fernandoiglesiasg
      @fernandoiglesiasg 3 หลายเดือนก่อน +1

      In that case, what design would you suggest for the example?

    • @oschonrock
      @oschonrock 3 หลายเดือนก่อน

      @@fernandoiglesiasg It depends on the wider context. Perhaps make `collide` take a position{int x, int y} object produced from each space object. That makes it single dispatch on the objects and a single version of `collide`?

    • @cppweekly
      @cppweekly  2 หลายเดือนก่อน +2

      To me it's just an argument that we want pattern matching so we get this naturally when we need/want it.

    • @fernandoiglesiasg
      @fernandoiglesiasg 2 หลายเดือนก่อน

      Aha! That’s interesting, I think I hadn’t considered pattern matching in the context of multiple dispatching. I normally look into Julia doc when want to recall let’s say “multiple dispatching done right”.

  • @roco65tube
    @roco65tube 3 หลายเดือนก่อน

    thanks for the video Jason! It would have been great to see the reasoning why multiple disptach is not natively supported in c++

    • @cppweekly
      @cppweekly  2 หลายเดือนก่อน

      I think it's the kind of thing we're "waiting for pattern matching for" (if that ever comes...)

    • @flemminggramchristensen6292
      @flemminggramchristensen6292 23 วันที่ผ่านมา

      Things are only in C++ if they solve real world problems. Not just if they are interesting.
      Other early oo languages has it.

  • @grincha09
    @grincha09 3 หลายเดือนก่อน +1

    Nice

  • @darkmagic543
    @darkmagic543 3 หลายเดือนก่อน +9

    Oh that's a really cool pattern. On a personal note, I just don't like the trailing return types, It's just confusing when you see auto and sometimes scroll very far just to find the actual return type. I prefer wiriting everything before the return type on the line above.

    • @cppweekly
      @cppweekly  2 หลายเดือนก่อน

      But if you get used to it you don't like the old school way.
      This is the kind of thing I'm pretty agnostic on.

  • @Voy2378
    @Voy2378 2 หลายเดือนก่อน

    would be nice to see if boost::variant2 generates better code than std::variant

  • @mworld
    @mworld 3 หลายเดือนก่อน

    I implemented ChaiScript into a game I was creating. Unfortunately it remembers declarations and fails to execute a second+ time. Needs a reset on the chai object. Using Lua + Sol works in the way I expected.

    • @cppweekly
      @cppweekly  2 หลายเดือนก่อน

      yeah, that was a feature that was considered awhile back but never implemented.

  • @jakubskopal43
    @jakubskopal43 3 หลายเดือนก่อน +5

    The problem with trailing return types is that if you forget to write the trailing end ( -> CopyableMovableClass&) on a method returning a reference, you will end up copying (because you will have "auto getRefObject()" instead of "auto getRefObject() -> CopyableMovableClass&". If the object has copy constructor all will be compiled without a warning. Too bad that there is not a some kind of warning that user did not write trailing type at least on clang-tidy side.

    • @keris3920
      @keris3920 3 หลายเดือนก่อน +4

      I'm confused about your concern. If I forget an & on the legacy syntax, it will create a copy. Also, auto& my_func() is valid syntax, which I would argue is exactly as error prone in this respect as the legacy format

    • @jakubskopal43
      @jakubskopal43 3 หลายเดือนก่อน +2

      @@keris3920 It is not the same, the one you talking about is one problem which can be done even with trailing part. you can also write -> auto instead of a -> auto& but on top of that you can also miss out the trailing part without any warning. I see there two possibilities how to make unintented code instead of one.

    • @keris3920
      @keris3920 3 หลายเดือนก่อน

      @@jakubskopal43 your entire argument rests on the presumption that copies are a bad thing. Copies can often be faster and don't suffer from some of the lifetime issues that references suffer from (e.g. dangling references). Anyway, that's beside the point.
      The point I'm trying to make, is that regardless of the syntax you use, programmers will always make silly mistakes. I ultimately agree with you that a linter/formatter would help uncover some small bugs in this area. But I could probably list a seemingly infinite amount of bugs that could arise from forgetting parts of a function declaration. At the end of the day, I appreciate your warning, but I don't see trailing return type syntax as the root cause for this flavor of bug.

    • @jakubskopal43
      @jakubskopal43 3 หลายเดือนก่อน +2

      ​@@keris3920 I did not say that copies are bad thing. I explicitly said that you want to return reference .. you do not want to copy the object. It does not matter what I think about copies, it is about intent. And the intent was return a reference which can silently result in unintended code because you have two places to make a mistake instead of one.

    • @keris3920
      @keris3920 3 หลายเดือนก่อน

      @@jakubskopal43 organize your functions in the opposite order of which they're used so that the compiler complains about deducing the return type of the function before it's declared. That way you will get a compile error when you forget the return type. Problem solved.

  • @GeorgeTsiros
    @GeorgeTsiros 3 หลายเดือนก่อน

    is this "reflection at home"?

  • @dadisuperman3472
    @dadisuperman3472 3 หลายเดือนก่อน

    Available starting which version of C++

    • @oschonrock
      @oschonrock 3 หลายเดือนก่อน

      c++17, I think

    • @AruisDante
      @AruisDante 3 หลายเดือนก่อน +2

      std::variant and std::visit were added in 17.

    • @oschonrock
      @oschonrock 3 หลายเดือนก่อน

      @@AruisDante
      @dadisuperman3472
      yes 17. But you need the to deduction guide for the "visitor struct" option until c++20

  • @milasudril
    @milasudril 3 หลายเดือนก่อน +1

    The proper way to do it is to use the visitor pattern

  • @Voy2378
    @Voy2378 2 หลายเดือนก่อน

    it is so cringy that they used [[nodiscard]] instead of [[use]] because probably 0.0001% of lines of code have variable named use.

    • @spacey6960
      @spacey6960 2 หลายเดือนก่อน

      [[nodiscard]] makes more sense to me, and i ve never even used it. [[use]] sounds like a suggestion, not an obligation. Maybe something like [[mustuse]] would have been just as good

  • @sumitsatpute8906
    @sumitsatpute8906 2 หลายเดือนก่อน

    I can use this in our code but I will get a review comments 😂😂