0

I'm a little new to C++ and I have a question regarding converting C code into C++ code, as well as mixing C and C++ code.

For example, I'm refactoring a previous C file to a C++ file because I am now required to use a std::string in the header file's struct. It now looks as follows (firstClass.hpp):

struct firstClass
{
              .
              .
              .

    std::string test_string;

    firstClass();
    firstClass(const firstClass &c);
    ~firstClass();
};

And as a result, here's first-class.cpp:

#include "firstClass.hpp"

extern "C"
{
              .
              .
              .

     #include <errno.h>
     #include <assert.h>
     #include <stdlib.h>
     #include <string.h>
}

              .
              .
              .

Now here's my question: I have other files that previously included firstClass.h (Note: this is the C-variant) before I converted it to C++ code -- do these files need to also be converted to C++ code? Also, if additional files include these files mentioned above will they need to be converted as well? I guess to sum up my question: after converting this initial file to C++, how far down the chain of includes do I need to also convert those files?

Damian
  • 159
  • 15
  • 4
    The use of `extern "C"` is wrong. Now, your question, it's a definite "yes!", but you could try it yourself. You would probably get inconsistent data and/or linker errors. BTW: I'd suggest you first try to compile all code as C++. Then, start using C++-specific features. Apart from a few casts, C can in many places be compiled without change. – Ulrich Eckhardt Feb 21 '20 at 15:42
  • 1
    Take a look at what extern "C" does... i dont think it does what you think it does (it lets C++ functions be used in C code) – Object object Feb 21 '20 at 15:52
  • @UlrichEckhardt thanks so much for the feedback. From my research online I thought extern "C" was used to surround my C includes so it was usable by C++, but is this not correct? – Damian Feb 21 '20 at 15:59
  • 3
    Side note: If you need a destructor and a copy constructor, you should finish off [the Rule of Three](https://stackoverflow.com/questions/4172722/what-is-the-rule-of-three) with an assignment operator. – user4581301 Feb 21 '20 at 15:59
  • 1
    Headers are rarely, if ever, designed so that they will work when wrapped with `extern "C"`. For now, forget that it exists. – Pete Becker Feb 21 '20 at 16:04
  • 2
    The headers shown in your question aren't "your" C includes. They're **C standard library** headers and don't need `extern "C"` to work with your C++ code. – Blastfurnace Feb 21 '20 at 16:06
  • @Blastfurnace That makes *a lot* of sense, and explains a couple of my errors. Thanks! – Damian Feb 21 '20 at 16:11
  • @Damian -- Note that if you got a third-party library that was written in `C`, you probably would need to use `extern C` for those particular functions in the library. – PaulMcKenzie Feb 21 '20 at 16:28
  • 1
    @user4581301: Though in all likelihood, they probably just want [the Rule of Zero](https://en.cppreference.com/w/cpp/language/rule_of_three#Rule_of_zero); the only member they show is a `std::string`, and for simple data storage cases, the rule of zero (supported by `std::unique_ptr` and the like if needed) is almost always what you want. – ShadowRanger Feb 21 '20 at 16:31
  • @ShadowRanger Can't argue with that. Well, I could, but I'd lose. – user4581301 Feb 21 '20 at 18:09

2 Answers2

1

Most C++ compilers will process both C source code files, files with a .c extension, and C++ source code files, files with a .cpp extension. So the first step in a conversion would be to change to a C++ compiler and get your project files and/or make files in order so that it all compiles.

The next thing is to go through the source code base to determine what data structures are going to change and which include files, normally files with a .h extension, have these data structures that will need to change.

At this point you will need to begin partitioning out what source is C++ and what source is C. For instance while C struct is a subset of the C++ struct, C++ struct allows for constructors and deconstructors which C does not allow. This means you can use a C struct with C++ source but you can not use a C++ struct with the C++ features in a C source file.

Other C++ language keywords such as class just won't work period with a C source code file.

Next is to look at what C++ features, such as templates and C++ Standard Library functionality such as std::string, you are going to use. Again you will need to partition off the C++ source from the C.

The extern "C" functionality allows you to use C functions with C++ source code by declaring a function or variable name as being C rather than C++. This is necessary because the C++ compiler does name mangling, using an algorithm to generate a modified name for functions allowing function overloading. See What is name mangling, and how does it work?

The other thing you will be faced with is that C++ has introduced new versions of most of the standard C include files without the .h extension. You can use the old version with C++ but the new versions are preferred since they are for C++. However C source files can only use the old, .h extension, version of these files.

Conditional compilation for header files

The magic that allows the use of the older standard C include files with C++ source code is that most compilers have a special #define which can be used to do conditional compilation. With the Visual Studio compiler the special define is __cplusplus and it can be used something like:

#if defined(__cplusplus)
extern "C" {
#endif

// The type CONNENGINEHANDLE is for future expansion to allow multiple
// sockets to be managed by the dll.
typedef unsigned short CONNENGINEHANDLE;

// following functions are used with a server implementation.
// these functions will setup the parameters for the server and
// start the listen needed to accept a connection from a client
CONNENGINE_API int fnConnEngineSetDomainNamePort(CONNENGINEHANDLE hConnEngineSocket, char *aszDomainName, int nPortNo);
CONNENGINE_API int fnConnEngineStartEngine (int nPort, HWND hWinHandle, UINT wReceiveMsgId);
CONNENGINE_API int fnConnEngineStopEngine ();

#if defined(__cplusplus)
};
#endif

What the above preprocessor code that is part of an include file does is to allow a C++ source code file to include the same header file as C source code files but when it is included in the C++ file, the extern "C" { is part of the header file text because the Preprocessor has the __cplusplus defined. So the C++ source is able to use functions defined in the C source code and the name mangling ordinarily done with C++ is turned off for those functions whose declarations are within the braces.

However though this special #define to detect if a C++ source file is being processed by the Preprocessor you still can't use C++ language constructs in C source files. You will still need to partition out the C++ from the C.

Interface functions for C calling C++ functionality

In some cases you may need to execute C++ source code functionality from C source code functionality. In those cases you will need to create a C compatible interface to the C++ functionality.

For instance if you have a struct that contains a std::string, which is incompatible with C source code, you can create the C++ source code to hide the implementation details and to then provide a C compatible interface.

For instance from the include file example showing the use of __cplusplus above, one of those functions is defined in a C++ source code file as follows. This C++ source code file also include the same header file as would be used with a C source code file and with the declaration above for the function fnConnEngineStartEngine(), the C++ compiler knows it is not supposed to name mangle the function name fnConnEngineStartEngine{}. This C++ source code provides an interface between C source that uses the C++ object theApp.

CONNENGINE_API int fnConnEngineStartEngine (int nPort, HWND hWinHandle, UINT wReceiveMsgId)
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState());
    if (hWinHandle == 0)
        hWinHandle = theApp.m_hWinHandle;

    if (wReceiveMsgId == 0)
        wReceiveMsgId = theApp.m_wReceiveMsgId;

    theApp.StartEngineAsServer (nPort, hWinHandle, wReceiveMsgId);
    return 0;
}
Richard Chambers
  • 16,643
  • 4
  • 81
  • 106
0

What you want to do is use a C++ compiler on the C code. The C++ compiler will process the C code just fine and handle the C++ syntax.

Not sure what compiler or IDE you're using. This can be done for many compilers by adding an argument on the command line or to build options. Or by an option in the compiler settings for an IDE.

GlenL
  • 39
  • 7
  • 2
    C++ is not C. The question is about using C++ specific constructs such as `std::string` which are incompatible with a C compiler. – Richard Chambers Feb 21 '20 at 15:58
  • @RichardChambers so due to the addition of this std::string, would all files that include firstClass.hpp (and all subsequent files that in-turn include those files) will need to be also converted? – Damian Feb 21 '20 at 16:06
  • @Damian Yes, neither member functions, nor `std::string`, exist in C. So any code using this `struct` now needs to be compiled as C++. – walnut Feb 21 '20 at 16:11