BOOKS i'm reading |
Idioms for using C++ in C programs (Part 1 of 2)Contents
IntroductionUsing C from C++ is trivial since it was one of the design goals of the C++ language. All you have to do is to
conditionally wrap C functions declarations into #ifdef __cplusplus extern "C" { #endif void A(); #ifdef __cplusplus } #endif Doing the reverse is relatively easy if you know the simple guidelines. This tutorial is the first part of a 2 parts serie devoted to the topic. In this tutorial, I will introduce the simple guidelines and pitfalls to avoid when using C++ in C programs. The second part will continue by presenting higher level patterns to leverage the good software architecture patterns that C++ language facilities encourage into C programs.
1. The main module must be in C++A lot of stuff happens before and after the 2. Avoid using strdup()
namespace util { // Variant #1 char *strdup(const char *s, int size) { ++size; char *res = static_cast<char *>(malloc(size)); if(res) { // Use memcpy() as it is faster than strcpy() // since we already have the strlen. memcpy(res,s,size); } return res; } // Variant #2 char *strdup(const char *s) { return strdup(s,strlen(s)); } } I have 2 variants of 3. Do not let C++ exceptions slip out of C++ modules into C modulesC does not understand C++ exceptions. The details of how C++ exceptions are thrown are specific to each implementation of the C++ language. I have not study exactly how it works but the C function will certainly not like the stack unwinding that the C++ called function will have performed. All that to say that if you forget to wrap your C++ entry points with a try/catch block and an exception slips out, the resulting behavior is undefined. At best, the exception will quietly disapear since you did not take the opportunity to catch it (very unlikely, IHMO). A much more likely scenario is that the program will core dump or if you are unlucky, you will get erratic behavior of your program in some other unrelated areas of it. 4. Use the pimpl idiom to carry objects into C structuresThe pimpl was coined several years ago by Jeff Sumner (chief programmer at PeerDirect). It is a popular design idiom in C++ to reduce the header file dependencies. The best coverage of that pattern that I have seen is in the book Exceptionnal C++ from Herb Sutter but for the purpose of this tutorial, briefly the pimpl pattern is that the only data member of class is a pointer of a forward declared class: class A { private: class CImpl; CImpl *m_pImpl; }; In the cpp file containing the class A definition, you can include all the header files needed for class CImpl and declare CImpl with all the data members that you would have placed into class A without the pimpl idiom. What this technique buys you is that all the users of class A will not need to include all the header files that you included for the class CImpl. If you want to store C++ objects in a C structure, the pimpl idiom is the only way to do it. Since I am not sure that C support forward declarations or at least not as well as in C++, I suggest using a void pointer as the implementation pointer and typecast it back to the correct type when you need to use it. Here is how you could do it: sType.h: #ifdef __cplusplus extern "C" { #endif typedef struct { void *m; /* Must be type less as you cannot include * a C++ template class declaration in C modules */ } sType; /* C style constructor/destructor */ sType *create_sType(); void destroy_sType(sType *); void use_sType(sType *); #ifdef __cplusplus } #endif sType.cpp: inline std::map<const char*, int> *getMap(sType *s) { return static_cast<std::map<const char*, int> *>(s->m); } // You must wrap your C functions entry points to be sure that // C++ exceptions do not cross into C modules. sType *create_sType() { try { sType *res = new sType; res->m = new std::map<const char*, int>; return res; } catch(...) { } return NULL; } void destroy_sType(sType *s) { delete getMap(s); delete s; } /* * For every methods that need to use the map, use the inline function getMap() to retrieve * the map from the sType POD (Plain Old Data). */ void use_sType(sType *s) { } ConclusionBy following these 4 simples guidelines, you will be able to start using C++ from C. Of course, there is more to say on the topic such as patterns in C that when recognized, you can apply systematic transformations in C++ that will optimize the benefits of porting a program from C to C++. This is exactly that topic that is covered in the next part of this serie of tutorials. Also, my blog is probably the best medium if you would like to provide feedback to this tutorial, you can do so here. Bibliography
|