Passing inline functions as arguments

I am wondering if C ++ will still obey the inline when the function is passed as agitation. In the following example, will a new frame for onFrame be onFrame every time frame() is called in a while ?

 bool interrupt = false; void run(std::function<void()> frame) { while(!interrupt) frame(); } inline void onFrame() { // do something each frame } int main() { run(onFrame); } 

Or would it change?

 void run(std::function<inline void()> frame) { while(!interrupt) frame(); } 

If you do not have a definitive answer, can you help me find a way to test this? Perhaps memory addresses or some kind of debugger are used?

+5
source share
3 answers

still obeys the inline ... will the new frame be pushed onto the stack

This is not what the inline keyword does (see this question for an extensive reference).


Assuming that, as Barry says, you hope to convince the optimizer to embed your function call (once again for good luck: this has nothing to do with the inline ), the + lambda function template is probably the way to go.

To understand why this is so, think about what the optimizer should work with in each of these cases:

  • function template + lambda

     template <typename F> void run(F frame) { while(!interrupt) frame(); } // ... call site ... run([]{ onFrame(); }); 

    here the function exists in general (created from the template) on the call site, and all that the optimizer should work in the scope and clearly defined.

    Please note that the optimizer may still reasonably refuse the inline call if it considers that the overpressure in the cache will exceed the preservation of the stack frame.

  • function pointer

     void run(void (*frame)()) { while(!interrupt) frame(); } // ... call site ... run(onFrame); 

    here run can be compiled as a separate function (although this copy can be selected by the linker if it can prove that no one has used it), as well as onFrame , especially since its address is taken. Finally, the optimizer may need to consider whether run is called with many different function pointers, or only one, when deciding whether to embed these calls. All in all, this seems like a lot of work and can end up optimizing the connection time.

    NB. I used a "stand-alone function" to mean that the compiler is likely to emit a code table and character record for a normal free function in both cases.

  • std::function

    This is a long time ago. Let's just notice that this class goes to great lengths (like erasing like Barry) to make a function

     void run(std::function<void()> frame); 

    It doesn’t depend on the exact type of function, which means hiding information from the compiler at the point where it generates the code for run , which means that the optimizer does not need to work (or, on the contrary, more work is needed to cancel everything that carefully hides the information).


As for checking what your optimizer does, you need to study this in the context of your entire program: it can choose different heuristics depending on the size and complexity of the code.

To be sure that it really is, just parse the source code or compile the assembler. (Yes, this is potentially big β€œsimple”, but it depends on the platform, and not on the topic on this issue, and a skill that is worth learning in any case).

+3
source

It will be difficult for the compiler to embed your function if it needs to go through std::function type-erased sending in order to get there. It can happen anyway, but you make it as difficult as possible. Your suggested alternative (with the argument std::function<inline void()> ) is poorly formed.

If you do not need to erase styles, do not use erase styles. run() can just take an arbitrary callable:

 template <class F> void run(F frame) { while(!interrupt) frame(); } 

This is easier for the built-in compiler. Although just having an inline function alone does not guarantee that the function is inserted. See this answer .

Note also that when you pass a pointer to a function, it also makes it less likely to hit the string, which is inconvenient. I am trying to find an answer here that had a great example, but until then, if embedding is very important, wrapping it in lambda might be the way:

 run([]{ onFrame(); }); 
+8
source

Compile for release and check list files or enable disassembly in the debugger. The best way to find out is to check the generated code.

0
source

Source: https://habr.com/ru/post/1246001/


All Articles