How to Get the Function Pointer of a Built-In Standard Operator

Is it possible to get the function pointer of a built-in standard operator?


Built-in operators

Why you cannot have function pointers of them:

C++11, §13.6/1, [over.built]

The candidate operator functions that represent the built-in operators defined in Clause 5 are specified in this subclause. These candidate functions participate in the operator overload resolution process as described in 13.3.1.2 and are used for no other purpose.

Built-in operators (those for the built-in types) aren't real operator functions. So you can't have function pointer pointing to them. You also cannot invoke them using operator<(A,B) syntax.
They only participate in overload resolution but the compiler will translate them directly into the appropriate asm/machine instruction without any kind of "function call".

The way to get around this issue:

user1034749 has already answered this question, but for completeness:

The standard defines a lot of function objects in §20.8, [function.objects], i.e.

  • Arithmetic operations
  • Comparisons
  • Logic operations
  • Bitwise operations

A function object is an object of a function object type. In the places where one would expect to pass a pointer to a function to an algorithmic template (Clause 25), the interface is specified to accept a function object. This not only makes algorithmic templates work with pointers to functions, but also enables them to work with arbitrary function objects.

C++11, §20.8.5, [comparisons]

  • equal_to
  • not_equal_to
  • greater, less
  • greater_equal
  • less_equal

Those are templated function objects which decay to the analogous operator in their operator() function. They can be used as function pointer arguments.

user1034749 is right, I want to state: There's no other way, these are completely equivalent in usage to 'raw' function pointers. Reference given.

Standard class type operators

You can use standard library operators as function pointers (which are present as "real functions").

But you'll have to refer to the respective instance of the template. The compiler will need appropriate hints to deduce the correct template.

This works for me on MSVC 2012 using operator+ of std::basic_string

template<class Test>
Test test_function (Test const &a, Test const &b, Test (*FPtr)(Test const &, Test const &))
{
return FPtr(a, b);
}

int main(int argc, char* argv[])
{
typedef std::char_traits<char> traits_t;
typedef std::allocator<char> alloc_t;
std::basic_string<char, traits_t, alloc_t> a("test"), b("test2");
std::cout << test_function<std::basic_string<char, traits_t, alloc_t>>(a, b, &std::operator+) << std::endl;
return 0;
}

If the template argument of test_function is left out to be deduced this will fail (at least for MSVC 2012).

function pointers of standard operators

You have to make the difference between built-in operators and overloaded operators. Built-in operators are operators that have as operands only basic types (integers, booleans, floats, pointers).

E.g.:

int a = 9, b = 7;
a + b; <-- built-in plus operator
6 + 5; <-- built-in plus operator

C++ gives the possibility to define operators for user defined types (i.e. classes). std::string is a class (defined by the library, but that doesn't matter). So if we write this:

std::string a = "asdf", b = "qwert";
a + b; <-- this is not built-in operator.

In order for the above to compile there has to be a declaration and definition of a plus operator that operates on two objects of type string. It happens that the standard library defines such operator: std::operator+(std::basic_string). So this:

std::string a = "asdf", b = "qwert";
a + b;

is actually this:

std::string a = "asdf", b = "qwert";
std::operator+(a, b); <-- call to overloaded operator (see this as a function call)

Built-in operators are not functions. Overloaded operators behave like functions.

Built-in operators have only basic types as operands and their functionality is defined by the standard (you cannot redefine what int + int does).

Overloaded operators must have at least one operand of user-defined type.


Lets get to some practical examples:

template <class T, class Compare>
bool compare (T a, T b, Compare comp) {
return comp(a,b);
}

I've replaced your function pointer with a template parameter. So this can be a function pointer, a function reference or a function object.

What choices do you have to call this with T as int? Well you certainly can't have an operator passed for comp. So you have to have a function or a function object (see link below).

// function
bool intCompareLess(int a, int b) {
return a < b;
}

compare(3, 5, intCompareLess);


// function object
class IntComparatorLess {
public:
// this is a function call operator overload
bool operator()(int a, int b) {
return a < b;
}
};

compare(3, 5, IntComparatorLess());

// or don't reinvent the wheel:
compare(3, 5, std::less<int>()); // std::less is somehow similar with IntComparatorLess

what if you call compare with T as a user defined type

class MyClass {
public:
int x, y;
};

Now you have an extra option: define a operator and pass it as parameter:

bool operator<(MyClass c1, MyClass c2) {
if (c1.x == c2.x)
return c1.y < c2.y;
return c1.x < c1.x
}

MyClass a, b;
compare(a, b, (bool (*)(MyClass, MyClass))operator<);

usefull: C++ Functors - and their uses

Function Pointer of an Operator in C++

The built-in operator | that takes two int and returns an int can not be accessed through the notation operator|. For instance, the following does not compile

int a = operator|(2,3);

Is it possible to pass a pointer to an operator as an argument like a pointer to a function?

You cannot obtain a pointer to a built-in operator. But fortunately, the standard library provides function objects for all standard operators. In your case, that object's name is std::greater:

sort (arr, arr + N, std::greater<int>{});

Since C++14, you can even omit the argument type and it will be deduced from how the object is used:

sort (arr, arr + N, std::greater<>{});

And since C++17, the empty <> can be omitted too:

sort (arr, arr + N, std::greater{});

How do function pointers in C work?


Function pointers in C

Let's start with a basic function which we will be pointing to:

int addInt(int n, int m) {
return n+m;
}

First thing, let's define a pointer to a function which receives 2 ints and returns an int:

int (*functionPtr)(int,int);

Now we can safely point to our function:

functionPtr = &addInt;

Now that we have a pointer to the function, let's use it:

int sum = (*functionPtr)(2, 3); // sum == 5

Passing the pointer to another function is basically the same:

int add2to3(int (*functionPtr)(int, int)) {
return (*functionPtr)(2, 3);
}

We can use function pointers in return values as well (try to keep up, it gets messy):

// this is a function called functionFactory which receives parameter n
// and returns a pointer to another function which receives two ints
// and it returns another int
int (*functionFactory(int n))(int, int) {
printf("Got parameter %d", n);
int (*functionPtr)(int,int) = &addInt;
return functionPtr;
}

But it's much nicer to use a typedef:

typedef int (*myFuncDef)(int, int);
// note that the typedef name is indeed myFuncDef

myFuncDef functionFactory(int n) {
printf("Got parameter %d", n);
myFuncDef functionPtr = &addInt;
return functionPtr;
}

Pass a function or pass a function pointer in C?


Is there an actual difference between all of my 4 snippets? Why all of them behave exactly the same?

All four code snippets are the same.

With regard to how execute is called, this is covered in section 6.3.2.1p4 of the C11 standard:

A function designator is an expression that has function type. Except when it is the
operand of the sizeof operator, the _Alignof 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’’.

So calling execute(print) or execute(&print) are the same because of this.

Regarding the parameter to execute, this is covered in section 6.7.6.3p8:

A declaration of a parameter as ‘‘function returning type’’ shall be adjusted to ‘‘pointer to
function returning type’’, as in 6.3.2.1

So this means void execute(void (*f)()) and void execute(void f()) are the same.

Which one of the 4 snippets should I use, and why?

This tends to be a matter of style, but I would personally declare variables and parameters as a pointer-to-function type rather than a function type, and I would pass the function name without the address-of operator.

Should I invoke the passed function received via parameter with f() or with (*f)()

This is also a matter of style. I would go with f() as its easier to read.

Why in the first print of each snippet, the size of the function variable (sizeof(print)) is always 1? What are we actually getting the sizeof in this case? (it obviously not the size of a pointer, which would be 8 bytes in my 64-bit machine. I would get the size of a pointer if I used sizeof(&print))

Using sizeof on a function designator is explicitly disallowed as per section 6.5.3.4p1:

The sizeof operator shall not be applied to an expression that has function type or an incomplete type, to the parenthesized name of such a type, or to an expression that designates a bit-field member. The _Alignof operator shall not be applied to a function type or an incomplete type.

And doing so invokes undefined behavior.

Why on snippets 1 and 3, in the second print, sizeof(f) gives me 8 (the size of a pointer), even though the parameter is declared as void (f)() (so without a *, I could assume it is not a pointer)

This goes back to 6.7.6.3p8 above, namely that a function parameter of type function is converted to type pointer-to-function, so that's what you're getting the size of.

Why does function pointer in c does not have to be pointer at all?

The type of a function pointer is not its return type. It is the type of the function pointed to.

In the case of the parameter int (*op)(int,int), the type of op is "pointer to function which takes an int and an int, and returns int".

You could also define the parameter as int op(int,int) in which case the type of op is "function which takes an int and an int, and returns int".

Because function pointers point to code, they don't have a "value" in the sense that objects do. In fact, anyplace you specify an expression with a function type it is automatically converted to a pointer to a function. So you can declare the parameter op as int (*op)(int,int) or int op(int,int) and they will be exactly the same.

Section 6.3.2.1p4 of the C standard says the following regarding function types:

A function designator is an expression that has function
type. Except when it is the operand of the sizeof operator,
the _Alignof 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’’.

Section 6.7.6.3p8 also says the following regarding a function type as a parameter:

A declaration of a parameter as ‘‘function returning type’’ shall be
adjusted to ‘‘pointer to function returning type’’, as in 6.3.2.1.

It is because of this that a parameter of function type is allowed. A variable of function type that is not a parameter is not useful because it can't be initialized or assigned.



Related Topics



Leave a reply



Submit