5

I was reading a book on c++ (A Complete Guide to Programming in C++ by Ulla Kirch-Prinz and Peter Prinz; ISBN: 0-7637-1817-3) and it mentioned that isupper(), along with islower(), isalpha(), isdigit(), isalnum(), isspace(), and isprint(), are macros for character classification.

I find this kind of weird for two reasons:

  1. In the same book, it talks about how, even though macros can have arguments, they are still essentially just placeholder names that are filled in with the definition. This seems weird, since I don't know what kind of replacement text would operate conditionally. That sounds more in line with either a function or a built-in operation (like sizeof).

  2. It also seems weird because I've heard some people call these functions, while I've also heard some people call them macros.

Any explanation would be dearly appreciated. I'm moderately new to this, so I'm still trying to figure this all out. Thank you.

Ardent Coder
  • 3,777
  • 9
  • 27
  • 53
thepufferfish
  • 441
  • 4
  • 17
  • Aparently in C it's both. But in C++ it's a function. – john Apr 05 '20 at 20:55
  • 2
    That book dates from 2001, in C++ terms that's ancient history. The language has changed a lot. – john Apr 05 '20 at 20:58
  • @john They changed it? – thepufferfish Apr 05 '20 at 20:59
  • 2
    There have been three major revisions of the language since that book was written, and we are about to get the fourth. – john Apr 05 '20 at 21:00
  • most likely an inline function which avoids call stack overhead. – seand Apr 05 '20 at 21:05
  • @john Having said that, I'm still confused how an earlier version would've gone about implementing isupper() as a macro. As I've mentioned in point 1, macros just kind of replace text before something is processed; I don't know what kind of expression would be needed to create a conditional thing. – thepufferfish Apr 05 '20 at 21:05
  • Historically in C, macros have been used to minimize function call overhead and for other reasons. – seand Apr 05 '20 at 21:07
  • 2
    In C++, all of those have been functions since the first standard. If you mean "They changed it (from C to C++)?" then the answer is yes. If you mean Kirch-Prinz and Prinz got it wrong in their book, the answer is yes. – Eljay Apr 05 '20 at 21:07
  • 1
    @thepufferfish This question https://stackoverflow.com/questions/61038709/use-of-macro-overrides-for-functions/61038741, includes an extract from the ctype.h header file (for one particular compiler) which defines these macros. – john Apr 05 '20 at 21:08
  • And now I'm a bit confused. It seems like the two previous comments contradict each other. – thepufferfish Apr 05 '20 at 21:12
  • 1
    In C++ the classification functions have never been macros. `` is a C header. In C++ you’d use ``. – Pete Becker Apr 05 '20 at 22:22

2 Answers2

6

Standard C (in all its versions, as far as I know) allows any standard library function to also be defined as a macro. In the current C standard, that's buried in §7.1.4 (Use of Library Functions), paragraph 1:

Any function declared in a header may be additionally implemented as a function-like macro defined in the header… Any invocation of a library function that is implemented as a macro shall expand to code that evaluates each of its arguments exactly once, fully protected by parentheses where necessary, so it is generally safe to use arbitrary expressions as arguments.

The macros may be defined by a standard library implementation in order to provide efficiencies (although this is less important than it used to be, now that most compiler will inline functions), but the functions also need to be defined as functions with exactly the same semantics, which means that you can use the function without including the header (as long as you provide the correct function prototype) and that you can take the address of the function.

And, as indicated in the second sentence I quoted, you don't have to worry about the possible odd side effects of macros, such as multiple evaluation of their arguments -- unless the description of the function indicates otherwise. (One such function is getc, which is semantically identical to fgetc except that getc is allowed to implemented as a macro which does evaluate its argument more than once.)

It was never the case that these functions were only implemented as macros (at least, not in conformant standard libraries). But there was a time when it was so common to also implement simple functions as macros that some people just counted on the macros existing. Or talked about them as though that was the only implementation.

None of this is the case in C++. C++ requires functions to be functions (and macros to be macros), and the functions are in namespaces. That didn't make C++ incompatible with C; it was merely necessary for the C++ headers which declare functions shared with C (that is, the ones whose names start with c) to #undef all the (potential) macros definitions after including the corresponding C header.

rici
  • 234,347
  • 28
  • 237
  • 341
  • So let me see if I have this right. Macros were used because the code would be put _into_ the program (such as `main()`), instead of invoking functions and adding to the call stack. I'm still not entirely sure what you mean by 'evaluation' (once or multiple times) of arguments. I'm a little bit confused about 'not including the header' as long as you can provide the prototype. If you don't have a header with a function matching the prototype, the prototype is kind of undefined, no? – thepufferfish Apr 06 '20 at 05:56
  • Also, just out of curiousity, how are macro-versions of functions actually made? So far as I understand, macros have to be defined on one line, which is fine and well for something like `#define PRINT(x) (cout << x);` or `#define PRINT(x) printf(x);` but even for slightly more complex functions, such as `isupper()`, I would imagine there would be a single, long line of code. – thepufferfish Apr 06 '20 at 06:00
  • @thepufferfish: macros were used to inline, yes. A macro parameter is expanded: that is, replaced with its argument. That could result in it being executed several times. For example: `#define DOUBLE(X) ((X)+(X))`. Now use it in your program: `int x = DOUBLE(printf("%s\n", "Hello"));`. – rici Apr 06 '20 at 06:04
  • This has to be the main argument against macros. – john Apr 06 '20 at 06:08
  • 1
    Standard C allows you to use a standard library function without including the header as long as you yourself include the correct prototype. You can copy the prototype from the standard, for example. If you are only going to use `toupper` and you don't want to `#include ` you are allowed to just declare it: `int toupper(int c);`. I'm not saying that's a good idea but it's legal. – rici Apr 06 '20 at 06:10
  • Finally, you can use `\` at the end of a line to continue with the next source line. Look at some example macro definitions. – rici Apr 06 '20 at 06:11
  • @rici Okay, I think I understand. But this is just something that can happen with the standard ones. It isn't something a person could implement on their own. I'm going to guess the compilers access the standard headers anyways? – thepufferfish Apr 06 '20 at 06:13
  • @john: i don't think anyone will argue that inlining isn't better. It just wasn't available 30 years ago. Frankly, the sooner we stop thinking about C90 being somehow the "pure" version of C, the better. I look forward to the day when C90 is no longer taught, although I might not live to see it. – rici Apr 06 '20 at 06:14
  • @thepufferfish: don't confuse a header with a library. The definition of the function is in a library, and the C compiler knows where to find it. The declaration is in the header, but you can provide it yourself if you want to. – rici Apr 06 '20 at 06:18
  • If you still don't understand the difference, find a good book :-) You can't program in C without a good understanding of what a header is and what a library is. – rici Apr 06 '20 at 06:19
  • @rici Could you recommend a good title? I thought the book I had was decent though, I didn't even realize until now that it is outdated. – thepufferfish Apr 06 '20 at 06:21
  • There's a CC-licensed version of [Jens Gustedt's "Modern C"](https://modernc.gforge.inria.fr/). It's *not* an introductory text, but it has good information and good exercises. It's good to be challenged a bit, no? For a good basic explanation of the C preprocessor, I recommend the [Gnu CPP manual](https://gcc.gnu.org/onlinedocs/cpp/) – rici Apr 06 '20 at 06:45
  • There's also this [book list](https://stackoverflow.com/questions/562303/the-definitive-c-book-guide-and-list) here on SO. I don't endorse all of them. :-( – rici Apr 06 '20 at 06:51
  • @thepufferfish: If you're asking about C++, there's a [list here](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list). The only books I have experience with are those by Stroustrup, which have served me well. – rici Apr 06 '20 at 07:06
  • @rici so, I still don't entirely understand where I mixed up headers and libraries. So far as I can tell from the K&R book, macros, functions, etc. are in standard headers that make up the library. – thepufferfish Apr 11 '20 at 08:44
  • 1
    @thepufferfish: Citation? The headers include macros and function *declarations*, as I said. The function *definitions* are in libraries. I don't believe K&R contradicts that in any way, although it might not be as clear as one might want. – rici Apr 11 '20 at 14:24
  • @rici Okay, I think I get it now. I slipped up and mixed up declarations and definitions. So the compiler has access to the definitions in the library (I'm not %100 sure where it is, but I think it's built into the compiler, no?), and you can forgo header files (and the declarations in them) by including a prototype as the declaration. – thepufferfish Apr 12 '20 at 00:29
  • 1
    @thepufferfish: most of the standard library is in an actual library file; the path is system-dependent but if you're using gcc or clang, you can probably find out which library file is being used by linking with the -v option. On Gnu/linux systems, most of the standard library is in glibc. The math library is in libm, probably in the same directory. Some standard library functions are implemented directly by the compiler. Some standard library functions are called by generated code. The implementation has a lot of latitude. But most of it is just an ordinary library, like one you might build. – rici Apr 12 '20 at 00:50
5

Apart from the history discussed in the comments,

isupper is officially a function in C++.

See the documentation in that link. It shows the signature, namespace, and other details.

Edit: (response to the comment)

Historically, yes it might have been a macro while evolving from C. Because that was implementation defined.

If you wonder how that macro could have been defined, see this.

Ardent Coder
  • 3,777
  • 9
  • 27
  • 53
  • This does seem to resolve the issue of a macro with conditional components. Having said that, I am wondering why some people refer to it as a macro. If it historically was a macro at one point, how could it be defined? And if it wasn't, why would so many people talk about it as a macro? – thepufferfish Apr 05 '20 at 21:18
  • The classification functions have never been macros in C++. – Pete Becker Apr 05 '20 at 22:14
  • @PeteBecker Notice that I said **C** in that sentence and **it** referred to `isupper` – Ardent Coder Apr 05 '20 at 22:14