A Guide to C++ and C Interoperability

orting C code to C++ is no big deal: change the source file’s extension from .c to .cpp and recompile. By contrast, porting C++ source code and binaries to C requires more elbow grease and midnight oil. The following sections show how the two languages can share a single declaration of a class with member functions and other C++ goodies. Then you’ll see how C apps can utilize state-of-the-art C++ algorithms and containers with a little help from the linker.


You have a class that declares member functions but you want to use it in a C app as well. Additionally, you want to simplify the implementation of certain tasks in your C app by utilizing C++ algorithms and containers. How do you accomplish these goals?


Use conditional compilation to hide C++ features from a C compiler and wrap compiled C++ code in a C-callable function.

UDT Sharing
The simplest form of sharing a declaration of a user-defined type (UDT) between C and C++ is sticking to the lowest common denominator of C. The following C struct can be used as-is in a C++ program:

typedef struct String{ char * pstr; int size;} String;

C doesn’t recognize the keyword class so you must stick to struct. In addition, using the same token for the tag (the name before the {) and the typedef name (the name following the }) guarantees that the following code will compile successfully in both languages:

//works both in C and C++char buff[100];String mystr; //no 'struct' before Stringmystr.pstr=&buff[0];mystr.size=sizeof(buff);

And yet, C++ users would expect to find member functions, conversion operators, and overloaded operators in String. The fact that these C++ features will not be usable in C is no excuse for depriving C++ users of these conveniences. Your goal is to have a single declaration of String that will be shared both by C and C++ users, without sacrificing essential C++ features. How do you accomplish this feat?

A C++ Overhaul
All data members in String must remain implicitly public. protected and private are out of the question as they might break binary compatibility between C and C++. Additionally, all C++ features must appear inside an #ifdef-endif block like this:

typedef struct String{>#ifdef __cplusplus //anything you put here will be visible only from C++#endif char * pstr; int size;} String;

Here’s the augmented String struct:

typedef struct String{#ifdef __cplusplus char * getpstr() {return pstr;} void init(int sz=0) {pstr=sz? new char[sz]:0;                       size=sz;} operator const char* () const {return pstr;} int getsize() const {return size;} char& operator[] (int id) {return pstr[id];} const char& operator[] (int id) const {return pstr[id];}#endif char * pstr; int size;} String;

C++ users can now use String as an ordinary C++ class with member functions, overloaded operators and conversion operators:

String s={0}; //zero initialize all data memberss.init(10);s[5]='a';cout<

C users on other hand will keep using String like this:

#include String s1;s1.pstr=(char *) malloc(10);s1.size=10;printf("%s
", s1.pstr);printf("%d
", s1.size);free(s.pstr);

Unbelievable as it may seem, the two code listings above use the same String type.

ABI Issues
You probably noticed that String doesn't define a constructor, a destructor or an assignment operator. This wasn't an oversight. In spite of its C++ interface, String still remains a POD type, which means that its binary layout is identical in C and C++. Indeed, you're limited with respect to the C++ features that you may add to String but you still have enough leeway to make C++ users content. To summarize, adding any of the following features to String will make it a non-POD type:

  • virtual functions
  • user-defined destructor
  • user-defined assignment operator
  • user-defined copy constructor
  • user-defined constructor
  • reference members
  • data members that are pointers to members
  • base class(es)
  • non-POD member objects

Using C++ Functionality in C
Thus far, this article has focused on the source code level of sharing declarations between the two languages. You can also utilize compiled C++ functionality in C by wrapping C++ code in an intermediary layer. Here's an example.

Suppose you're maintaining a C application that reads char* strings and stores them as char**. Your task is to sort these strings alphabetically and print them onscreen. Instead of wading through strcmp(), qsort() and pointer mayhem, you want an elegant, STL-based solution. It's doable.

First, declare an intermediary function as extern "C". This function will be compiled as a C++ function but it's callable from C, too. C doesn't recognize the extern "C" linkage specification. Therefore, use #ifdef-#endif to hide this part from the C compiler:

// func.h#ifdef __cplusplus extern "C" #endifvoid func (char **s, int n);// end of func.h

func() is implemented in a .cpp source file that's compiled separately. Here's a typical implementation that uses all sorts of STL conveniences:

//func.cpp#include #include #include #include #include extern "C" void func (char **s, int n){ vector vs; for (int i=0; i(cout,"
"));}

Compile func() as usual. Next, add its declaration to your C application:

//main.c#include "func.h"int main(){ char *strings[4]={{"bcd"},{"abc"},{"efg"},{"aaa"}}; func(strings, 4);}

A C compiler doesn't care how func() is implemented; it only inserts a call to a function with this name into main.obj. It's the linker's job to resolve the call to the same function defined in func.obj. Since the linker sees only the .obj files, the extern "C" linkage specification is mandatory. It guarantees that both C and C++ generate the same symbol for func(). Without extern "C", you'll get a linkage error.

Exceptions
Can func() use any C++ feature? Yes, almost. Virtually all implementations nowadays use a single runtime library for C and C++ so yanking compiled C++ code into C should "just work". There's one caveat, though: C functions do not propagate C++ exceptions. If func() throws an exception, your application will behave unpredictably. To avoid this, func() must handle all exceptions, if any, locally.

Share the Post:
Share on facebook
Share on twitter
Share on linkedin

Overview

The Latest

Top 5 B2B SaaS Marketing Agencies for 2023

In recent years, the software-as-a-service (SaaS) sector has experienced exponential growth as more and more companies choose cloud-based solutions. Any SaaS company hoping to stay ahead of the curve in this quickly changing industry needs to invest in effective marketing. So selecting the best marketing agency can mean the difference

technology leadership

Why the World Needs More Technology Leadership

As a fact, technology has touched every single aspect of our lives. And there are some technology giants in today’s world which have been frequently opined to have a strong influence on recent overall technological influence. Moreover, those tech giants have popular technology leaders leading the companies toward achieving greatness.

iOS app development

The Future of iOS App Development: Trends to Watch

When it launched in 2008, the Apple App Store only had 500 apps available. By the first quarter of 2022, the store had about 2.18 million iOS-exclusive apps. Average monthly app releases for the platform reached 34,000 in the first half of 2022, indicating rapid growth in iOS app development.