Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pass function to another function by pointer and by name [duplicate]

I'm learning function pointers and this example from wiki:

int add(int first, int second)
{
    return first + second;
}

int subtract(int first, int second)
{
    return first - second;
}

int operation(int first, int second, int (*functocall)(int, int))
{
    return (*functocall)(first, second);
}

int main()
{
    int  a, b;
    int  (*plus)(int, int) = add;
    a = operation(7, 5, plus);
    b = operation(20, a, subtract);
    cout << "a = " << a << " and b = " << b << endl;
    return 0;
}

As I can see there plus is a pointer to function add and it's passed to function operation. It's clear. But what about subtract.

Why pointer is not used for it? What is the difference between 2 methods? Is it c++ specific?

like image 471
Viacheslav Kondratiuk Avatar asked Dec 26 '22 02:12

Viacheslav Kondratiuk


2 Answers

In C++, a function can be automatically converted to a function pointer, so the following are equivalent:

b = operation(20, a, subtract);
b = operation(20, a, &subtract);

Since &substract has thecorrect type(int (*)(int, int)), the code compiles and works as expected.

Is it c++ specific?

Can't really answer this, since there might be other languages that allow this. I'm more than sure there are.

like image 86
Luchian Grigore Avatar answered Jan 19 '23 01:01

Luchian Grigore


Why pointer is not used for substract?

In fact, all the function name is a pointer in its corresponding live scope. Here, in your code, when you define the function substract(), add().

You also defined two variable named substract and add, which type is function pointer: int (*)(int, int). so you can declare a new function pointer plus, and assign add to it. All the three variables is all pointer.

The following is the assembly code generated by clang++, which could give you a detail explanation. I have removed all the un-relevant code. The function name is a bit ugly to read, this is because of the C++ name mangling, you can just ignore the capital chars and digits to easily understand the name.

function add():

    .text
    .globl  _Z3addii
    .align  16, 0x90
    .type   _Z3addii,@function
_Z3addii:                               # @_Z3addii
    .cfi_startproc
# BB#0:                                 # %entry
    movl    %edi, -4(%rsp)
    movl    %esi, -8(%rsp)
    movl    -4(%rsp), %esi
    addl    -8(%rsp), %esi
    movl    %esi, %eax
    ret
.Ltmp6:
    .size   _Z3addii, .Ltmp6-_Z3addii
    .cfi_endproc

function substract:

    .globl  _Z8subtractii
    .align  16, 0x90
    .type   _Z8subtractii,@function
_Z8subtractii:                          # @_Z8subtractii
    .cfi_startproc
# BB#0:                                 # %entry
    movl    %edi, -4(%rsp)
    movl    %esi, -8(%rsp)
    movl    -4(%rsp), %esi
    subl    -8(%rsp), %esi
    movl    %esi, %eax
    ret
.Ltmp7:
    .size   _Z8subtractii, .Ltmp7-_Z8subtractii
    .cfi_endproc

The _Z3addii and _Z8subtractii label give the start point of the two function, which also is an address that point to the start of the function.

I have add some comments in the code to show how the function pointer works, which started with ###.

function main:

    .globl  main
    .align  16, 0x90
    .type   main,@function
main:                                   # @main
    .cfi_startproc
# BB#0:                                 # %entry
    pushq   %rbp
.Ltmp16:
    .cfi_def_cfa_offset 16
.Ltmp17:
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
.Ltmp18:
    .cfi_def_cfa_register %rbp
    subq    $32, %rsp
    movl    $7, %edi
    movl    $5, %esi
    leaq    _Z3addii, %rax  ### Here, the assembly just load the label of _Z3addii, not a plus related variable, so in fact they are  the same type.
    movl    $0, -4(%rbp)
    movq    %rax, -24(%rbp)
    movq    -24(%rbp), %rdx  ### move the value of the function pointer to the rdx register.
    callq   _Z9operationiiPFiiiE
    movl    $20, %edi
    leaq    _Z8subtractii, %rdx  ### Here, just load the label -f _Z8subsractii, which is the value of the function pointer substract. move it directly to rdx register.
    movl    %eax, -8(%rbp)
    movl    -8(%rbp), %esi
    callq   _Z9operationiiPFiiiE
    leaq    _ZSt4cout, %rdi
    leaq    .L.str, %rsi
    movl    %eax, -12(%rbp)
    callq   _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
    movl    -8(%rbp), %esi
    movq    %rax, %rdi
    callq   _ZNSolsEi
    leaq    .L.str1, %rsi
    movq    %rax, %rdi
    callq   _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
    movl    -12(%rbp), %esi
    movq    %rax, %rdi
    callq   _ZNSolsEi
    leaq    _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, %rsi
    movq    %rax, %rdi
    callq   _ZNSolsEPFRSoS_E
    movl    $0, %ecx
    movq    %rax, -32(%rbp)         # 8-byte Spill
    movl    %ecx, %eax
    addq    $32, %rsp
    popq    %rbp
    ret
.Ltmp19:
    .size   main, .Ltmp19-main
    .cfi_endproc

In the above main function assebly, we saw that all the function pointer value is moved to the register rdx. Here in the following operation function:

function operation():

    .globl  _Z9operationiiPFiiiE
    .align  16, 0x90
    .type   _Z9operationiiPFiiiE,@function
_Z9operationiiPFiiiE:                   # @_Z9operationiiPFiiiE
    .cfi_startproc
# BB#0:                                 # %entry
    pushq   %rbp
.Ltmp10:
    .cfi_def_cfa_offset 16
.Ltmp11:
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
.Ltmp12:
    .cfi_def_cfa_register %rbp
    subq    $16, %rsp
    movl    %edi, -4(%rbp)
    movl    %esi, -8(%rbp)
    movq    %rdx, -16(%rbp)
    movq    -16(%rbp), %rdx
    movl    -4(%rbp), %edi
    movl    -8(%rbp), %esi
    callq   *%rdx  ### directly jump to the address, which is the value of the rdx register.
    addq    $16, %rsp
    popq    %rbp
    ret
.Ltmp13:
    .size   _Z9operationiiPFiiiE, .Ltmp13-_Z9operationiiPFiiiE
    .cfi_endproc

So, we can see from the assembly that, the variable substract, add, plus in your given code are all pointers, and comes from the label of the start point of the function.

What is the difference between the two method?

Since the plus, add and substract are all the same type function pointers, just like Luchian Grigore says, they are the same.

From the above assembly code, we can also find out that, the two method are absolutely the same method call, without any difference.

Is it C++ specified?

C family languages

and some other derived languages, like obj-c support function pointers directly.

Java language

In Java, there is such a concept named function pointers, but an class which implemnts an interface` could achieve the same goal as function pointer.

For example: you could define an interface first:

interface StringFunction {
  int function(String param);
}

and then define an function that could accept objects that implements the interface:

public void takingMethod(StringFunction sf) {
   //stuff
   int output = sf.function(input);
   // more stuff
}

And then you could define different classes that implements interface StringFunction, and use it as an parameter of takingMethod()

Python

In Python, function name is just a type of variable, and you could directly use it, like the following ways:

def plus_1(x):
    return x + 1

def minus_1(x):
    return x - 1

func_map = {'+' : plus_1, '-' : minus_1}

func_map['+'](3)  # returns plus_1(3) ==> 4
func_map['-'](3)  # returns minus_1(3) ==> 2

Ruby

Ruby also have some similar ways: Function pointer in Ruby?

like image 42
Kun Ling Avatar answered Jan 19 '23 01:01

Kun Ling