|
Imagine you are writing a program to process
cylinder-related calculations, that is, to get its diameter, its circumference, its
areas, and volume. You could start your program as follows: |
#using <mscorlib.dll>
using namespace System;
int main()
{
double Radius;
Radius = 25.55;
Console::WriteLine("Cylinder Summary");
Console::Write("Radius: ");
Console::WriteLine(Radius);
Console::WriteLine();
return 0;
}
This would produce:
Cylinder Summary
Radius: 25.55
Press any key to continue...
When we studied functions that return a value, we saw that
the result of such a function can be assigned to a value locally declared in the
calling function:
#using <mscorlib.dll>
using namespace System;
double CalculateDiameter(double Rad)
{
return Rad * 2;
}
int main()
{
double Radius, Diameter;
double CalculateDiameter(double R);
Radius = 25.52;
Diameter = CalculateDiameter(Radius);
Console::WriteLine("Cylinder Summary");
Console::Write("Radius: ");
Console::WriteLine(Radius);
Console::Write("Diameter: ");
Console::WriteLine(Diameter);
Console::WriteLine();
return 0;
}
At this time, we know that when a function returns a value,
the calling of the function is a complete value that can be assigned to a
variable. In fact, when calling a function that takes an argument, if that
argument itself is gotten from a value returned by a function, the calling of
the second function can be done directly when calling the first function. This seemingly
complicated scenario can be easily demonstrated as follows:
#using <mscorlib.dll>
using namespace System;
int main()
{
double Radius, Circumference;
double CalculateDiameter(double R);
double CalculateCircumference(double D);
Radius = 25.52;
// Instead of calling the CalculateDiameter() function first and
// assign it to another, locally declared variable, such as in
// "double Diameter = CalculateDiameter(Radius)", we can call the
// CalculateDiameter(Radius) directly when we are calling the
// CalculateCircumference() function.
// This is possible because the CalculateCircumference() function
// takes an argument that is the result of calling the
// CalculateDiameter() function. As long as we only need the
// circumference and we don't need the diameter, we don't have
// to explicitly call the CalculateDiameter() function.
Circumference = CalculateCircumference(CalculateDiameter(Radius));
Console::WriteLine("Cylinder Summary");
Console::Write("Radius: ");
Console::WriteLine(Radius);
Console::Write("Circumference: ");
Console::WriteLine(Circumference);
Console::WriteLine();
return 0;
}
double CalculateDiameter(double Rad)
{
return Rad * 2;
}
double CalculateCircumference(double Diam)
{
return Diam * Math::PI;
}
This would produce:
Cylinder Summary
Radius: 25.52
Circumference: 160.3467536
Press any key to continue
In some circumstances, such as this one, we may find out
that the value we want to process in a function is in fact a value gotten from
an intermediary function. Unfortunately, a regular function cannot be passed to
a function like a regular variable as argument. In reality, the C++ language allows this but
the function can be passed as a pointer.
Declaring a Pointer to Function |
|
The concept of a callback function is
highly used in Microsoft Windows application programming. For this
reason, you should know how callback functions work and how to use them. A callback function is a pointer to a function. A pointer to
a function is a special function that is declared as a pointer. Its name by
itself is considered a variable. As such, and unlike a regular variable, the
name of this function can be assigned a regular function. This allows the
function to be passed as argument. The function itself is not implemented but
its name is used as a programmer type-defined object. The reason a function can be passed as argument is because
the name of a function is itself a constant pointer.
The basic syntax to declare
a pointer to a function is:
DataType (*FunctionName)();
The DataType can be any of the data types we have
used so far and objects we will learn in future lessons.
The FunctionName must be a valid name for a function.
The name of the function must be preceded by an asterisk operator. To actually
make this declaration a pointer to a function, the asterisk and the name of the
pointer must be included between parentheses. If you omit the parentheses, the
compiler would think that you are declaring a function that returns a
pointer, which changes everything.
Because this is a pointer, you must use parentheses,
required for every function declared. If this function will not take any
argument, you can leave the parentheses empty or type void.
Based on this, you can declare a pointer to a function as
follows:
int main()
{
void (*SomethingToDo)(void);
return 0;
}
After declaring a pointer to a function, keep in mind that
this declaration only creates a pointer, not an actual function. In order to use
it, you must define the actual function that would carry the assignment the
function is supposed to perform. That function must have the same return type
and the same (number of) argument(s), if any. For example, the above declared
pointer to function is of type void and it does not take any
argument. you can define a function as follows:
void MovieQuote()
{
Console::Write("We went through a lot of trouble because of you");
Console::Write("You owe us");
Console::Write(" From \"Disorganized Crime\"");
}
With such an associated function defined, you can assign it
to the name of the pointer to function as follows
SomethingTodo = MovieQuote;
This assignment gives life to the function declared as
pointer. The function can then be called as if it had actually been defined.
Here is an example:
#using <mscorlib.dll>
using namespace System;
void MovieQuote()
{
Console::WriteLine("We went through a lot of trouble because of you");
Console::WriteLine("You owe us");
Console::WriteLine(" From \"Disorganized Crime\"");
}
int main()
{
void (*SomethingToDo)();
// Assign the MovieQuote() function to the pointer to function
SomethingToDo = MovieQuote;
// Call the pointer to function as if it had been defined already
SomethingToDo();
Console::WriteLine();
return 0;
}
This would produce:
We went through a lot of trouble because of you
You owe us
From "Disorganized Crime"
Press any key to continue...
You can also declare a pointer to function for a function
that returns a value. Remember that both functions must return the same type of
value. Here is an example:
#using <mscorlib.dll>
using namespace System;
int Addition()
{
int a = 16, b = 442;
return a + b;
}
int main()
{
int (*SomeNumber)();
// Assign the MovieQuote() function to the pointer to function
SomeNumber = Addition;
// Call the pointer to function as if it had been defined already
Console::Write("The number is ");
Console::WriteLine(SomeNumber());
Console::WriteLine();
return 0;
}
If you want to use a function that takes arguments, when
declaring the pointer to function, provide the return type and an optional name for
each argument. Here is an example:
int (*SomeNumber)(int x, int y);
When defining the associated function, besides returning the
same type of value, make sure that the function takes the same number of
arguments. Here is an example:
#using <mscorlib.dll>
using namespace System;
int Addition(int a, int b)
{
return a + b;
}
int main()
{
int (*SomeNumber)(int x, int y);
int x = 128, y = 5055;
// Assign the MovieQuote() function to the pointer to function
SomeNumber = Addition;
// Call the pointer to function as if it had been defined already
Console::Write(x);
Console::Write(" + ");
Console::Write(y);
Console::Write(" = ");
Console::WriteLine(SomeNumber(x, y));
Console::WriteLine();
return 0;
}
This would produce:
128 + 5055 = 5183
Press any key to continue
You can also create a programmer-defined type as a pointer
to function. Here is the syntax to use:
typedef (*TypeName)(Arguments);
The typedef keyword must be used.
The TypeName and its asterisk must be enclosed in
parentheses. The name must follow the rules applied to objects so far.
The TypeName must be followed by parentheses. If the
pointer to function will take arguments, provide its type or their types between
parentheses. Otherwise, you can leave the parentheses empty (but you must
provide the parentheses).
After creating such a custom type, the name of the type
would be used as an alias to a pointer to function. Consequently, it can be used
to declare a pointer to function. Here is an example:
#using <mscorlib.dll>
using namespace System;
int Addition(int a, int b)
{
return a + b;
}
int main()
{
// Creating a programmer-defined type
typedef int (*AddsTwoIntegers)(int x, int y);
// Now, the AddsTwoIntegers name is a pointer to function
// that can take two integers. It can be used for a declaration
AddsTwoIntegers SomeNumber;
int x = 128, y = 5055;
SomeNumber = Addition;
// Call the pointer to function as if it had been defined already
Console::Write(x);
Console::Write(" + ");
Console::Write(y);
Console::Write(" = ");
Console::WriteLine(SomeNumber(x, y));
Console::WriteLine();
return 0;
}
This would produce:
128 + 5055 = 5183
Press any key to continue...
A Pointer to a Function as Argument |
|
Using pointer to functions, a function can be passed as
argument to another function. The function must be passed as a pointer. The
argument is declared in a complete format as if you were declaring a function.
Here is an example of a function that is passed a function as argument.
double Circumference(double (*FDiam)(double R))
This Circumference() function takes one argument. The
argument itself is a pointer to function. This argument itself takes a
double-precision number as argument and it returns a double-precision value. The
Circumference() function returns a double-precision number.
It is important to know that the pointer to function that is
passed as argument is declared completely, in this case as
double (*FDiam)(double R)
Although the FDiam declaration is accompanied by an
argument, in this case R, this argument allows the compiler to know that FDiam
takes an argument. This argument actually will not be processed by the
Circumference() function when the Circumference() function is defined because
the R argument does not belong to the Circumference() function.
When calling the Circumference() function, you will use the
FDiam argument as a variable in its own right, using its name, as in
Circumference(Diameter)
When defining the Circumference() function, you must process
the pointer to function that it takes as argument. If this argument is an alias
to a function that returns a value, you can call it and pass it the argument as
we studied in the last section. If you want to involve the FDiam argument in any
operation, you can declare a local variable to the Circumference() function. If
the FDiam argument must be involved in an operation that involves a value
external to the Circumference() function, you must pass that type of value as
argument to the Circumference() function, unless you are using a global variable. This means
that, in most circumstances, the pointer to function passed as argument may be
accompanied by at least one other argument. For example, if you want to use the
FDiam as a diameter value to calculate the circumference (Circumference =
Diameter * PI), you may have to declare it with an argument for the radius. It
would be declared as follows:
double Circumference(double (*FDiam)(double R), double Rad);
The function can then be implemented as follows:
double Circumference(double (*FDiam)(double R), double Rad)
{
double Circf;
Circf = (*FDiam)(Rad);
return Circf * Math::PI;
}
Remember that, when declaring a function, the compiler does
not care about the name(s) of the argument(s). If the function takes any, what
the compiler cares about are the return type of the function, its name, and the
type(s) of its argument(s), if any. Therefore, the above function could as well
be declared as follows:
double Circumference(double (*)(double), double);
This indicates that the Circumference() function takes two
arguments whose names are not known. The first argument is a pointer to a
function that takes one double-precision number as argument and returns a
double. The second argument of the Circumference() function is also a
double-precision number. The Circumference() function returns a double-precision
number. This is what the program at this time would look like:
#using <mscorlib.dll>
using namespace System;
double Diameter(double);
double Circumference(double (*D)(double R), double r);
int main()
{
double Radius;
Radius = 25.52;
Console::WriteLine("Cylinder Summary");
Console::Write("Radius: ");
Console::WriteLine(Radius);
Console::Write("Circumference = ");
Console::WriteLine(Circumference(Diameter, Radius));
Console::WriteLine();
return 0;
}
double Diameter(double Rad)
{
return Rad * 2;
}
double Circumference(double (*FDiam)(double R), double Rad)
{
double Circf;
Circf = (*FDiam)(Rad);
return Circf * Math::PI;
}
This would produce:
Cylinder Summary
Radius: 25.52
Circumference = 160.3467536
Press any key to continue
To simplify the declaration of a pointer to function, we saw
that you can create a programmer-defined type using the typedef keyword.
This can also help when passing a function as argument. Here is an example:
typedef double (*FDiam)(double R);
double Circumference(FDiam, double);
When creating such a programmer-defined type, remember that
you must give a name to the alias, in this case FDiam. After this creation,
FDiam is an alias to a pointer to function of a double-precision type and which
takes one double-precision number as argument.
Remember, as we learned when studying functions that return
a value, that the item on the right side of the return keyword can be a value or
a complete expression. Therefore, you can simplify the implementation of the
Circumference() function as follows:
double Circumference(double (*FDiam)(double R), double Rad)
{
const double PI = 3.14159;
return (*FDiam)(Rad) * PI;
}
An Array of (Pointers to) Functions |
|
To further refine the call to a group of functions that
perform the same kind of task, you can declare an array of pointers to a type of
function. Before creating an array of pointers to function, you must first know or
have the functions you would be referring to. These functions must have a
similar signature. This means that they must return the same type of value, they
must have the same number of arguments and they must have the same
type(s) of argument(s). Here are examples of such functions:
double Diameter(double Radius)
{
return Radius * 2;
}
double Circumference(double Radius)
{
return Diameter(Radius) * PI;
}
double Area(double Radius)
{
return Radius * Radius * PI;
}
To declare an array of pointers to function, you can first
define an alias to the variable. This is an example:
typedef double (*Measure)(double R);
After this definition, as we learned already, Measure is an
alias to a function that takes a double type of variable and returns a double
value. Using this name, you can declare an array of functions. The members of
the array are names of the functions that would compose the array. Here is an
example:
Measure Calc[] = { Diameter, Circumference, Area };
You can initialize each member using its index and calling
the corresponding function. This can be done as follows:
#using <mscorlib.dll>
using namespace System;
const double PI = 3.14159;
double Diameter(double Radius)
{
return Radius * 2;
}
double Circumference(double Radius)
{
return Diameter(Radius) * PI;
}
double Area(double Radius)
{
return Radius * Radius * PI;
}
int main()
{
typedef double (*Measure)(double R);
double R = 12.55;
Measure Calc[] = { Diameter, Circumference, Area };
double D = Calc[0](R);
double C = Calc[1](R);
double A = Calc[2](R);
Console::WriteLine("Circle Characteristics");
Console::Write("Diameter: ");
Console::WriteLine(D);
Console::Write("Circumference: ");
Console::WriteLine(C);
Console::Write("Area: ");
Console::WriteLine(A);
Console::WriteLine();
return 0;
}
This would produce:
Circle Characteristics
Diameter: 25.1
Circumference: 78.853909
Area: 494.808278975
Press any key to continue
|