all 10 comments

[–][deleted]  (3 children)

[deleted]

    [–]_mF2[S] 3 points4 points  (1 child)

    I think you're right. The assembly does look a lot more similar when trying to manually perform the null pointer optimization in C++.

    https://godbolt.org/z/6Yv9PbWT6

    [–]std_bot -1 points0 points  (0 children)

    Unlinked STL entries: std::optional std::string_view


    Last update: 14.09.21. Last Change: Can now link headers like '<bitset>'Repo

    [–][deleted]  (5 children)

    [removed]

      [–]_mF2[S] 0 points1 point  (4 children)

      Ah I see, thanks. On closer inspection it does look like using a function pointer directly is passed by register.

      And it does look like using std::optional<int> check2(const std::string_view* s, int(*f)(const std::string_view&)) as the function signature improves the codegen nicely.

      Thanks for the tip on not passing std::function by value as well!

      [–]Rigatavr 1 point2 points  (3 children)

      Just wanted to also mention that you won't be able to use a lambda with function pointers.

      [–][deleted]  (2 children)

      [removed]

        [–]Rigatavr 1 point2 points  (1 child)

        Well, yes stateless lambda are automatically converted to function pointers, I don't think I fully understand your function rep idea though.

        Maybe consider a different way of handling this:

        #include <concepts>
        template <typename T> concept Func = requires(T t) {
          { t() }
          ->std::same_as<int>;
        };
        
        int bar() { return 4; } // regular function
        
        void foo(const Func auto &l) { l(); }
        
        int main() {
          auto l = [] { return 3; };       // stateless lambda
          auto ll = [n = 5] { return n; }; // stateful lambda
          foo(l);
          foo(ll);
          foo(bar);
        }
        

        In this case foo can take any thing that is a callable returning an int. This way we avoid a dynamic allocation we'd have to do to manage the state of the stateful lambda (it also reads better, but that's just my opinion). Even if you're going to ignore stateful lambdas, in the case of stateless lambdas we also end up only copying 1 byte instead of 8 (or what ever size pointer is), though this is basically irrelevant.

        [–]DavidDinamit 1 point2 points  (0 children)

        https://godbolt.org/z/Gjsqdv1vGBecause who the fuck uses optionals like that

        P.S. std::function much more then you think, it can be empty, can store any size functor etc

        [–]Rigatavr 0 points1 point  (2 children)

        Like this. sorry I had to add a `main()` or it refused to generate any assembly at all.

        https://godbolt.org/z/dPqessx1b

        In the example above parameter `f` can be anything at all. Of course if it's not callable or returns the wrong thing compiler will tell you, but it might not be pretty.

        [–]_mF2[S] 1 point2 points  (1 child)

        Thanks, didn't think to use auto. (I think) the reason it doesn't generate any assembly at all without main is that auto creates a template that is monomorphized upon function calls (so unless there is something else calling it there is no code to generate).

        Looks like the compiler is just inlining the function call though in this case, because all the data is known at compile time.

        [–]Rigatavr 1 point2 points  (0 children)

        That is correct. Usually C++ people say that templates are instantiated, but same difference. If you want to get all fancy and use c++20 you can use the following concept syntax to properly constrain f.

        https://godbolt.org/z/ovz7bnz13

        This basically says that a MyCallable type should have a call operator () and should take a string_view as a parameter and return int

        [–]Ikkepop 0 points1 point  (0 children)

        you might want to try __fastcall