Home
Fractals
Tutorials
Books
My blog
My LinkedIn Profile

BOOKS i'm reading

Napoleon Hill Keys to Success: The 17 Principles of Personal Achievement, Napoleon Hill, ISBN: 978-0452272811
The 4-Hour Workweek: Escape 9-5, Live Anywhere, and Join the New Rich (Expanded and Updated), Timothy Ferriss, ISBN: 978-0307465351
The Fountainhead, Ayn Rand, ISBN: 0452273331

Idioms for using C++ in C programs (Part 1 of 2)

Olivier Langlois, IT superstar coach, Dominate LinkedIn Formula author
by Olivier Langlois

Contents





Introduction

Using 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 extern "C" blocks like this:

#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 main() function in a C++ programs such as initializing the global C++ objects. It is imperative to minimally have the main() function written in C++ for a proper initialization of the C++ run-time environment.

2. Avoid using strdup()

strdup() is not part of the standard C and C++ libraries so there is no guarantee on how the memory to store the returned string will be allocated. It could be with malloc() or it could be with new[] depending if it is called from a C or a C++ module. Fortunately, it is very easy to redefine 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 strdup() since I have found out that very often the string length is known at the calling site so I save myself from recalculating it again.

3. Do not let C++ exceptions slip out of C++ modules into C modules

C 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 structures

The 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)
{
}

Conclusion

By 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

History

  • 09-09-2007:
    • Original article.


Back to the tutorials list

Home :: Fractals :: Tutorials :: Books :: My blog :: My LinkedIn Profile :: Contact