Item M34: How to mix C and C in the same program
For many years, you have been worried that a part of the C part of the C is used as part of the C part is as used as a plurality of compilers to generate a program. There is no way more compiler programming, unless the compiler is the same as the realization-related characteristics such as INT and DOUBLE, the transmission method). But this problem is ignored in the standardization of language, so the only way is to commit them to compatibility between the manufacturers of two compilers. C and C mixed programming are also this problem, so ensure that your C compiler and C compiler are compatible before the entity is mixed.
After confirming compatibility, there are four questions to consider: name transformation, static initialization, memory dynamic allocation, data structure compatibility.
* Name transformation
Name transformation, it is a unique name for each function of the C compiler. In C, this process is not needed because there is no function overload, but almost all C programs have functions (for example, the streamline library has a few versions of Operator << and Operator >>). Overload is not compatible with the vast majority of link programs because link programs are typically unable to distinguish between the same name. The name change is a compromise to the link, and the link program usually insists that the function name must be unique.
If only in the C range, the name transform will not affect you. If you have a function called Drawline and the compiler transforms it to XYZZY, you always use the name Drawline, and will not notice that the OBJ file behind XYzzy is XYZZY.
If Drawline is in the C run library, it is a different story. The header file containing your C source file is declared as:
Void Drawline (int X1, int y1, int x2, int y2);
Drawline is usually called in the code body. Each such call is converted to a function after calling name shift, so writing is
Drawline (A, B, C, D); // Call to unmangled function name
The OBJ file is called:
XYZZY (A, B, C, D); // Call to mangled function MAME
But if Drawline is a C function, the compiled Drawline function included in the OBJ file (or files such as dynamic link library) is still called drawline; no name transformation action. When you try to link the OBJ file to a program, you will get an error because the linker is looking for a function called XYZZY, without such a function exists.
To solve this problem, you need a way to tell the C compiler not to change in this function. You don't expect to change the functions written in other languages, such as C, compilation, Fortran, Lisp, Forth, or others. (Yes, this "other" should include COBOL, but what you will get? (Yes, what whave-you would include cobol, but then would you have, but if you call a name Drawline C function, it actually called Drawline, your OBJ file should contain such a reference, not a reference to the version of the transformation.
To prohibit name transform, use C Extern 'c' to indicate:
// Declare a Function Called Drawline; Don't mangle // ITS Name
EXTERN "C"
Void Drawline (int X1, int y1, int x2, int y2);
Don't think that there is an extern 'c', then there should be extern 'pascal' and extern 'Fortran'. No, at least in the C standard. Don't think of Extern 'c' as the declaration of this function is written in C language, it should be considered as a function of a function that should be called as a written written. (Use the term, extern 'c' means that this function has C link, but this means is not very clear. No matter what, it always means a thing: the name change is banned.)
For example, if unfortunately, you must write a function with compilation, you can also declare it for extern 'c':
// this function is in assemblymbler - Don't mangle its name
EXTERN "C" void twiddlebits (unsigned char bits);
You can even declare external 'c' on the C function. This is useful when you write a library with C to use customers who use other languages. By disabling the name of these C functions, your customers can use the natural and intuitive names you choose, instead of using your compile-generated transformation name:
// The Following C Function is designed for use outside
// c and shop not have it its name mangled
EXTERN "C" void Simulate (Ite ution);
Often, you have a bunch of functions that don't want to change, add external 'c' to each function. Fortunately, this is not necessary. EXTERN 'C' can take effect on a set of functions, as long as they put them in a pair of braces:
Extern "C" {// disable name mangling for
// all the following functions
Void Drawline (int X1, int y1, int x2, int y2);
Void TwiddleBits (unsigned char bits);
Void Simulate (INT IT ITe);
...
}
This simplifies the operation of the header files that must be used at the same time using Extern 'C'. When compile with C , you should add external 'c', but when using C, it should not be the case. You can organize the header file by only the macro __cplusplus defined under the C compiler.
#ifdef __cplusplus
Extern "C" {
#ENDIF
Void Drawline (int X1, int y1, int x2, int y2);
Void TwiddleBits (unsigned char bits);
Void Simulate (INT IT ITe);
...
#ifdef __cplusplus
}
#ENDIF
By the way, there is no standard name transform rule. Different compilers can use different transformation methods at will, and the fact that different compilers do do. This is a good thing. If all compilers use the same transformation rules, you will mistake the code they generate is compatible. Now, if the hybrid link comes from the OBJ file of the different compilers, it is poor to get a link error, because the transformed name does not match. This error hints, you may have other compatibility issues, and I find it better than I have found later. * Static initialization
After mastering the change, you need to face a C fact: there is a lot of code to be executed before the MAIN execution is executed and executed. In particular, static class objects and definitions of constructors in global, namespaces or class objects in the files are usually called before the main is executed. This process is called static initialization (see Item E47). This is the opposite of our usual understandings of C and C processes, we have been regarding main as an entrance to the program. Similarly, the object generated by static initialization should also call its destructor during the static destructory; this process usually occurs after the main operation of Main.
In order to solve the main () should be called first, the object needs to be constructed before main () execution, many compilers insert a special function at the beginning of main (), which is responsible for static initialization . Similarly, the compiler is inserted into the main () end inserted a function to sect a static object. The resulting code usually looks like this:
Int main (int Argc, char * argv [])
{
PerformStaticInitialization (); // generated by the
// Implementation
The Statements you put in main go here;
PerformStaticDestruction (); // generated by the
// Implementation
}
Don't pay attention to these names. Functions PerformStaticInitialization () and PerformStaticDestruction () are usually more vague names, even inline functions (these functions will not be found in your OBJ file). The point is: If a C compiler uses this method to initialize and destruct static objects unless main () is written with C , these objects will never be initialized and destructure. Because this initialization and destructive static objects are so common, as long as the program's arbitrary part is written, you should write the main () function with the C .
Sometimes it seems to write main () more meaningful - such as most of the program, C part is just a support library. However, this C library is likely to contain static objects (even if there is no, there may be - see Item M32), so write main () is still a good idea. This doesn't mean you need to override your C code. Just rename the main () written to RealMain (), then call realmain ():
Extern "C" // Implement THIS
Int realmain (int Argc, char * argv []); // Function IN C
INT Main (int Argc, char * argv []) // Write this IN C {
Return Realmain (Argc, Argv);
}
When doing this, it is best to add comments to explain the reason.
If you can't write main (), you have trouble, because there is no other way to ensure that the structure and destructor of the static object are called. It's not saved, just handling some trouble. Compiler producers know this problem, almost all provide an additional system to initiate static initialization and static sectations. To know how your compiler is implemented, excavate its random document or contact the manufacturer.
* Dynamic memory allocation
Dynamic memory allocation is now mentioned. The general rules are simple: C sections use new and delete (see Item M8), and the C portion uses Malloc (or its deformation) and free. As long as the memory allocated memory uses Delete release, Malloc allocated memory is released, then there is no problem. Use free release the memory allocated memory or release Malloc allocated with DELETE, its behavior is not defined. Then, the only thing to remember is: Strictly isolate your New and Delete with Mallco and Free.
It is easy to do. Look at this rough (but very convenient) strDup function, it is not in the C and C standards (running libraries), it is very common:
Char * strdup (const char * ps); // Return a Copy of Thae
// string pointed to by ps
To do not have memory leaks, StrDUP calls must be released in the memory allocated in strDup (). But this memory is released? Use DELETE? Use Free? If the strDUp you call comes from the C function library, then the latter. If it is written with C , it is prior to the former. The operation you need to do after calling StrDup, in different operating systems, different compilers are different. To reduce this portability problem, avoid calling those that are neither in a standard run library (see Item E49 and ITEM M35) without fixed forms (in most computer platforms).
* Compatibility of data structure
The last problem is to pass data between C and C. It is impossible to let C functions understand the characteristics of C , and their interaction must be limited to the concept of C. Therefore, it is clear that there is no portable method to transfer the object or transfer the function of the pointer to the member function to write. However, C understands the normal pointer, so you want your C and C compiler to produce compatible outputs, and the functions between the two languages can safely exchange pointers to the object, and pointers to non-member functions or static member functions. Naturally, the variables of the structure and built-in type (such as int, char, etc.) are also free to pass.
Because of the rules in C , the rule in C is assumed that "the same structure defined under the two types of compilers will be processed in the same way" is safe. Such a structure can be securely transmitted safely in C and C. If you add a non-virtual function in the C version, the memory structure has not changed, so only the structure of the non-virtual function (or class) is compatible with the twin version of them in C (its definition just removes these member functions) Declinations). Increase the virtual function will end the game because its object will use a different memory structure (see Item M24). Structure inherited from other structures (or class), usually also changed its memory structure, so the structure of the base class cannot interact with the C function. In terms of data structures, conclusion is that the mutual delivery of data structures in C and C is secure-providing the same definition to C and C to compile. Increasing the non-virtual member function in the C version may not affect compatibility, but almost other changes will affect it.
* to sum up
If you want to mix C and C programming under the same procedure, remember the following guidelines:
* Make sure the C and C compilers generate a compatible OBJ file.
* The function that will be used in both languages is extern 'c'.
* Write main () as long as it is possible, use C .
* Total DELETE releases the memory allocated by DELETE; always releases Malloc allocated memory.
* The things that will be delivered between two languages are limited to the range of data structures compiled with C; the C version of these structures can contain non-virtual member functions.