Comment by kjksf

Comment by kjksf 6 months ago

22 replies

How is Func0 / Func1<T> better than std::function?

Smaller size at runtime (uses less memory).

Smaller generated code.

Faster at runtime.

Faster compilation times.

Smaller implementation.

Implementation that you can understand.

How is it worse?

std::function + lambda with variable capture has better ergonomics i.e. less typing.

akdev1l 6 months ago

I think none of these points are demonstrated in the post hence I fail to visualize it

Also I copy pasted the code from the post and I got this:

test.cpp:70:14: error: assigning to 'void ' from 'func0Ptr' (aka 'void ()(void *)') converts between void pointer and function pointer 70 | res.fn = (func0Ptr)fn;

  • kjksf 6 months ago

    Thanks, fixed.

    It works in msvc but as someone pointed out, it was a typo and was meant to be (void*) cast.

  • cryptonector 6 months ago

    > test.cpp:70:14: error: assigning to 'void ' from 'func0Ptr' (aka 'void ()(void *)') converts between void pointer and function pointer 70 | res.fn = (func0Ptr)fn;

    This warning is stupid. It's part of the "we reserve the right to change the size of function pointers some day so that we can haz closures, so you can't assume that function pointers and data pointers are the same size m'kay?" silliness. And it is silly: because the C and C++ committees will never be able to change the size of function pointers, not backwards-compatibly. It's not that I don't wish they could. It's that they can't.

    • akdev1l 6 months ago

      It’s not a warning, it’s a compile time error and I am not even using -Wall -Werror

      I also believe there are platforms where a function pointer and a data pointer are not the same but idk about such esoteric platforms first hand (seems Itanium had that: https://stackoverflow.com/questions/36645660/why-cant-i-cast...)

      Though my point was only that this code will not compile as is with whatever clang Apple ships*

      I am not really sure how to get it to compile tbqh

      Some further research ( https://www.kdab.com/how-to-cast-a-function-pointer-to-a-voi...) suggest it should be done like so:

      > auto fptr = &f; void a = reinterpret_cast<void &>(fptr);

      edit: I tried with GCC 15 and that compiled successfully

      • gpderetta 6 months ago

        FWIW, POSIX practically requires void otr and function otr inter-convertibility hence the support from GCC.

      • comex 6 months ago

        It should just be

            res.fn = (void *)fn;
        
        `res.fn` is of type `void *`, so that's what the code should be casting to. Casting to `func0Ptr` there seems to just be a mistake. Some compilers may allow the resulting function pointer to then implicitly convert to `void *`, but it's not valid in standard C++, hence the error.

        Separately from that, if you enable -Wpedantic, you can get a warning for conversions between function and data pointers even if they do use an explicit cast, but that's not the default.

spacechild1 6 months ago

You can't just keep claiming these things without providing evidence. How much faster? How much smaller? These claims are meaningless without numbers to back it up.

oezi 6 months ago

I think the one key downside for std::function+lambda which resonated with me was bad ergonomics during debugging.

My unanswered question on this from 8 years ago:

https://stackoverflow.com/questions/41385439/named-c-lambdas...

If there was a way to name lambdas for debug purposes then all other downsides would be irrelevant (for most usual use cases of using callbacks).

  • neobrain 6 months ago

    > If there was a way to name lambdas for debug purposes then all other downsides would be irrelevant (for most usual use cases of using callbacks).

    Instead of fully avoiding lambdas, you can use inheritance to give them a name: https://godbolt.org/z/YTMo6ed8T

    Sadly that'll only work for captureless lambdas, however.

almostgotcaught 6 months ago

Your Func thing is better than std::function the same way a hammer is better than a drill press... ie it's not better because it's not the same thing at all. Yes the hammer can do some of the same things, at a lower complexity, but it can't do all the same things.

What I'm trying to say is being better than x means you can do all the same things as x better. Your thing is not better, it is just different.

m-schuetz 6 months ago

None of the arguments on this list seem convincing. The only one that makes sense was the argument that it helps identify the source of a crash.

How much smaller is it? Does it reduce the binary size and RAM usage by just 100 bytes?

Is it actually faster?

How much faster does it compile? 2ms faster?

  • kjksf 6 months ago

    I didn't write that article to convince anybody.

    I wrote it to share my implementation and my experience with it.

    SumatraPDF compiles fast (relative to other C++ software) and is smaller, faster and uses less resources that other software.

    Is it because I wrote Func0 and Func1 to replace std::function? No.

    Is it because I made hundreds decisions like that? Yes.

    You're not wrong that performance wins are miniscule.

    What you don't understand is that eternal vigilance is the price of liberty. And small, fast software.

    • pwagland 6 months ago

      This is a valid point missed by many today. The mantra of don't optimise early is often used as an excuse to not optimise at all, and so you end up with a lot of minor choices scattered throughout the code with all suck a tiny bit of performance out of the system. Fixing any of these is also considered to be worthless, as the improvement from any one change is miniscule. But added up, they become noticeable.

    • SuperV1234 6 months ago

      > Is it because I made hundreds decisions like that? Yes.

      Proof needed. Perhaps your overall program is designed to be fast and avoid silly bottlenecks, and these "hundred decisions" didn't really matter at all.

      • fsloth 6 months ago

        In my experience performance comes from constant vigilance and using every opportunity to choose the performant way of implementing something.

        Silly bottlenecks are half of the perf story in my experience. The other half are a billion tiny details.

badmintonbaseba 6 months ago

> Smaller size at runtime (uses less memory).

Yours is smaller (in terms of sizeof), because std::function employs small-buffer optimization (SBO). That is if the user data fits into a specific size, then it's stored inline the std::function, instead of getting heap allocated. Yours need heap allocation for the ones that take data.

Whether yours win or lose on using less memory heavily depends on your typical closure sizes.

> Faster at runtime

Benchmark, please.