More effective tive C ++ item m34: how to mix C ++ and C in the same program

Source: Internet
Author: User
Item m34: how to mix C ++ and C in the same program
For many years, you have been worried about using C ++ in part during programming, just as using multiple compilers to generate programs in the age of all C programming. There is no way to program Multiple compilers, unless different compilers have the same implementation-related features (such as the size of the int and double bytes and the passing method. However, this problem is ignored in language standardization, so the only way is that the producers of the two compilers promise that they are compatible. This is also the problem in mixed programming of C ++ and C, so make sure that your c ++ compiler is compatible with the C compiler before mixed programming of entities.
After confirming the compatibility, there are four issues to consider: Name Change, static initialization, dynamic memory allocation, and data structure compatibility.
* Name Conversion
Name conversion means that the C ++ compiler gives every function of a program a unique name. In C, this process is not required because there is no function overload, but almost all c ++ programs have function names (for example, the stream Runtime Library declares several versions of operator <and operator> ). Reload is not compatible with most linked programs, because linked programs generally cannot distinguish functions with the same name. Name conversion compromises the linked program. The linked program usually insists that the function name must be unique.
If it is only within the range of C ++, the name change will not affect you. If you have a function called drawline and the compiler converts it to xy‑line, you always use the name drawline and won't notice that the OBJ file references xy‑line.
If drawline is in the C Runtime library, it is a different story. The header file contained in your c ++ source file is declared:
Void drawline (INT X1, int Y1, int X2, int Y2 );
In the Code body, drawline is also called. Every such call is converted by the compiler to the function after the name transformation.
Drawline (A, B, C, D); // call to unmangled function name
The OBJ file calls the following:
Xywise (A, B, C, D); // call to mangled function Mame
However, if drawline is a C function, the compiled drawline function in the OBJ file (or a file such as a dynamic link library) is still called drawline; there is no name transformation action. When you try to link the OBJ file to a program, you will get an error because the linked program is looking for a function called xyruntime without such a function.
To solve this problem, you need a method to tell the C ++ compiler not to perform name transformation in this function. You do not want to perform name transformations on functions written in other languages, such as C, assembly, Fortran, lisp, forth, or others. (Yes, this "other" should include COBOL, but what will you get at that time? (Yes, what-have-you wowould include COBOL, but then what wowould you have? ) In short, if you call a C function named drawline, it is actually called drawline. Your OBJ file should contain such a reference, instead of referencing a version with a name change.
To disable name conversion, use the 'pattern' of 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 );
Do not think there is an extern 'C', so there should also be an extern 'pascal 'and an extern 'fortran '. No, at least not in the C ++ standard. Do not regard extern 'C' as a declaration that this function is written in C language. It should be considered as a declaration that a function should be called as if it was written in C. (The term extern 'C' indicates that the function has a C link, but the expression is not clear. In any case, it always means that the name change is forbidden .)
For example, if you unfortunately have to write a function using an assembly, you can declare it as extern 'C ':
// This function is in your er-Don't mangle its name
Extern "C" Void twiddlebits (unsigned char bits );
You can even declare the extern 'C' on the C ++ function '. This is useful when you use C ++ to write a database for customers using other languages. By disabling the name transformation of these C ++ functions, your customers can use the natural and intuitive name you choose, instead of using the transformed name generated by your compilation:
// The following C ++ function is designed for use outside
// C ++ and shocould not have its name mangled
Extern "C" Void simulate (INT iterations );
Often, you have a bunch of functions that do not want to perform name transformations. It is painful to add extern 'C' to each function. Fortunately, this is unnecessary. Extern 'C' can take effect for a group of functions, as long as they are placed in a pair of braces:
Extern "C" {// disable name mangling
// All the following functions
Void drawline (INT X1, int Y1, int X2, int Y2 );
Void twiddlebits (unsigned char bits );
Void simulate (INT iterations );
...
}
Using extern 'C' simplifies the maintenance of header files that must be used by both C ++ and C. When compiling with C ++, you should add extern 'C', but this should not be the case when compiling with C. By defining the macro _ cplusplus only under the C ++ compiler, you can organize the header file as follows:
# Ifdef _ cplusplus
Extern "C "{
# Endif
Void drawline (INT X1, int Y1, int X2, int Y2 );
Void twiddlebits (unsigned char bits );
Void simulate (INT iterations );
...
# Ifdef _ cplusplus
}
# Endif
By the way, there is no standard name conversion rule. Different compilers can use different transformation methods at will, but in fact different compilers do the same. This is a good thing. If all compilers use the same conversion rules, you will mistakenly assume that the code they generate is compatible. Now, if the mixed link comes from the OBJ files of different compilers, it is very likely that the link should be incorrect because the converted name does not match. This error implies that you may have other compatibility issues. It is better to find it earlier than to find it later.
* Static Initialization
After understanding the name change, you need to face the fact in C ++ that a large amount of code is executed before and after the main execution. In particular, static class objects and constructors Defining class objects in a global, namespace, or file body are usually called before the main is executed. This process is called static initialization (see item e47 ). This is opposite to our general understanding of C ++ and C Programs. We always regard main as the entry point of the program. Similarly, the object generated through static initialization also needs to call its destructor during the static destructor process. This process usually occurs after the main end of running.
In order to solve the dilemma that the main () should be called first, and the object needs to be constructed before the main () Execution, many compilers () A special function is inserted at the very beginning, which is responsible for static initialization. Similarly, the compiler inserts a function at the end of main () to analyze static objects. The generated code usually looks like this:
Int main (INT argc, char * argv [])
{
Initialize mstaticinitialization (); // generated by
// Implementation
The statements you put in main go here;
Required mstaticdestruction (); // generated by
// Implementation
}
Do not focus on these names. The functions merge mstaticinitialization () and merge mstaticdestruction () are generally more vague names or even inline functions (these functions are not found in your obj file at this time ). The main point is: if a C ++ compiler uses this method to initialize and analyze static objects, unless main () is written in C ++, these objects will never be initialized or destructed. Because this method of initialization and Analysis of static objects is so common, as long as any part of the program is written in C ++, you should use C ++ to write the main () function.
Sometimes it seems more meaningful to use C to write main ()-for example, the majority of programs are C, and the C ++ part is only a support library. However, this c ++ library may contain static objects (even if it does not exist, it may be later-see item M32). Therefore, it is still a good idea to write main () in C ++. This does not mean that you need to rewrite your C code. You only need to change the name of main () written in C to realmain (), and then use main () of C ++ to 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 );
}
In doing so, it is best to add comments to explain the reasons.
If you cannot use C ++ to write main (), you will be in trouble, because there is no other way to ensure that the construction and destructor of static objects are called. This is not to say that it is not saved, but it is more troublesome to handle it. Compiler manufacturers know this problem and almost all provide an additional system to start the static initialization and static analysis processes. You need to know how your compiler is implemented, explore its random documentation or contact the producer.
* Dynamic memory allocation
Dynamic memory allocation is now mentioned. The traffic rule is simple: C ++ uses new and delete (see item M8), and C uses malloc (or its deformation) and free. As long as the new memory is released using Delete and the memory allocated by malloc is released using free, there is no problem. Use free to release memory allocated by new or delete to release memory allocated by malloc. The action is not defined. The only thing to remember is to strictly isolate your new and delete from mallco and free.
Easier to say than to do. Take a look at this rough (but very convenient) strdup function, which is not in the C and C ++ standard (Runtime Library), but is very common:
Char * strdup (const char * PS); // return a copy of
// String pointed to by PS
To avoid Memory leakage, strdup calls must release the memory allocated in strdup. But is the memory so released? Delete? Use free? If the strdup you call comes from the C function library, it is the latter. If it is written in C ++, I am afraid it is the former. The operations required after strdup is called are different in different operating systems and different compilers. To reduce this portability problem, try to avoid calling functions that are neither in the standard Runtime Library (see item e49 and item m35) nor fixed forms (in most computer platforms.
* Compatibility of data structures
The last problem is to transmit data between C ++ and C. It is impossible for C functions to understand the features of C ++. Their interactions must be limited to the concept that C can represent. Therefore, it is clear that there is no portable Method for passing objects or passing pointers to member functions to the functions written by C. However, C understands common pointers, so I want your c ++ and C compilers to produce compatible output, functions in the two languages can safely exchange pointers to objects and to non-member or static member functions. Naturally, the structure and built-in types (such as int and char) variables can also pass freely.
Because the struct rules in C ++ are compatible with the rules in C, it is safe to say that "the same structure defined under the two compilers will be processed in the same way. Such a structure can be passed back and forth securely in C ++ and C. If you add a non-virtual function in C ++, the memory structure does not change. Therefore, only the non-virtual function structure (or Class) objects are compatible with their twin versions in C (the definition only removes the declarations of these member functions ). Adding a virtual function will end the game because its object will use a different memory structure (see item M24 ). The structure inherited from other structures (or classes) usually changes its memory structure. Therefore, the structure with a base class cannot interact with C functions.
In terms of data structure, the conclusion is: it is safe to transfer data structures between C ++ and C in this way-the same definition is provided for compilation under C ++ and C. Adding non-virtual member functions in C ++ may not affect compatibility, but almost other changes will affect compatibility.
* Conclusion
If you want to mix C ++ and C Programming in the same program, remember the following guiding principles:
* Ensure that the C ++ and C compilers generate compatible OBJ files.
* Declare the function used in both languages as extern 'C '.
* If possible, use C ++ to write main ().
* Delete is used to release the memory allocated by new, and free is used to release the memory allocated by malloc.
* Restrict the content transmitted between the two languages to the range of data structures compiled by C. The C ++ versions of these structures can contain non-virtual member functions.
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.