0

Assuming that I want to implement functions with an asynchronous behaviour or I just want to use function pointers anyway, does invoking a function pointer is something that is granted to result in an invocation of the associated function immediately followed by the next instruction ?

Example

#include <iostream>
#include <cstdint>
int triple(int a) { return a * 3; }
void foo() { std::cout << "executing foo()" << '\n'; }
using fptrT = int (*)(int);
int main()
{
    fptrT p = triple;
    p(3);
    foo();
}

What both standards say about what happens when the expression p(3) get evaluated and when foo() will be executed ?

user2485710
  • 9,451
  • 13
  • 58
  • 102
  • 8
    Calling a function through a pointer is no different than calling the function directly. – Mark Ransom Nov 13 '14 at 21:39
  • @MarkRansom this was sounding in my head for some reasons ... It sound strange to me that both options are not different, I can't really explain it. Anyway, this can't be used to implement functions with an async behaviour ? How should I go from here ? – user2485710 Nov 13 '14 at 21:41
  • @MarkRansom isn't my `fptrT p` acting as an extra level of indirection ? this is the point that confuses me, it's like delegating `p` to call a function and not calling a function "directly" via its own name. – user2485710 Nov 13 '14 at 21:43
  • 1
    @user2485710 Neither C nor C++ provide asynchronous behavior in the core language. You need to use either a threading or coroutine library. There are many available, and the newer standards (C11 and C++11) have support in the standard library for threading. – sfstewman Nov 13 '14 at 21:43
  • @user2485710 You know that you could always spawn threads instead of normal function calls? If the overhead per call is too much, a constant running working thread with some sort of communication to add jobs etc. is a bit more complicated, but of course possible too. – deviantfan Nov 13 '14 at 21:44
  • 3
    @user2485710 Indirect != asynchronous. A function pointer is a lot like a virtual method in a class: the same code can call different functions depending on the value of the pointer. This is completely orthogonal to functions running asynchronously. – sfstewman Nov 13 '14 at 21:45
  • @sfstewman yes but the standard `std::async` is just horrible in my opinion, not usable for me in any scenario I can think of . – user2485710 Nov 13 '14 at 21:46
  • @deviantfan the observer pattern it's not just a queue in a separate thread doing work, it's another thing, yes I can do that, but at point I'll just have a circular buffer or queue, not async functions. – user2485710 Nov 13 '14 at 21:47
  • @user2485710 I have no opinion on C++ libraries. I'm just telling you that function pointers don't work the way you think they do. There are a myriad of threading and coroutine libraries available for C++. Choose whatever one you like. – sfstewman Nov 13 '14 at 21:47
  • What @deviantfan describes *is not* the observer pattern. He's describing a threading pool. – sfstewman Nov 13 '14 at 21:48
  • @sfstewman I still can't picture why function pointer are equivalent to "normal" function calls and I still would expect an async behaviour from them even knowing all this . – user2485710 Nov 13 '14 at 21:48
  • 2
    Why would you expect async behavior? What makes you think that? – nanny Nov 13 '14 at 21:49
  • @user2485710 Normal: You say "execute A" and the computer executes A. Function pointer: "Pointer P has an address, there is a function, execute this". Computer reads P and executes the function then. Aside from a pointer access, nothing is different. – deviantfan Nov 13 '14 at 21:51
  • @MarkRansom what the other libraries have that C or C++ doesn't that allows me to implement asynchronous functions ? That's what I don't grasp . There is some OS call that I have to use ? – user2485710 Nov 13 '14 at 21:51
  • 1
    @user2485710 Third party libs are just using threads etc. within them to make it look like somewhat normal function calls to you. – deviantfan Nov 13 '14 at 21:53
  • @nanny the "1 more layer of indirection thing", you store the address of a function in a pointer, you invoke a dereference via a pointer and not the underlaying function, that's what I was picturing and the opposite still doesn't make a lot of sense to me; I know it's wrong, but it's completely counterintuitive . – user2485710 Nov 13 '14 at 21:53
  • One more layer of indirection just means a pointer to a pointer to a function. Adding such a layer doesn't make such a function call non-blocking, however. – Robert Harvey Nov 13 '14 at 21:55
  • Until very recently the C++ standard had no concept of threading or asynchronous operation whatsoever. It was based on a single threaded model, and anything beyond that had to be provided by the OS. It still does, but now at least there's a standard interface. – Mark Ransom Nov 13 '14 at 22:07

2 Answers2

8

Christope's answer is correct. This is to supplement.

A function pointer cannot itself provide asynchronous behavior. The standards actually bar this. I'm far more familiar with the C standard than the C++ standard, so I'll use that. My understanding is that both should be approximately the same on this point.

What the C11 standard says about functions and function pointers

Let's start with the definition of a function call in C, given in 6.5.2.2 paragraph 3:

A postfix expression followed by parentheses () containing a possibly empty, comma- separated list of expressions is a function call. The postfix expression denotes the called function. The list of expressions specifies the arguments to the function.

And modified by the constraint in paragraph 1:

The expression that denotes the called function (92) shall have type pointer to function returning void or returning a complete object type other than an array type.

Importantly, the accompanying footnote 92 says:

Most often, this is the result of converting an identifier that is a function designator.

So, the C11 standard basically defines a function call as something that calls a function pointer. And, for this purpose, named function identifiers are automatically converted into function pointers to the code in the identifier. Thus, C sees no difference between functions and function pointers.

An experiment

While it's always good to refer to the standard, it's also pretty useful to just look at how solid implementations do things. Let's do a test where we write fairly simple code and then look at the underlying assembly

The code:

#include <stdio.h>
#include <stdlib.h>

typedef void (*my_func_ptr)(int,int);

void my_function(int x, int y)
{
  printf("x = %d, y = %d, x + y = %d\n",x,y,x+y);
}

int main()
{
  /* declared volatile so the compiler has to call the function through
   * the pointer and cannot optimize it to call the function directly */
  volatile my_func_ptr fp = my_function; 

  my_function(3,5);
  fp(3,6);

  return 0;
}

I compiled the code using gcc on Mac OS X with default optimizations (gcc -o fptr fptr.c), which is actually a gcc frontend to the LLVM library. To look at the assembly, I ran the program under lldb, set a breakpoint at main, and issued the disassemble -f commmand, which disassembles the current function. I use settings set target.x86-disassembly-flavor intel for Intel-style assembly. The default in lldb is AT&T style, which looks a bit different.

The main routine in assembly is this:

  push   rbp
  mov    rbp, rsp
  sub    rsp, 0x20                   ; sets up the stack frame
  mov    edi, 0x3                    ; my_function(3,5). 1st arg: edi
  mov    esi, 0x5                    ;                   2nd arg: esi
  lea    rax, qword ptr [rip - 0x59] ; loads address of my_function into rax
  mov    dword ptr [rbp - 0x4], 0x0  
  mov    qword ptr [rbp - 0x10], rax ; saves address of my_function on stack
  call   0x100000ed0                 ; explicit call to my_function
  mov    eax, 0x0
  mov    edi, 0x3                    ; fp(3,6). 1st arg: edi
  mov    esi, 0x6                    ;          2nd arg: esi
  mov    rcx, qword ptr [rbp - 0x10] ; rcx <- address of my_function from stack
  mov    dword ptr [rbp - 0x14], eax
  call   rcx                         ; call address at rcx
  mov    eax, dword ptr [rbp - 0x14]
  add    rsp, 0x20
  pop    rbp
  ret    

Notice that both function calls are essentially the same. They use the same assembly. Both times the actual call is invoked with a call op. The only difference is that the first time the address is hard coded and the second time, the address is stored in the rcx register. Also notice that there's nothing asynchronous about the code.

What C11 says about sequence points

When you start reasoning about sequence points, you actually find that, within a single thread, the standard disallows the kind of asynchronous behavior that you expected. In most cases, C11 constrains the compiler to execute code that is separated by a sequence point in sequential order. In Section 5.1.2.3 (program execution), the execution order of the program is defined as a series of sequence points. The relevant definition is essentially in paragraph 3:

Sequenced before is an asymmetric, transitive, pair-wise relation between evaluations executed by a single thread, which induces a partial order among those evaluations. Given any two evaluations A and B, if A is sequenced before B, then the execution of A shall precede the execution of B.

And later in that paragraph:

The presence of a sequence point between the evaluation of expressions A and B implies that every value computation and side effect associated with A is sequenced before every value computation and side effect associated with B.

Basically, this establishes that code separated by a sequence point must be executed synchronously (in order). However, the standard provides an out if the compiler can reason that two pieces of code cannot affect each other, in paragraph 4:

In the abstract machine, all expressions are evaluated as specified by the semantics. An actual implementation need not evaluate part of an expression if it can deduce that its value is not used and that no needed side effects are produced (including any caused by calling a function or accessing a volatile object).

So, how do function pointers enter this? Appendix C clarifies what that a sequence point lies between expression statements, which are essentially statements that end with a semicolon (see 6.8.3). This includes function calls.

How this bars asynchronous execution of function pointers

Consider two sequential function calls:

f();
g();

Neither takes an argument, so the reasoning is a bit simpler. The calls must be executed in order unless the compiler can reason that any side effect of f() is unused in g() and vice versa. The only way compilers can reason about this in a function is if the function's code is available to the compiler. In general, this is not possible for function pointers, because the pointer could point to any function that satisfies the constraints of the function pointer type.

Note that in some cases the compiler can infer the correct function (if the function pointer is only assigned once and exists in local scope), but this is often not the case. Thus, the compiler must execute the functions in the order presented, and the first function must return before the second.

What about threading and coroutine libraries

The C11 standard has different rules for threading. Notice that Section 5.1.2.3 restricts itself to execution within a single thread. Coroutine libraries that play with the stack essentially break the C11 machine model, and are bound to a particular set of implementations (ie: not necessary portable to any C environment). A coroutine library essentially has to supply its own set of sequential-ordering guarantees.

Community
  • 1
  • 1
sfstewman
  • 5,589
  • 1
  • 19
  • 26
  • I know I was wrong, but you are kinda "cheating" too, because you are referring to implementation details, furthermore, you are referring to a single implementation on a particular machine. But I got the point, I was just seeing this in a different way than what actually is. – user2485710 Nov 13 '14 at 22:18
  • I'm not cheating. Someone else can refer to the standard, I don't particularly care to right now, especially since you seem more concerned with C++ than C and I try to avoid the C++ standard. But, I'll add something from the C standard on sequence points if you must. – sfstewman Nov 13 '14 at 22:18
  • they basically have the same memory model, they are also following the same path of evolution, C11 got threads too for example, there are differences but I don't think that there are substantial differential considering what I'm asking here. It's always nice to read some comments and data that have some context, if you think that this will add something to discussion then do it. – user2485710 Nov 13 '14 at 22:24
  • @user2485710 Well, that took a bit and it's a little unwieldy, but perhaps still useful. – sfstewman Nov 13 '14 at 23:22
6

A function pointer is just another way to call a function, using a stored address instead of a fixed predetermined function name:

C++11, sect. 5.2.2.1: A function call is a postfix expression followed by parentheses containing (...) the arguments to the function. For an ordinary function call, the postfix expression shall be either an lvalue that refers to a function, or it shall have pointer to function type.

For C, the wording is a little different (C11, sect. 6.5.2.2), but the principles are the same (except for pointer to member functions, which don't exist in C).

Function pointers can be used for callback mecanisms, or to implement design patterns such as the strategy pattern to customize dynamically a generic algorithm. In C++ there are now more powerfull alternatives such as lambda functions or function objects.

If you look for aynchronous function call, you should have a look at std::async():

   std::future<int> ft = std::async (triple,3);   // invoque asynchronously
   //... remaining code 
   bool myresult = ft.get();  // get result when it is needed

Note about your comment: It's difficult to predict performance, as it depends on library implementation, OS and hardaware capability. But for me it proved to be rather efficient: on MSVC2013 for example, recent experiments showed me that the threads created for async are reused when possible, thus reducing creation overhead to its bare minimum. By using multicore hardware async really permits to increase the overall throughput.

Community
  • 1
  • 1
Christophe
  • 68,716
  • 7
  • 72
  • 138
  • horrible, not your example but the standard implementation, it's based on threads and the futures returned via `async` are strange too, they have a dtor that blocks . – user2485710 Nov 13 '14 at 21:50
  • How else would you expect `async` to work? Maybe you ought to clarify your expectations a bit. The destructor blocks because the async object needs to be cleaned up once it's no longer needed. That's generally true of any C++ object. – Robert Harvey Nov 13 '14 at 21:51
  • @RobertHarvey yes, but it's 1 thread for each call to `std::async`, if I want to call `std::async` 1000 times in my code, just this step is equivalent to spawning 1000 threads! My problems with liking the C++ threading model are also about threads themselves that don't scale very well, they are just cumbersome, from one of the leading languages I was expecting a task model, not a threading model ( with some flaws ) . – user2485710 Nov 13 '14 at 21:56
  • Coroutines don't necessarily require additional threads. You can defer getting the result from a function by simply continuing execution of the remaining code and returning later to get the result of the original function. You can do all this on a single thread (or perhaps two); that's what Node.JS does, and that's mostly what the `async` keyword does in C#. Still, none of this has much to do with adding an additional layer of indirection to a function call; they're two different concepts. – Robert Harvey Nov 13 '14 at 21:58
  • 2
    To put it another way, the purpose of a function pointer is to give you the ability to *change out the function for a different one,* and that's all it does. – Robert Harvey Nov 13 '14 at 21:59
  • @RobertHarvey it depends how you think about the invocation of said expression, I think that the way function pointers are implemented is weird and it's probably time to make them async, maybe with some syntactic sugar, or 1 more keyword, but I still think that this chain of events is counter intuitive . What you are describing is what ? I think you are talking about a queue doing work on a separate thread, right ? Or it's another pattern ? – user2485710 Nov 13 '14 at 22:05
  • It would probably take two threads, one for the main body of the function, and a worker thread. But you can stack up as many tasks as you like on that worker thread; my point is that you don't need one thread for every `async` call, necessarily. As for the function pointers, just refer to my comment above this one. That's all that a function pointer does; it doesn't do anything else, and it's not relevant to the `async` discussion. – Robert Harvey Nov 13 '14 at 22:08
  • @user2485710: As to the idea of syntactic sugar to create `async` methods; I think that is a great idea. C# has already done this; see here if you want to know how it works: http://msdn.microsoft.com/en-us/library/hh191443.aspx – Robert Harvey Nov 13 '14 at 22:14