Question

I've been a Java and VB.Net programmer for about 4 years and a C# programmer for about 6 months. I've also used a bunch of dynamic languages like Perl, Python, PHP, and JavaScript.

I've never had a need for a preprocessor.

My question is: why do you see such extensive use of preprocessors in C, C++, and Objective-C but rarely (or never) see it in languages like Java, C#, or Scala?

Was it helpful?

Solution

I don't know Objective-C, so my answer will be about contrasting the use of the preprocessor in C and C++.

The preprocessor was originally necessary for C for several reasons. If I remember correctly, originally C did not have constants, so #define was needed to avoid magic numbers. Prior to 1999 C did not have inline functions, so again #define was used to create macros or "pseudo-functions" to save the overhead of a function call, while keeping code structured. C also doesn't have run-time or compile-time polymorphism, so #ifdefs were needed for conditional compilation. Compilers were typically not smart enough to optimize away unreachable code, so, again, #ifdefs were used to insert debugging or diagnostic code.

Using the preprocessor in C++ is a throwback to C, and is generally frowned upon. Language features, such as constants, inline functions, and templates can be used in most situations where in C you would have used the preprocessor.

The few cases where the use of a pre-processor in C++ is acceptable or even necessary include the guards for the header files, to prevent the same header from being included multiple times, #ifdef __cplusplus to use the same header for both C and C++, __FILE__ and __LINE__ for logging, and a few others.

The preprocessor is also often used for platform-specific definitions, although C++ Gotchas by Stephen Dewhurst advises having separate include directories for the platform specific definitions, and using them in separate build configurations for each platform.

OTHER TIPS

The reason why you don't see the preprocessor used in Java, C#, or Scala is that those languages obviously don't have one.

One of the common uses for the C preprocessor is to help provide platform-specific code. Since C (I'm including C++ and Objective-C here) is a low-level language that needs to interface with the operating system directly, in portable code there must necessarily be different sections of the code compiled for different operating systems. You can find extensive examples of this sort of thing in a mature, highly portable code base such as zlib.

As a simple example, to close a network socket one must do something like this (at some level, this can certainly be wrapped in a function but it has to exist somewhere):

#ifdef WIN32
    closesocket(s);
#else
    close(s);
#endif

Newer languages that run on VMs do not need the different platform-specific sections of code, and can be written against the single, portable standard library.

The preprocessor also provides a way to define constants in C, which are provided by other, better, language features in newer languages.

In The Design and Evolution of C++, Bjarne Stroustrup stated that he wanted to remove the dependency on the preprocessor in C++, but was not successful.

Every language needs a mechanism for separate compilation. Ideally the language distinguishes interfaces from implementations, and a module depends only on the interfaces of the modules it exports. (See, e.g., Ada, Clu, Modula, and so on.)

C has no language construct for interfaces or implementations. Because it's vital that different .c files share a single view of interfaces, the programming discipline evolved of putting declarations (i.e., interfaces) in .h files and sharing those declarations/interfaces using textual inclusion (#include). In principle, #define and #ifdef could be dispensed with, but #include could not.

Nowadays language designers recognize that textual inclusion is no way to run a railroad, so languages tend to run either to separately compiled interfaces (Ada, Modula, OCaml), to compiler-generated interfaces (Haskell), or to dynamic systems that guarantee interface consistency (Java, Smalltalk). With such a mechanism, there is no need for a preprocessor, and plenty of reasons not to have one (think source-code analysis and debugging).

Because the design and aims of these languages are not the same.

C was built with the preprocessor in mind as a powerful tool, it was used to implement very basic stuff(such as inclusion guards) and developers were able to use it to either optimize their code through macros or to optionally include/exclude certain blocks of code in addition to other things. C++ inherited most of C's idioms, macros are not used for speed anymore(because inline was introduced) but it's still used for plenty of things, see the post What are preprocessor macros good for?

Because Gosling and Heilsberg both understand the perils and technical debt incurred with the misuse of preprocessing!

I disagree with what seems to be the going consensus where that cpp is un-necessary in modern languages. I have a lot of cases where I've got 3 slightly different versions of the same program, and I want to be able to make a bunch of changes for each version. With CPP, I can put them all in #if #else blocks, and I can define the #if at the compile line. in Java, I need to create some sort of static global and initialize it at compile time. I never got that to work properly.

Preprocessing is very, very common in the Java world. It's used to compensate for the language's lack of adequate built-in abstraction facilities, which would otherwise lead to endless copied-and-pasted boilerplate code.

The reason many people don't realise this is true is that in the Java world it's called "code generation" rather than "preprocessing", because "preprocessor" sounds like nasty old C, while "code generation" sounds like a professional tool that efficiates mature enterprise processes. It's still preprocessing, though, even if you have to pay a fortune for an incompatible non-standard proprietary tool to do it, instead of just using facilities built into the language.

The preprocessor in C and C++ has two different functions

  • pulling files together in the build process - languages like Java et al. have their own mechanisms like import to do this

  • performing textual substitutions - this is still needed to certain extent in C, but C++ can do it (for the most part) better using templates

So both C and C++ need it for the first of these, but C++ can junk it for the second, although it can be useful even in C++ - see this question from earlier today.

Modern languages have the preprocessor included in the language itself! For C++, the preprocessor is needed only for module management and conditional inclusion for example which is very useful.

I believe it was a separate tool, because compilers were not one-tool as we know them today. I heard that very old C compilers used to produce the tokens to files, then do the rest of compilation in separate stages. The main reason I can think of is that memory and other resources were very scarce compared to what we have today.

You can be sure that the modern language is written in C or C++, and in that implementation itself there are macros. You need them to deal with operating system differences for one thing. The dynamic / higher-level languages wrap, and hide many of the things that somewhere at the bottom, you need macros for.

Also, macros are used for speed sometimes. In dynamic languages, speed isn't as crucial.

You should look a bit more closely at Perl. Perl supports source filters, which are basically custom Perl pre-processors written in Perl :)

Java was designed to avoid several features that make C++ hard to use.

C# copy ( or inherit ) most of the design decision from Java.

Higher level programming languages avoid this kind of low level artifacts.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top