Showing posts with label function pointers. Show all posts
Showing posts with label function pointers. Show all posts

Example of 'functors'

What are 'functors?'

Depending on what you prefer to read, there are many definitions and explanations of Functors.

From the function pointer tutorial:

Functors are functions with a state. In C++ you can realize them as a class with one or more private members to store the state and with an overloaded operator () to execute the function. Functors can encapsulate C and C++ function pointers employing the concepts templates and polymorphism. You can build up a list of pointers to member functions of arbitrary classes and call them all through the same interface without bothering about their class or the need of a pointer to an instance. All the functions just have got to have the same return-type and calling parameters. Sometimes functors are also known as closures. You can also use functors to implement callbacks.

From StackOverflow:
  • A functor is pretty much just a class which defines the operator(). That makes it "look like" a function.
  • Another advantage to a functor over a pointer to a function is that the call can be inlined in more cases.
  • You can use boost::function, to create functors from functions and methods
Here is a simple example of functors that I have created using mishmash from various places:



//Program tested on Microsoft Visual Studio 2008 - Zahid Ghadialy
//Program to demonstrate the use of functors

#include<iostream>

using namespace
std;

class
someClass
{

public
:
someClass(int x) : someVariable(x) {}
int
operator()(int y) {return (someVariable + y);}
int
internalStateValue(){return someVariable;}

private
:
int
someVariable;
};


int
main()
{

someClass class1(50);
someClass class2(75);

cout<<"Class1 state variable value is : "<<class1.internalStateValue()<<endl;
cout<<"Class2 state variable value is : "<<class2.internalStateValue()<<endl;

int
test1 = class1(22);
cout<<"Test 1 value is : "<< test1<<endl;
int
test2 = class2(22);
cout<<"Test 2 value is : "<< test2<<endl;

cout<<"Class1 final state variable value is : "<<class1.internalStateValue()<<endl;
cout<<"Class2 final state variable value is : "<<class2.internalStateValue()<<endl;

return
0;
}





The output is as follows:

String literals in C++

Here's a short one on string literals in C++. Ask yourself: what is their type? It is 'array of n const char', correct! So, we might think:

char* literal = "Hello World!";

would be "invalid"/"illegal" in C++. But you'd be surprised it is not and even Comeau online compiles it successfully without even a warning.

The C++ standard, however, tries to protect you hinting that the above is wrong by stating that it is a deprecated feature in C++ that probably was allowed to keep backward compatibility with C.

Here is what the standard says as part of section [2.13.4/2]:

[quote]
A string literal that does not begin with u, U, or L is an ordinary string literal, also referred to as a narrow string literal. An ordinary string literal has type “array of n const char”, where n is the size of the string as defined below; it has static storage duration (3.7) and is initialized with the given characters.
[/quote]

So, the following would have definitely been invalid in C++:

char* literal = "Hello World!";

"Hello World!" is an array of 13 [spooky :-)] constant characters. 'literal' is the pointer to the first element of the array and since that element is const, the pointer cannot be declared as a non-const char*. The pointer has to be of the type 'pointer to a const char'.

But as mentioned above, to have backward compatibility with C where the above works an implicit conversion is defined for array to pointer conversion where a string literal would be converted to an r-value of type "pointer to a char". The standard mentions this in section [4.2/2]:

[quote]
A string literal (2.13.4) with no prefix, with a u prefix, with a U prefix, or with an L prefix can be converted to an rvalue of type “pointer to char”, “pointer to char16_t”, “pointer to char32_t”, or “pointer to wchar_t”, respectively. In any case, the result is a pointer to the first element of the array. This conversion is considered only when there is an explicit appropriate pointer target type, and not when there is a general need to convert from an lvalue to an rvalue. [ Note: this conversion is deprecated. See Annex D. —end note ]
[/quote]

But, the thing to be happy about is the note above, that is re-iterated in Annexure D section [D.4/1] as:

[quote]
The implicit conversion from const to non-const qualification for string literals (4.2) is deprecated.
[/quote]

So, best is to keep the good habit of declaring the pointer to the literal as a pointer to a const. :-)

[The C++ standard's draft version used for quotes above, has document number : N2315=07-0175 dated 2007-06-25]

Simple example of Callback Functions

Here is a simple example introducing to the concept of callbacks in C++. Function Pointers will be used implicitly in demonstrating this example.


//Program tested on Microsoft Visual Studio 2008 - Zahid Ghadialy
//Example showing how callback functions work in C++
#include<iostream>

using namespace
std;

int
adds(int a, int b)
{

return
a+b;
}


int
subs(int a, int b)
{

return
a-b;
}


int
DoIt(int x, int y, int (*c)(int, int))
{

cout<<"DoIt : "<<c(x,y)<<endl;
return
0;
}


int
main()
{

DoIt(3,4,adds);
DoIt(4,3,subs);

//subs by another name
int (*minus)(int,int) = subs;
DoIt(4,2,minus);

return
0;
}


Simple Function Pointers example

This example shows how function pointers work. There is also an example of how priority queues work.



//Program tested on Microsoft Visual Studio 2008 - Zahid Ghadialy
//This program intents to show the use of function pointers
//This program also uses priority queues
#include<iostream>
#include<queue>

using namespace
std;

typedef
int (*fpRegCb)(int* i_p, int index);

int
funcA(int *val, int index)
{

cout<<"Func A: index = "<<index<<" val = "<<*val<<endl;
return
(*val + 10);
}


int
funcB(int *val, int index)
{

cout<<"Func B: index = "<<index<<" val = "<<*val<<endl;
return
(*val + 15);
}


class
someSimpleListType
{

private
:
someSimpleListType(); //This cant be used
public:
int
index;
int
*value;
fpRegCb callback;
someSimpleListType(fpRegCb cb, int *val, int idx):value(val),index(idx)
{

callback = cb;
};

//< operator is overloaded for use with priority queues. This will
//decide the priority of the new element, where it should be placed
const bool operator <(const someSimpleListType& sLT) const
{

return
(index < sLT.index);
}
};


priority_queue <someSimpleListType> someSimpleList;

int
main()
{

int
index=0;
int
*a=new int(22);
int
*b=new int(37);
int
*c=new int(62);

//The higher the priority, higher it is in the queue
someSimpleList.push(someSimpleListType(funcA, a, index++));
someSimpleList.push(someSimpleListType(funcB, b, index++));
someSimpleList.push(someSimpleListType(funcB, c, index++));
//The last push will end up top of queue as index(priority) is greatest

//Lets get all the elements from the list and process them
cout<<"Test 1"<<endl;
while
(!someSimpleList.empty())
{

someSimpleListType &sLT = someSimpleList.top();
cout<<"Callback["<<sLT.index<<"] = "<<sLT.callback(sLT.value,sLT.index)<<endl;
someSimpleList.pop();
}


//Since the priority queue is now empty, push some more elements
someSimpleList.push(someSimpleListType(funcA, a, index++));
someSimpleList.push(someSimpleListType(funcB, b, index++));
someSimpleList.push(someSimpleListType(funcB, c, index++));

//This time we will only process funcB and ignore funcA
cout<<endl<<"Test 2"<<endl;
while
(!someSimpleList.empty())
{

someSimpleListType &sLT = someSimpleList.top();
if
(sLT.callback == funcB)
{

cout<<"Callback["<<sLT.index<<"] = "<<sLT.callback(sLT.value,sLT.index)<<endl;
}

someSimpleList.pop();
}


return
0;
}


Quiz : function pointers as template arguments

Yesterday, I came across a piece of template code that took me a little by surprise (because I had not come across something like this before) but I was able to put my reasoning through. I will share the code first:

[CODE]

#include <iostream>

template<typename T>
void foo(const T&)
{
               std::cout << "const";
}

template<typename T>
void foo(T&)
{
               std::cout << "non-const";
}

void bar() { }

int main()
{
               foo(bar);
}

The question was - what would the program print? Will the argument "bar" resolve as a parameter to the first template having argument type "const F&" or to the second template having non-const argument type "F&"?

The easiest way to check for the resolution is to ask for explicit template instantiation of the "foo" function template. How? Here is how:

[CODE]

int main()
{
               typedef void (*f_ptr)(); //create a typedef for functions like bar
                                                               //taking no arguments and having return type as void.
               foo<const f_ptr>(bar); //1
               foo<f_ptr>(bar); //2
}

After that, just remove one of the "foo" templates. So, the code to check for compilation is this:

[CODE]
#include<iostream>

template<typename T>
void foo(T&)
{
               std::cout << "non-const";
}

void bar() { }

int main()
{
               typedef void (*f_ptr)(); //create a typedef for functions like bar
                                                               //taking no arguments and having return type as void.
               foo<const f_ptr>(bar); //1
               foo<f_ptr>(bar); //2
}


If you removed the second template, code compiles fine. But if you kept the second one and removed the first one, you will see that the compilation fails for mis-match in the argument type in statement "//2".

Problem solved. Isn't it? What does this tell about the argument "bar" ? It tells that it is a constant. And hence the call in the initial sample code would resolve to the template instance having the argument type declared const. It is in a way similar to any other type constants, for example 5, 100, 2000 are integral constants, they are literals. And when you declare something as say int i; here i is a variable that can be modified. but 5, 100, or 2000 cannot be. In our initial code, both the templates were capable of instantiating the right function for the argument. In both's presence, the argument match has to be exact and hence instantiation happens from the const one but in its absense the instantiation can happen even with the non-const one as the call can help the template to instantiate over type const f_ptr instead of just f_ptr (which is the case for the const one).

Functions pointers when being passed by taking the address of the function directly is a value of const function pointer type.

Check out this stream