Summary of mixed programming problems of C and C + +

Keywords: C C++

1. When to mix c and c + + code?

Here are some key points:

  • 1. When compiling main(), you must use the C + + compiler (for example, for static initialization)
  • 2. Your C + + compiler should guide the linking process (for example, so that it can get its special library)
  • 3. Your C and C + + compilers may need to be from the same vendor and have compatible versions (for example, they have the same calling convention)

In addition, you need to read the rest of this section to learn how to make your C functions callable by C + + and / or your C + + functions callable by C.
By the way, there's another way to handle this whole thing: compile all the code (even c-style code) using the c + + compiler. This almost eliminates the need to mix c and c + +, and it makes you more careful (you may find some errors in your c-style code). The disadvantage is that you need to update c-style code in some way, mainly because the c + + compiler is more careful / picky than the c compiler. The point is that cleaning up c-style code may require less work than mixing c and c + +, and as a reward, you can clean up c-style code. Obviously, if you can't change your c-style code (for example, if it comes from a third party), you don't have much choice.

2. How to call C functions from C + +?

Simply declare the c function extern "c" (in your c + + code) and call it (from your c or c + + code). For example:

    // C++ code
    extern "C" void f(int); // one way
    extern "C" {    // another way
        int g(double);
        double h();
    };
    void code(int i, double d)
    {
        f(i);
        int ii = g(d);
        double dd = h();
        // ...
    }

Functions are defined as follows:

 	/* C code: */
    void f(int i)
    {
        /* ... */
    }
    int g(double d)
    {
        /* ... */
    }
    double h()
    {
        /* ... */
    }

Note that C + + type rules are used instead of C rules. Therefore, you cannot use parameters that call the number of errors declared as the extern "C" function. For example:

	// C++ code
    void more_code(int i, double d)
    {
        double dd = h(i,d); // error: unexpected arguments
        // ...
    }

3. How to call C + + functions from C?

Simply declare the C + + function extern "C" (in your C + + code) and call it (from your C or C + + code). For example:

    // C++ code:
    extern "C" void f(int);
    void f(int i)
    {
        // ...
    }

Now f() can be used as follows:

/* C code: */
void f(int);
void cc(int i)
{
    f(i);
    /* ... */
}

Of course, this applies only to non member functions. If you want to call member functions (including virtual functions) from C, you need to provide a simple wrapper. For example:

// C++ code:
class C {
    // ...
    virtual double f(int);
};
extern "C" double call_C_f(C* p, int i) // wrapper function
{
    return p->f(i);
}

Now C::f() can be used as follows:

    /* C code: */
    double call_C_f(struct C* p, int i);
    void ccc(struct C* p, int i)
    {
        double d = call_C_f(p,i);
        /* ... */
    }

If you want to call an overloaded function from C, you must provide a wrapper with a different name for the C code you want to use. For example:

    // C++ code:
    void f(int);
    void f(double);
    extern "C" void f_i(int i) { f(i); }
    extern "C" void f_d(double d) { f(d); }

Now you can use the f() function like this:

 /* C code: */
    void f_i(int);
    void f_d(double);
    void cccc(int i,double d)
    {
        f_i(i);
        f_d(d);
        /* ... */
    }

Note that these techniques can be used to call C + + libraries from C code, even if you cannot (or do not want to) modify C + + header files.

4. How to include standard C header files in C + + code?

To #include a standard header file (for example), you don't have to do anything unusual. For example:

// This is C++ code
#include <cstdio>                // Nothing unusual in #include line
int main()
{
  std::printf("Hello world\n");  // Nothing unusual in the call either
  // ...
}

If you are from C, the std:: part of the std::printf() call may look unusual, but this is the right way to write in C + +.
If you are compiling C code using the C + + compiler, you do not want to adjust all these calls from printf() to std::printf(). Fortunately, in this case, the C code will use the old header file < stdio. H > instead of the new header file, and the magic of namespace will handle everything else:

/* This is C code that I'm compiling using a C++ compiler */
#include <stdio.h>          /* Nothing unusual in #include line */
int main()
{
  printf("Hello world\n");  /* Nothing unusual in the call either */
  // ...
}

Final comment: if you have C header files that do not belong to the standard library, we provide you with some different guidelines. There are two situations: either you The header file cannot be changed , or you can Change header file.

5. How to include non system C header files in C + + code?

If you want to include a C header file that is not provided by the system, you may need to wrap the #include line in the extern "C" {/... /} construct. This tells the C + + compiler that the function declared in the header file is a C function.

// This is C++ code
extern "C" {
  // Get declaration for f(int i, char c, float x)
  #include "my-C-code.h"
}
int main()
{
  f(7, 'x', 3.14);   // Note: nothing unusual in the call
  // ...
}

Note: some different guidelines apply to C header file provided by the system (for example) And you can Changed C header file.

6. How can I modify my own C header files to make it easier to #include them in C + + code?

If the C header file you include is not provided by the system, and if you can change the C header file, you should strongly consider adding extern "C" {...}, so that C + + users can more easily use #include in their c + + code. Since the C compiler cannot understand the extern "C" structure, the lines {and} of extern "C" must be wrapped in #ifdef so that ordinary C compilers will not see them.
Step 1: put the following lines at the top of the c header file (Note: when / only when the compiler is a c + + compiler, the symbol #uu cplusplus is #defined)

#ifdef __cplusplus
extern "C" {
#endif

Step 2: put the following lines at the bottom of the C header file:

#ifdef __cplusplus
}
#endif

Now you can #include your C header file in your C + + code without any extern al "C" nonsense:

// This is C++ code
// Get declaration for f(int i, char c, float x)
#include "my-C-code.h"   // Note: nothing unusual in #include line
int main()
{
  f(7, 'x', 3.14);       // Note: nothing unusual in the call
  // ...
}

Note: some different guidelines apply to C header file provided by the system (for example) And you The C header file cannot be changed.

7. How can C + + code call non system C functions f(int,char,float)?

If you want to call a single c function, and for some reason you do not or do not want to #include the C header file of the function, you can use the C + + code using the extern "C" syntax. Naturally, you need to use a complete function prototype:

extern "C" void f(int i, char c, float x);

A block consisting of several C functions can be grouped by curly braces:

extern "C" {
  void   f(int i, char c, float x);
  int    g(char* s, const char* s2);
  double sqrtOfSumOfSquares(double a, double b);
}

After that, you only need to call the function like calling a C + + function:

int main()
{
  f(7, 'x', 3.14);   // Note: nothing unusual in the call
  // ...
}

8. How does C code call C + + function f(int,char,float)?

f(int,char,float) will be called by the C compiler using the extern "C" construct:

// This is C++ code
// Declare f(int,char,float) using extern "C":
extern "C" void f(int i, char c, float x);
// ...
// Define f(int,char,float) in some C++ module:
void f(int i, char c, float x)
{
  // ...
}

The external "C" line tells the compiler that external information sent to the linker should be decorated with the C calling convention and name (for example, preceded by an underscore). Because C does not support name overloading, you cannot have a C program call multiple overloaded functions at the same time.

9. When using C (c + +) to call C + + (c) code, why does the connector report an error?

If you don't get the correct extern "c", sometimes you get linker errors instead of compiler errors. This is because, unlike the c compiler, the c + + compiler usually "modifies" the function name (for example, supports function overloading).

10. How to pass C + + class objects to C functions, or C functions to C + + class objects?

This is an example (for information about extern "C", see the first two FAQs).
Fred.h:

/* This header can be read by both C and C++ compilers */
#ifndef FRED_H
#define FRED_H
#ifdef __cplusplus
  class Fred {
  public:
    Fred();
    void wilma(int);
  private:
    int a_;
  };
#else
  typedef
    struct Fred
      Fred;
#endif
#ifdef __cplusplus
extern "C" {
#endif
#if defined(__STDC__) || defined(__cplusplus)
  extern void c_function(Fred*);   /* ANSI C prototypes */
  extern Fred* cplusplus_callback_function(Fred*);
#else
  extern void c_function();        /* K&R style */
  extern Fred* cplusplus_callback_function();
#endif
#ifdef __cplusplus
}
#endif
#endif /*FRED_H*/

Fred.cpp:

// This is C++ code
#include "Fred.h"
Fred::Fred() : a_(0) { }
void Fred::wilma(int a) { }
Fred* cplusplus_callback_function(Fred* fred)
{
  fred->wilma(123);
  return fred;
}

main.cpp:

// This is C++ code
#include "Fred.h"
int main()
{
  Fred fred;
  c_function(&fred);
  // ...
}

c-function.c:

/* This is C code */
#include "Fred.h"
void c_function(Fred* fred)
{
  cplusplus_callback_function(fred);
}

Unlike C + + code, C code cannot judge that two pointers point to the same object unless the pointer types are exactly the same. For example, in C + +, it is easy to check whether a Derived named dp points to the same object pointed to by a Base named bp: just say if (dp == bp)... The C + + compiler automatically converts the two pointers to the same type, in this case Base *, and then compares them. According to the implementation details of the C + + compiler, this conversion sometimes changes the bits of the pointer value.

Technical Narrator: most C + + compilers use binary object layouts, resulting in this transformation in multiple inheritance and / or virtual inheritance. However, the C + + language does not impose this object layout, so in principle, conversion can occur even if non virtual single inheritance is used.

Pointers are simple: your C compiler does not know how to convert pointers, so for example, the conversion from Derived * to Base * must be done in code compiled with the C + + compiler, not in code compiled with the C compiler.

Note: you must be very careful when converting both to void *, because this conversion does not allow the C or C + + compiler to make correct pointer adjustment! Even if (b == d) is true, the comparison (x == y) may be false:

void f(Base* b, Derived* d)
{
  if (b == d) {   // Validly compares a Base* to a Derived*
    // ...
  }
  void* x = b;
  void* y = d;
  if (x == y) {   // BAD FORM! DO NOT DO THIS!
    // ...
  }
}

If you really want to use void * pointer, here is a safe method:

void f(Base* b, Derived* d)
{
  void* x = b;
  void* y = static_cast<Base*>(d);  // If conversion is needed, it will happen in the static_cast<>
  if (x == y) {   // Validly compares a Base* to a Derived*
    // ...
  }
}

11. Can my c function directly access the data in C + + class objects?

Sometimes.
You can safely access the data of c + + objects from c functions. If the c + + class:

  • 1. No virtual functions (including inherited virtual functions)
  • 2. Place all its data in the same access level section (private / protected / public)
  • 3. There are no sub objects that completely contain virtual functions

If a c + + class has any base class (or any fully contained sub object has a base class), accessing data is technically non portable, because the class layout under inheritance is not imposed by the language. But in practice, all c + + compilers use the same method: base class objects appear first (in left to right order in the case of multiple inheritance), and then member objects.

In addition, if the class (or any base class) contains any virtual function, almost all c + + compilers put void * at the first virtual function position of the object or at the beginning of the object. Again, this is not required by language, but it is the way "everyone" does it.

If a class has any virtual base class, it is more complex and less portable. A common implementation technique is to make the object finally contain the object of the virtual base class (V) (no matter where V is displayed as the virtual base class in the inheritance hierarchy). The rest of the object appears in the normal order. Each derived class with V as the virtual base class actually has a pointer to the V part of the final object.

Reference catalogue

https://isocpp.org/wiki/faq/mixing-c-and-cpp

Posted by ChrisA on Mon, 25 Oct 2021 01:36:01 -0700