C++ Weekly - Ep 333 - A Simplified std::function Implementation

Поділитися
Вставка
  • Опубліковано 26 гру 2024

КОМЕНТАРІ • 66

  • @RishabhDeepSingh
    @RishabhDeepSingh 2 роки тому +28

    This guy can write the whole standard library from scratch.

    • @destiny_02
      @destiny_02 2 роки тому +3

      he probably writes Microsoft's VC++ standard library

    • @ra2carrier749
      @ra2carrier749 2 роки тому

      @@destiny_02 Really, what's his role in MSVC team?

    • @destiny_02
      @destiny_02 2 роки тому

      @@ra2carrier749 idk, but he was a Microsoft MVP for sure.

    • @ra2carrier749
      @ra2carrier749 2 роки тому

      @@destiny_02 Huh, then it makes sense that he could implement a tiny-function so easily and quickly.

    • @Serenadio
      @Serenadio 2 роки тому +1

      and he will if we donate enough:)

  • @workasm83
    @workasm83 2 роки тому +9

    Note that, the original impl of std::function does not use type erasure with a virtual dispatch: instead, they keep a pointer to a static function declared within a class template. And for the most compact impl of a light-weight "function view" search for llvm function_ref which also uses "static function trick"

  • @khoalb
    @khoalb 2 роки тому +3

    Jason: I hope it gives you a little bit of an idea as the complexity that is required here to fully implement something like this.
    Me, who got lost 10 minutes in: ...yep.

  • @ruadeil_zabelin
    @ruadeil_zabelin 2 роки тому +23

    Unfortunately you chose to simply use a unique_ptr. In my opinion this is mostly interresting for creating an std::function that does not allocate on the heap at all, but reserves some space inside of itself and just uses inplace new (like std::function does). But instead of allocating when it's too large, you simply throw a compile error. This way you can enforce that within your codebase, using this function would not generate any overhead by silently allocating

    • @frydac
      @frydac 2 роки тому +1

      what do you mean, 'this is mostly interesting'? What is the 'this' you are referring to?

    • @ruadeil_zabelin
      @ruadeil_zabelin 2 роки тому +1

      @@frydac "this" refering to implementing a custom std function instead of using the standard one.

    • @frydac
      @frydac 2 роки тому +2

      Ah ok, I think the intention was purely educational, not trying to create an actual usable and useful alternative, not that both can't be achieved of course, but yeah, probably not in 21min :D

    • @ruadeil_zabelin
      @ruadeil_zabelin 2 роки тому +6

      @@frydac The difference isn't as big as you might think. Instead of the unique_ptr member you keep a small std::array of bytes (say 32 bytes), and instead of the make_unique you use in_place new on that buffer. You only need some checks to see if everything fits; but that can be a simple static_assert with a sizeof.

  • @OskarSigvardsson
    @OskarSigvardsson 2 роки тому +14

    I would love to see you keep working on this and implement the “small function optimization” thing, where if the function object is small, it doesn’t have to heap allocate. I’m assuming you do terrifying things with “union”? Or would std::variant work?

    • @AndrewCodeDev
      @AndrewCodeDev 2 роки тому +4

      I'd be interested in his take on this too. I've successfully done this with using local buffers (like std::aligned_storage... except that's getting deprecated) - basically, it either uses emplacement new if it's below a certain size or calls something like make unique.
      Anyhow, good suggestion, I'd watch that episode for sure.

    • @oschonrock
      @oschonrock 2 роки тому +1

      @@AndrewCodeDev Yeah exactly. Just placement new is sufficient for that.

  • @wilhelmherzner3016
    @wilhelmherzner3016 2 роки тому +7

    Neat! Interestingly this is exactly how Klaus Igelberger explained Type Erasure in his talk.
    I'm a big fan of std:: function and really eager to reduce the overhead in the setting where a class hierarchy is replaced by member std:: functions - does anybody know whether coding it manually avoids or alleviates the overhead compared to virtual function calls?
    I'd love to iterate over a vector of base class objects AND call the derived member functions...

    • @cppweekly
      @cppweekly  2 роки тому +2

      You might be interested in the idea of a "function view" which is complex in a different way.

  • @nmmm2000
    @nmmm2000 2 роки тому

    Thanks.
    I never realized std::function is so difficult to be implemented.
    This explains why is so slow - if is function pointer, it is double dispatch - from from Vtable, one from function pointer.
    Thanks again.

  • @_Omni
    @_Omni 2 роки тому +4

    We live in a simulation, I was going to implement this yesterday and I was looking for examples 🤯

  • @Omnifarious0
    @Omnifarious0 2 роки тому +2

    It's the handle/body design pattern! :-)

  • @Sebanisu
    @Sebanisu 2 роки тому +2

    I don't have a lot of experience with type erasure as I am avoiding inheritance. Though it's neat seeing it done like this nested and hidden inside of a class. I have always wondered how the return type( params) templates worked.

  • @r75shell
    @r75shell 2 роки тому

    Well, it doesn't explain why it's type-erasure, and actually it shouldn't. In case you wonder what is type-erasure watch talk by Klaus Igelberger. And to clarify, types everywhere. Just eventually stored value is actually an instance of templated interface. And implementation of virtual functions produced by powers of templates of C++.

  • @essenceidentity
    @essenceidentity 2 роки тому +2

    This was a great tutorial. Thanks alot Jason !! 🙂

  • @manuellernst3700
    @manuellernst3700 2 роки тому

    Great explanation. Just today I needed to use std::function for a project, where I needed a type able to store both function pointers and lambdas with captures, and wondered if there was an alternative to it as I only needed the operator() overload

  • @johnfoe3574
    @johnfoe3574 2 роки тому +1

    Sadly I don't have a clue what a std::reference_wrapper is.

  • @fixpontt
    @fixpontt 2 роки тому +3

    any chance you make videos about c++ coroutines?

    • @cppweekly
      @cppweekly  2 роки тому

      I do not currently, but I do have one planned.

  • @alexmomot6268
    @alexmomot6268 2 роки тому

    Thx a lot!
    Look forward to see the final implementation with member functions support. ;)

    • @cppweekly
      @cppweekly  2 роки тому +2

      That's automatically taken care of by `std::invoke` in the final version in the episode.

  • @daantimmer
    @daantimmer 2 роки тому +2

    I challenge you to make a simplified function implementation that does not use any sort of allocation. (With self imposed/chosen size limits)

    • @cppweekly
      @cppweekly  2 роки тому

      I could do one with a function pointer optimization that is still simple, but by the time you add in a small function optimization, things will necessarily get complicated.
      I added an episode idea to track this concept: github.com/lefticus/cpp_weekly/issues/98

    • @jdkoftinoff
      @jdkoftinoff 2 роки тому

      @@cppweekly Use the same pattern as small function optimization but make the max size of the buffer used for small function optimization itself a template parameter so you can specify your expected capture size and it would only go to heap if the actual size was too large for your expected capture size. You may still need to be able to handle special alignment requirements of the captured data (like for AVX or NEON maybe)

  • @oschonrock
    @oschonrock 2 роки тому +8

    Nice! add `override` ?

    • @ruadeil_zabelin
      @ruadeil_zabelin 2 роки тому +4

      Use final instead since you never intend to inherit from it further

    • @cppweekly
      @cppweekly  2 роки тому +2

      Whoops if I forgot override, or final.

  • @stefanmilenkovic4715
    @stefanmilenkovic4715 6 місяців тому

    Why would you use polymorphism?

  • @LongVu-sm8wm
    @LongVu-sm8wm 2 роки тому +1

    Hello, Could you please to explain why you declared the callable_interface as an abstract interface instead of a callable function pointer ?

    • @cppweekly
      @cppweekly  2 роки тому +1

      you have to have somewhere to hold the function object. In many cases it will be a simple function pointer (or something that can be converted to one) but certainly not in all cases. Example: a lambda with captures no longer works in that idea.

    • @LongVu-sm8wm
      @LongVu-sm8wm 2 роки тому

      @@cppweekly I got it, thanks.

  • @PaulMetalhero
    @PaulMetalhero 2 роки тому +2

    Nice code, Jason
    Please make a tutorial about creating a scripting language for modern C++!

    • @cppweekly
      @cppweekly  2 роки тому +2

      This has been on my to-do list for at least 3 years now. I keep starting the prototyping then giving up.
      The problem is: I know just how much work it would be! I spent too much time working on ChaiScript and it's hard to go down that road again.

  • @Clouds095
    @Clouds095 2 роки тому +1

    Please do a video on how to pass smart pointers to functions (i.e shared_ptr)

    • @cppweekly
      @cppweekly  2 роки тому

      That's a great idea, please add it to the episode idea tracker: github.com/lefticus/cpp_weekly/issues/

  • @theoathman8188
    @theoathman8188 2 роки тому

    very fascinating. I always wanted to poke around the implementation of std::function and discover how it works. This is always in the back of my mind.
    I'm left to wonder though, why this doesn't work on c++11 and what changes do I need to make to make it work there ? Looks like c++20 solved something that c++11 can't handle and I can't put my finger on it..

    • @theoathman8188
      @theoathman8188 2 роки тому

      ops... turns out std::make_unique is a c++14 and onwards thing, so:
      callable { new callable_impl (std::move(fo)) }
      works fine in c++11
      The compilers was acting weird though. It gave me a note that this is a c++14 thing but then change it's mind to somethin like "'make_unique' is not a member of 'std' "

  • @__hannibaalbarca__
    @__hannibaalbarca__ Рік тому

    Where can I found all your book

    • @cppweekly
      @cppweekly  Рік тому

      I try to keep updated links for things like that in my video descriptions. Here they are: leanpub.com/cppbestpractices
      amzn.to/3wpAU3Z

  • @roynevo367
    @roynevo367 2 роки тому

    Thank you!
    I'm a big fan of this channel.
    Can you elaborate what you meant by compile time and runtime type erasure?
    I mean what's the definition of this, is there a type erasure technique which is only done at compile time and is there one for runtime? Or is it always simply what you've done here which use compile time(templates) and runtime(polymorphism) concepts?

    • @cppweekly
      @cppweekly  2 роки тому

      Yes, I really was eluding to compile time polymorphism vs runtime polymorphism.

  • @tauicsicsics
    @tauicsicsics 2 роки тому

    Is there any chance to have a std function noexcept? In c++11 i could, on c++17 i cannot anymore...

    • @cppweekly
      @cppweekly  2 роки тому

      noexcept became part of the type system in C++17, which is probably what changed things for you.

  • @BILL1986able
    @BILL1986able 2 роки тому

    Can you share the compiler explorer link so that we can play with the code ourselves? Thank you

    • @cppweekly
      @cppweekly  2 роки тому +1

      Sorry, I wrote that ad-hoc and forgot to save the link!

  • @manuellernst3700
    @manuellernst3700 2 роки тому

    Is this suitable or safe for production?

    • @cppweekly
      @cppweekly  2 роки тому

      Technically, but it is incompletely in inefficient. Just use std::function instead.

  • @urizilber7545
    @urizilber7545 2 роки тому

    Wonderful

  • @jhbonarius
    @jhbonarius 2 роки тому +3

    333!

  • @takiemtam1
    @takiemtam1 Рік тому

    Can we call this over programming ? oh my god

  • @abdullasulfikkar5282
    @abdullasulfikkar5282 2 роки тому +1

    Nice

  • @anon_y_mousse
    @anon_y_mousse 2 роки тому +1

    Hm. I lost 7 minutes. Oh well, I got the gist anyway.

  • @jeanehu-pludain6640
    @jeanehu-pludain6640 2 роки тому

    I'm only one to be terrified ?😅

  • @leshommesdupilly
    @leshommesdupilly Рік тому

    Complicated way of writing "mov eax, 3"

  • @sanderjobing
    @sanderjobing 2 роки тому

    I used C++ 14 for compatibility reasons and in contrast to storing a heap-allocated std::unique_ptr, I was able to store a pointer to a function instead. Indeed surprisingly, you can call it with lambda as well. Complete example:
    #include
    namespace solly
    {
    template
    class function;
    template
    class function
    {
    public:
    typedef Ret (*functionptr_t)(Param...);
    function() = delete;
    function(functionptr_t f) : m_funcPtr(f)
    {
    }
    Ret operator() (Param... params)
    {
    return m_funcPtr(params...);
    }
    private:
    functionptr_t m_funcPtr;
    };
    } // namespace
    int f(int x, int y)
    {
    return (x + y);
    }
    int main()
    {
    solly::function func(f);
    solly::function func2([] (int a, int b)
    {
    return a + b;
    });
    std::cout

    • @sanderjobing
      @sanderjobing 2 роки тому

      @Gijsbert Dos Santos Thanks, I was wondering if there was a catch. Turns out there is.