Comparing Function Pointers

Comparing function pointers

C++03 5.10/1 [expr.eq]:

The == (equal to) and the != (not equal to) operators have the same
semantic restrictions, conversions, and result type as the relational
operators except for their lower precedence and truth-value result.
[Note: a < b
== c < d is true whenever a < b and c < d have the same truth-value. ] Pointers to objects or functions of the same type (after pointer conversions) can be compared for equality. Two pointers of the same
type compare equal if and only if they are both null, both point to
the same function, or both represent the same address (3.9.2).

Emphasis mine.

Can I compare a function pointer to a function for equality?

Yes, it is fine. The C standard is pretty self-explanatory in this case (C11 6.3.2.1/4):

A function designator is an expression that has function type. Except when it is the
operand of the sizeof operator, or the unary & operator, a function designator with
type ‘‘function returning type’’ is converted to an expression that has type ‘‘pointer to function returning type’’.

In your case test is an expression with function type. It gets converted to a function pointer of void(*)(void) type. This is a compatible pointer type with fp, so the == operator allows it.

Regarding the equality operator ==, the standard says (C11 6.5.9, emphasis mine):

Two pointers compare equal if and only if both are null pointers, both are pointers to the same object (including a pointer to an object and a subobject at its beginning) or function,

Is comparison of function-pointers legal

Your example may fail to compile. If it does compile, this is not guaranteed to work as expected. In practice though, it likely will.

As of C++11, conversion of function pointers to object pointers is conditionally-supported with implementation-defined results. Conditionally-supported means implementations can choose whether they want to support it. If they don't, they may just issue an error message.

For implementations that do support it, very little is guaranteed about the result of the conversion: all that's said is that if it's supported, you can convert it back and get the original value. This doesn't imply anything about the results of equality or relational comparisons.

[expr.reinterpret.cast]p8:

8 Converting a function pointer to an object pointer type or vice versa is conditionally-supported. The meaning of such a conversion is implementation-defined, except that if an implementation supports conversions in both directions, converting a prvalue of one type to the other type and back, possibly with different cv-qualification, shall yield the original pointer value.

For meaningful == and != comparisons, just compare directly.

[expr.eq]p3:

[...]

(3.2) Otherwise, if the pointers are both null, both point to the same function, or both represent the same address, they compare equal.

(3.3) Otherwise, the pointers compare unequal.

As for <, the usual rules for < render your comparison just as meaningless as int i, j; &i < &j;:

[expr.rel]p4:

4 The result of comparing unequal pointers to objects86 is defined in terms of a partial order consistent with the following rules:

[...]

(4.3) Otherwise, neither pointer is required to compare greater than the other.

Three-way comparison of pointer to functions fails

The result of using normal relational operators on function pointers is unspecified if the pointers are unequal. You can compare pointers to objects (but only meaningfully if they're pointers into the same array or structure), but relating function pointers isn't really viable.

Rather than taking the sometimes-unspecified route, C++20 simply forbids using <=> for non-object pointers.

You can test for equality between such pointers, but not their relative ordering. If you absolutely need to do this for some reason, you can cast the pointers to void*s and use std::less<> for such comparisons.

How to compare 2 functions in Go?

Before going further: you should refactor and not compare function value addresses.

Spec: Comparison operators:

Slice, map, and function values are not comparable. However, as a special case, a slice, map, or function value may be compared to the predeclared identifier nil.

Function values are not comparable. What you may do is compare if the addresses of the function values are the same (not the address of variables holding function values, but the function values themselves).

You can't take the address of a function, but if you print it with the fmt package, it prints its address. So you can use fmt.Sprintf() to get the address of a function value.

See this example (based on your code):

hand := &Handler{Undefined, Defined}
p1 := fmt.Sprintf("%v", Undefined)
p2 := fmt.Sprintf("%v", hand.Get)
fmt.Println("Expecting true:", p1 == p2)

fmt.Println("Expecting false:", fmt.Sprintf("%v", Defined) == fmt.Sprintf("%v", hand.Get))
fmt.Println("Expecting true:", fmt.Sprintf("%v", Defined) == fmt.Sprintf("%v", hand.Post))

Output (try it on the Go Playground):

Expecting true: true
Expecting false: false
Expecting true: true

Another option would be to use reflect.Value.Pointer() to get the address of the function values, this is exactly what the fmt package does: fmt/print.go:

func (p *pp) fmtPointer(value reflect.Value, verb rune) {
// ...
case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Slice,
reflect.UnsafePointer:
u = value.Pointer()
// ...
}

But you should refactor and not compare function value addresses.

C++ Comparing Member Function Pointers

Function pointers are not relationally comparable in C++. Equality comparisons are supported, except for situations when at least one of the pointers actually points to a virtual member function (in which case the result is unspecified).

Of course, you can always introduce an ordering by implementing a comparison predicate and comparing the pointers explicitly (won't look too elegant though, since you can only use equality comparisons). Other possible solutions would cross into the territory of the various implementation-specific "hacks".

How do I compare two functions for pointer equality in the latest Go weekly?

Note that there is a difference between equality and identity. The operators == and != in Go1 are comparing the values for equivalence (except when comparing channels), not for identity. Because these operators are trying not to mix equality and identity, Go1 is more consistent than pre-Go1 in this respect.

Function equality is different from function identity.


One reason for not allowing == and != on function types is performance. For example, the following closure is not using any variables from its environment:

f := func(){fmt.Println("foo")}

Disallowing comparisons of functions enables the compiler to generate a single implementation for the closure, instead of requiring the run-time to create a new closure (at run-time). So, from performance viewpoint the decision to disallow function comparisons was a good decision.


In relation to using the reflect package to determine function identity, a code like

func SomeFun()    {}
func AnotherFun() {}

func main() {
sf1 := reflect.ValueOf(SomeFun)
sf2 := reflect.ValueOf(SomeFun)
fmt.Println(sf1.Pointer() == sf2.Pointer()) // Prints true

af1 := reflect.ValueOf(AnotherFun)
fmt.Println(sf1.Pointer() == af1.Pointer()) // Prints false
}

relies on undefined behavior. There are no guarantees as to what the program will print. The compiler may decide that it will merge SomeFun and AnotherFun into a single implementation, in which case the 2nd print statement would print true. In fact, there is absolutely no guarantee that the 1st print statement will print true (it may, under some other Go1 compiler and run-time, print false).


A correct answer to your original question is:

package main

import "fmt"

func F1() {}
func F2() {}

var F1_ID = F1 // Create a *unique* variable for F1
var F2_ID = F2 // Create a *unique* variable for F2

func main() {
f1 := &F1_ID // Take the address of F1_ID
f2 := &F2_ID // Take the address of F2_ID

// Compare pointers
fmt.Println(f1 == f1) // Prints true
fmt.Println(f1 == f2) // Prints false
}


Related Topics



Leave a reply



Submit