Function’s Argument Evaluation and Type Checking

Hello,

Could you please explain it and give me an example? I didn't catch it.
1- A function’s argument is evaluated only once when the function is called.
2- The compiler performs type checking on functions.

Thanks in advance

Defining the CIRCLE_AREA macro as a function is safer. The circleArea function performs the same calculation as macro CIRCLE_AREA, but a function’s argument is evaluated only once when the function is called. Also, the compiler performs type checking on functions.
The preprocessor does not support type checking. In the past, programmers often used macros to replace function calls with inline code to eliminate the function-call overhead. Today’s optimizing compilers often inline function calls for you, so many programmers no longer use macros for this purpose.

 
#define CIRCLE_AREA(x) ((PI) * (x) * (x)) 


1
2
3
double circleArea(double x) {
return 3.14159 * x * x;
}
Last edited on
Compare the result of
1
2
3
4
r = 1;
cout << circleArea(r++) << endl;
r = 1;
cout << CIRCLE_AREA(r++) << endl;
1. If you pass an expression as argument to the CIRCLE_AREA the expression would end up being evaluated twice.

For example
 
double a = CIRCLE_AREA(f());
would expand to
 
double a = ((PI) * (f()) * (f())) ;
which might be a problem if f() has side effects that you only want to happen once or if f() is slow so that you don't want to call it more often than necessary.

If you instead write
 
double a = circleArea(f());
then f() would only be evaluated once.


2. If you pass an argument to the circleArea function that is not a double it will try to implicitly convert the argument to a double. If that fails you will get a compilation error.

The CIRCLE_AREA macro will not type check its argument. Instead it will just expand the macro code and give a compilation error if the * operator cannot be performed on the types involved. The downsides of this is not so easy to see in this situation because all the built-in numeric types can be implicitly converted to each other, and few other types support the binary * operator, but sometimes this lack of type checking can lead to code that still compiles but doesn't do what you expect.

Let's consider the following macro instead:
 
#define SUM(a, b) ((a) + (b)) 
If you passed two integers as arguments it would return the expected result
1
2
3
int a = 1;
int b = 2;
std::cout << SUM(a, b) << "\n"; // prints "3" 
but if you accidentally passed two std::strings it would instead do string concatenation.
1
2
3
std::string a = "1";
std::string b = "2";
std::cout << SUM(a, b) << "\n"; // prints "12" 
or if you called it with a string literal (i.e. char*) and an integer (perhaps believing you could use it for string concatenation) it would instead do pointer arithmetic on the char pointer.
 
std::cout << SUM("Hello", 2) << "\n"; // prints "llo" 


If you instead had used functions you wouldn't have ran into these issues because either the argument is implicitly converted to the expected type or it is rejected all together.

1
2
3
4
int sum(int a, int b)
{
	return a + b;
}
1
2
3
int a = 1;
int b = 2;
std::cout << sum(a, b) << "\n"; // prints "3" 
1
2
3
std::string a = "1";
std::string b = "2";
std::cout << sum(a, b) << "\n"; // error (no implicit conversion from std::string to int) 
 
std::cout << sum("Hello", 2) << "\n"; // error (no implicit conversion from char* to int) 

1
2
3
4
std::string concat(std::string a, std::string b)
{
	return a + b;
}
1
2
3
int a = 1;
int b = 2;
std::cout << concat(a, b) << "\n"; // error (no implicit conversion from int to std::string) 
1
2
3
std::string a = "1";
std::string b = "2";
std::cout << concat(a, b) << "\n"; // prints "12" 
 
std::cout << concat("Hello", 2) << "\n"; // error (no implicit conversion from int to std::string) 
Last edited on
Topic archived. No new replies allowed.