Chapter 36. C Language Considerations

FAQ 36.01 What are the main issues when mixing C and C++ code in the same application?

image

The main issues are

• The C++ compiler must be used to compile main(). This allows the C++ compiler to deal with static initialization and other magical things that main() has to do.

• The C++ compiler must be used to direct the linking process. This allows the C++ compilers to deal with templates and also to link in the C++ libraries.

• The C++ and C compilers probably need to come from the same vendor and have compatible versions. This allows them to use compatible calling conventions in the binary code they generate, as well as compatible naming strategies.

• The interface between the two languages should be kept reasonably small. For example, it is unwise to enable all C++ routines in the entire application to be callable from C, although the reverse is possible: it is not that hard to make C routines callable by C++.

• Read the rest of this chapter for more details.

FAQ 36.02 How can C++ code call C code?

The C++ compiler must know that the function has C linkage.

If the C code that is being called is part of the standard C library, such as printf(), simply #include the correct C header file and call the function.

If the C code that is being called is not part of the standard C library, the C++ compiler needs to know that the function is a C function. To do this, include an extern "C" block in the C++ code, and declare the C function inside the block:

image

An entire C header file can be included within an extern "C" block:

image

If a C header file needs to be included in a lot of C++ locations, the extern "C" part can be moved into the header file, provided only the C++ compiler sees it. The simplest way to do this is to create a new C++ header file that includes the C header file. For example, if “Fred.h” is a C header file, “Fred.hpp” can be created as a C++ header file:

image

The other approach is to modify the C header file directly, which obviously can only be done if the team has control over the C header file. The extern "C" part is wrapped in an #ifdef to make sure these lines are seen only by C++ compilers, not by C compilers. The idea is simple: insert the following lines near the top of the C header file.

image

Then insert the following near the bottom of the C header file.

image

This works because the C++ compiler automatically #defines the preprocessor symbol __cplusplus.

FAQ 36.03 How can C code call C++ code?

The C++ compiler must be told to compile the C++ function using C-compatible calling conventions (also known as C linkage). This is done using the same extern "C" construct as detailed in the previous FAQ, only this time the extern "C" goes around the declaration of the C++ function rather than the declaration of the C function. The C++ function is then defined just like any other C++ function:

image

The extern "C" declaration causes the C++ compiler to use C calling conventions and name mangling. For example, the C++ compiler might precede the name with a single underscore rather than the usual C++ name-mangling scheme.

The C code then declares the function using the usual C prototype:

image

There can be overloaded C++ functions with the same name as the function that was exported to the C code, but only one of these overloads can be declared as extern "C". Also, the C code cannot access more than one of these since C doesn't support name overloading. Member functions cannot be exported to C code using the extern "C" syntax.

FAQ 36.04 Why is the linker giving errors for C functions called from C++ functions and vice versa?

main() should be compiled with the C++ compiler, and the C++ compiler should direct the linking process.

The C++ compiler should be used to compile main() because it normally embeds C++-specific operations inside the compiled code (for example, to deal with static initialization; see FAQ 2.10). The C++ compiler should direct the linking process since it needs to deal with things such as C++ libraries, static initialization, and templates.

FAQ 36.05 How can an object of a C++ class be passed to or from a C function?

With a layer of glue code.

The example that follows is a bilingual header file, readable by both a straight C compiler and a C++ compiler. Bilingual header files often use the preprocessor symbol __cplusplus, which is automatically #defined by C++ compilers but left undefined by C compilers.

image

The function cCallingCpp() might be defined in a C++ file, such as c++-code.cpp.

image

The function cppCallingC() might be defined in a C file, such as c-code.c.

image

A Fred might be passed to the C code by main() (recall that main() must be compiled by the C++ compiler).

image

Note that C code should not cast pointers that refer to C++ objects because doing so can introduce errors, especially in cases where the pointer is returned to C++. For example, most compilers adjust the pointer during certain pointer casts involving multiple and/or virtual inheritance; the C compiler doesn't know how to do these adjustments.

The example assumes that the C compiler supports ANSI-C function prototypes. Use #ifdef __STDC__ for those rare legacy situations that require selecting code that supports only the outdated K&R C style.

FAQ 36.06 Can a C function directly access data in an object of a C++ class?

Sometimes.

First read the previous FAQ on passing C++ objects to and from C functions. A C++ object's data can be safely accessed from a C function if all of the following are true.

• The C++ class has no virtual base classes anywhere in the inheritance graph.

• The C++ class has no virtual functions (including inherited virtual functions).

• The C++ class has all its data in the same access level section (private:, protected:, or public:).

• The C++ class has no fully contained member objects that have either virtual functions or virtual base classes.

If the C++ class or its member object have any base classes, accessing the data will be technically nonportable, because the language does not mandate a specific class layout in the presence of inheritance. However, at least with nonvirtual inheritance, all C++ compilers do it the same way—the base class subobject appears first (in left-to-right order in the event of multiple inheritance), and member objects follow.

If the class has any virtual base classes, it is more complicated and even less portable.

By far the safer and easier way is to use an access function from C. This costs a function call to access the datum (that is, these calls from C cannot be inlined), but unless the application is CPU bound (see FAQ 33.02), it is probably best to make the application code safe and portable.

FAQ 36.07 Can C++ I/O (<iostream>) be mixed with C I/O (<stdio.h>)?

image

Yes, but be careful.

<iostream> and <stdio.h> can be used in the same program. The easiest way to mix them is to make sure that no single file is manipulated using both <iostream> routines and <stdio.h> routines.

If any given file needs to be manipulated by both <iostream> routines and <stdio.h> routines, special considerations must be taken into account. Make sure that ios::sync_stdio(false) has not been called. If it has then call ios::sync_with_stdio() as shown.

image

Note that this synchronization can degrade I/O performance, so it should be used only if <iostream> routines and <stdio.h> routines are manipulating the same file. For example, synchronization is needed if the program reads from both cin and stdin, or if it writes to both cout and stdout. But if <iostream> routines and <stdio.h> routines are not manipulating the same file, synchronization is unnecessary and should not be used.

FAQ 36.08 Which is safer: <iostream> or <stdio.h>?

<iostream> is safer than <stdio.h> due to improved type safety and less redundancy.

The <stdio.h> functions scanf() and printf() are interpreters of a tiny language, made up mainly of “%” fields (format specifiers). These functions select the correct I/O primitive at runtime by assuming that the format specifier and the actual argument are compatible; if they aren't compatible, garbage is printed or the program crashes. Thus, the programmer is required to provide duplicate information in the format specifier and the actual argument.

The <iostream> routines are different. Users provide only the object to be read or written; the compiler selects the correct I/O primitive at compile time via the rules of function overloading. Therefore, <iostream> is type safe since the selected primitive is always compatible with the actual argument.

FAQ 36.09 Which is more extensible: <iostream> or <stdio.h>?

<iostream> is more extensible than <stdio.h> since <iostream> allows I/O with user-defined types as well as built-in types.

The <stdio.h> functions scanf() and printf() work with a predefined set of types. In contrast, <iostream> allows new, user-defined data types to be written and read using the same syntax used for built-in types (that is, using << and >>). This extensibility is analogous to adding new “%” fields to the switch statement that is used within the implementation of scanf() and printf().

C++ allows user-defined types (class types) to look and act like built-in types.

FAQ 36.10 Which is more flexible: <iostream> or <stdio.h>?

<iostream> is more flexible than <stdio.h> since <iostream> separates the code that formats an object from the code that performs I/O of the character stream produced or consumed by formatting. This separation allows replacement of the underlying I/O mechanisms without the need to rewrite the formatting code.

For example, <iostream> uses real classes, hence users can create derived classes. User-defined types can thus look and act like streams but don't necessarily have to use the same underlying I/O mechanisms. The formatting code written for both user-defined and built-in types works correctly with these new classes.

FAQ 36.11 Why does it seem that C++ programming feels farther away from the machine than C?

Because it is.

Because C++ is an object-oriented programming language, it is designed to allow the creation and manipulation of objects from the problem domain. Thus, C++ allows programmers to operate at a higher level of abstraction: there is effectively a greater distance between the software and the machine. This higher level of abstraction allows programmers to develop software in the language of the problem domain rather than in the language of the computer. It is considered a feature, not a bug.

FAQ 36.12 Why does C++ do more things behind your back than C does?

Because the goal of programming in C++ is different than the goal of programming in C.

One of C's great strengths is that it has no hidden mechanism. What you see is what you get. You can read a C program and see every clock cycle.

This is not the case in C++. As an OO programming language, C++ has different goals than C. For instance, C++ calls constructors and destructors to initialize objects. Overloaded operators are another case in point—they provide a level of abstraction and economy of expression that lowers maintenance costs without destroying runtime performance. Longtime C programmers are often ambivalent about these features, but they soon realize their benefits.

Naturally, bad code can be written in any language. C++ doesn't guarantee any particular level of quality, reusability, abstraction, or any other measure of goodness.

C++ enables reasonable developers to write superior software. It doesn't make it impossible for bad programmers to write bad programs.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset