I have read about several methods to combine C and C++ codes, however, I'm still confused on how to proceed in my case. Here is my problem:
I have a relatively large amount of C code (consisting of various .c
and .h
files) which is used to model solids in finite and discrete elements. This code has relatively short and simple main function with a for
loop in which various other functions (from other files) are called sequentially. This code works fine when compiled in both Unix (icc compiler) and Visual Studio.
I have other code in C++ that solves molecular dynamics interactions. This code also consists of various files and runs fine in both Unix (icpc compiler) and VS. Both are stand alone programs with their own input and output set of files.
What I need to do is to run both programs in such way that my C program "calls" the C++ code in its main loop. Some information needs to be passed both ways between the two codes, which may be in the form of arrays (or pointers).
What is the simplest way to do this?
In particular, I have multiple questions based on recommendations I have read:
extern "C" {}
?extern "C"
in my C functions?extern "C"
in my C++ files? (headers? functions? all of them? or only those I need to call from the C program?)main
functions. Can I simply rename my C++ main
function?main
function from C to C++?main
in C++ and have it "called" by my C code; fifth, implement the transfer of information?)Sorry for the long text and multiple questions. I am relatively new to C and even newer to C++, so even my vocabulary on these programs is limited.
Thanks for the help. Any hints will be appreciated. If you need additional information, please let me know.
Here is the 'main' function of my C code:
#include "Yproto.h"
void show_time_info(YDC ydc,CHR Ystage[3]);
main(argc, argv)
INT argc; char **argv;
{ CHR c1name[300]; /* name of the problem i.e. input file */
struct YD_struct yd; /* Y database */
YDC ydc=&(yd.ydc); /* Y control database */
YDE yde=&(yd.yde); /* Y element database */
YDI ydi=&(yd.ydi); /* Y interaction database */
YDN ydn=&(yd.ydn); /* Y node database */
YDB ydb=&(yd.ydb); /* Y borehole database */
YDS yds=&(yd.yds); /* Y source (inter. fluid) database */
YDO ydo=&(yd.ydo); /* Y output database */
YDPE ydpe=&(yd.ydpe); /* Y property database for elements */
YDPN ydpn=&(yd.ydpn); /* Y property database for nodes (BC) */
YDPJ ydpj=&(yd.ydpj); /* Y property database for joints */
YDPM ydpm=&(yd.ydpm); /* Y property database for meshing */
INT Tctrlc, itimes=0;
CHR *p=NULL;
/* get name of the problem */
if(argv[1]!=NULL)
{ CHRcpy(c1name,argv[1]);
}
else
{ CHRwcr(stdout);
CHRw(stdout," please define input file names: "); CHRwcr(stdout);
CHRw(stdout," >");
fgets(c1name,sizeof(c1name),stdin);
if((p=strrchr(c1name,'\n'))!=NULL) *p = '\0';
}
strcpy(ydc->cfiname, c1name); ydc->cfiname[255]='\0';
ydc->finp=FILENULL; ydc->fcheck=FILENULL;
/* Process while any input */
while(Yrd(c1name,&yd)>0)
{ itimes=itimes+1;
CHRw(stdout,"NEW INPUT: "); CHRw(stdout, c1name); CHRwcr(stdout);
if(Ycheck(&yd)<0) break; date_and_time(ydc->cruntime); timestamp();
CHRw(stdout, "Start calculating ...\n");
omp_set_num_threads(8);
for(ydc->ncstep=ydc->ncstep;ydc->ncstep<ydc->mcstep;ydc->ncstep++)
{ show_time_info(ydc,"Ymd"); /* show time information */
Ymd(ydc,yde,ydi,ydn,ydpe,ydpn,ydpm); /* mesh elements */
/********** HERE IS WHERE I WOULD LIKE TO CALL MY C++ PROGRAM ***************/
Yfd(ydc,yde,ydn,ydi,ydo,ydpe,ydpn,ydpj); /* nodal forces */
Ybor(ydc,yde,ydn,ydb,yds,ydpe,ydpj,ydpn); /* borholes, inter. fluid */
Ycd(ydc,yde,ydi,ydn,ydpe,ydpn); /* contact detection */
Yid(ydc,yde,ydi,ydn,ydo,ydpe,ydpn, ydpj,ydpm); /* interaction */
Yod(c1name,&yd); /* output results */
Ysd(ydc,yde,ydn,ydo,ydpe,ydpn ); /* solve equations */
Yfrd(ydc,yde,ydi,ydn,ydpe,ydpn,ydpj,ydpm); /* fracture */
ydc->dctime=ydc->dctime+ydc->dcstec; /* update time */
/* CTRL-C Interruption */
Tctrlc = enablc(ydc->dctime, ydc->ncstep, ydc->mcstep);
if(Tctrlc!=1) break;
}
}
/* Termination */
CHRw(stderr," ***** Y HAS ORDERLY FINISHED *****"); CHRwcr(stderr);
CHRw(stderr,"Press a key to continue"); CHRwcr(stderr);
getchar();
}
UPDATE 24 HOURS AFTER ANSWERS
I followed the recommendations as per provided answers and the solution to my problem turned out to be much simpler than originally thought (although I did have to explore several options before getting it working). The best part is that it works in both Unix and Visual Studio. Here is the summary of steps I took:
Convert my main C file to C++. For this, rename the file containing the main
function of my C code with a .cpp extension (changed from Y.c to Y.cpp) and change the beginning of the main
function from:
main(argc, argv)
INT argc; char **argv;
to
int main(int argc,char **argv)
in order to make it C++ 'friendly'. (Note: I understand renaming the file to .cpp is not essential, but I think it's better to do it for clarity).
Wrap all my C header files with
#ifdef __cplusplus
extern "C" {
#endif
at the beginning, and
#ifdef __cplusplus
}
#endif
at the end.
Change the name of my main
C++ function and (temporarily) use no arguments. I named it int Ynano()
.
Create a new header file called Y_NANO.h (Y_NANO.cpp is the name of the file containing the originally main C++ function) with the line:
int Ynano();
Include the new header in both Y.cpp and Y_NANO.cpp:
#include "Y_NANO.h"
Call the function Ynano()
from the main
function in Y.cpp.
To compile in Visual Studio, simply put all the source files in the same folder and create a new project. In Unix, I followed the steps given here.
These steps will only make the programs run together, with no transfer of information between them. To transfer information between the programs, it is necessary to include some parameters as arguments of Ynano()
, but that's another story.
Some final comments:
The C++ language provides mechanisms for mixing code that is compiled by compatible C and C++ compilers in the same program. You can experience varying degrees of success as you port such code to different platforms and compilers.
1) Should I wrap my C header files with
extern "C" {}
?2) Should I use
extern "C"
in my C functions?
Only if you plan to #include
the C headers from some C++ source files, i.e., if you want to call one of the C functions from your C++ code. The typical way to make a C header file usable in C++, is like this:
#ifndef MY_C_HEADER_H
#define MY_C_HEADER_H
#ifdef __cplusplus
extern "C" {
#endif
/* All the original content of the C header */
#ifdef __cplusplus
}
#endif
#endif
If you don't want to modify the header, it is also possible to simply apply the extern "C"
from the outside of the header when including it in a C++ source file:
// in my_source.cpp (or some C++ header file):
extern "C" {
#include "my_c_header.h"
}
NOTE: Doing that solution is not at all recommended nor is it a long-term / maintainable solution, it is just a quick-and-dirty "just make it work" solution that often fails, but sometimes works, depending on how the C headers look like (C headers don't need to include many other headers, and shouldn't in general, but some authors don't have the common sense to do so).
The reason for extern "C"
is to disable C++ name-mangling, i.e., tell the compiler that the functions should be compiled to correspond to un-mangled symbols and/or should be looked up in the symbol table un-mangled (when linking to them). So, the rule is simple, any C++ functions that you want to compile into a library that would be callable from C code (or any other language for that matter) needs to be declared as extern "C"
. And any function declarations that you which to call in C++ code but links to a library compiled from C (or any other language) must be extern "C"
as well.
3) Or should I use extern "C" in my C++ files? (headers? functions? all of them? or only those I need to call from the C program?)
If you want to call some C++ functions from your C code, then those specific functions will have to be declared as extern "C"
when compiling that C++ code. In the C header file that declares those functions (for the purpose of calling them from the C code), there is no need for extern "C"
(it is always implied in C).
4) In understand I can not have two 'main' functions. Can I simply rename my C++ 'main' function?
What would be the purpose of two main functions? It is not allowed and not useful. You can still just have one "program" with one start and one end, i.e., one main function. You'll have to pick one of the main functions, and add to it whatever extra steps you want (calling the other library). In other words, you have to "merge" the main functions.
5) When compiling in unix, should I use both C (icc) and C++ (icpc) compilers for different files? or just the C++ compiler?
You use the C compiler to compile the C code and a C++ compiler to compile the C++ code. Most build systems (cmake, make, etc.) will automatically do that anyways. Technically, you could try and compile the C code using the C++ compiler, but don't expect it to work right away or even be easy at all to get it working, not worth the effort IMHO.
6) Could it be an option (to simplify things) to convert my main function from C to C++?
That is an option. You source file containing the C main function seems relatively simple, it includes one C header file and has a fairly simple main function. If so, this won't be hard to get to compile on a C++ compiler (unless the C header that it includes is a lot of other C headers, which is bad practice, but likely). You will need to wrap the C header file inclusion with the extern "C" { }
as shown above. Then, you can just try to compile it (only the source file containing the main function) in a C++ compiler, and the rest of the C code with the C compiler, and then link the whole thing together. If that works right away, then great, you can start to merge that C main function with the C++ main function from the other library, and you will be good to go.
Otherwise, the usual option is to figure out what you need the C++ code to do. Then, create a C-friendly function (no classes, etc.) in C++ that does these things, using the C++ library. Then, create a header file that declares that function, with the extern "C"
specifier (when compiling under C++ only (__cplusplus
)), and make sure that this header does not include any other C++ header (not standard headers, not any other headers from the C++ library). And finally, in your C source code, where you have the main function, include that header and call that function(s) from where you need it in the main function. Link the whole thing together and it should work.
7) If I don't need to pass information of classes between the two programs, do I need to do anything about them?
No. As long as you don't include any C++ header from the C code (which the compiler won't accept anyways), the C code is not aware that classes even exist. So, there is no danger here.
8) In what order do you suggest this problem to be tackled? (e.g. first have my C program compiled by C++ compiler; second, compile both codes together with no links; third, link the codes; fourth, rename main in C++ and have it "called" by my C code; fifth, implement the transfer of information?)
First step is, of course, to make sure you can compile both separately. Second step is to see if it is possible to compile the C program's main function (only the main function) with a C++ compiler (as explained above). If successful, start incorporating elements from the C++ main function into that new "merged" main function. If unsuccessful, follow the steps that I just mentioned.
9) Finally, there are some macros in each program, which are repeated (same name, same implementation). Is there a conflict with this? Should I only keep one set of macros?
MACROs... that's hard to tell. If you follow the procedure of creating a C++ function that can be called from the C main function, then you essentially have a perfect isolation of both libraries, i.e., they are compiled separately and linked together after. In that case, there will be no problem with conflicting MACROs (but there might be with functions of the same name, if some are extern "C"
in the C++ library). If you try to merge the main functions into one C++ main function, you might have some problems with conflicting MACROs between the C headers and C++ headers which will be included together.
I think you can just compile your C main()
in a separate CPP compilation unit and then "extern C" all your C function definitions. Calling from CPP to C is easy. The other way around is a little more cumbersome as you will have to create "...a C API for exposing the functionality of your C++ code..." - see How to call C++ function from C?
EDIT: Above edited thanks to feedback from Mikael (see comments). Looking at it, I think C++ to C is still generally easier if the C++ code takes advantage of C++ specific features like objects overloading, etc as then it may need C API wappers (see above link). In this case, as Mikael points out, it isn't really the case so either way is as easy / hard...
Note: Combine both your main()
's into the one CPP function.
run both programs in such way that my C program "calls" the C++ code
That's usually a little difficult I'm afraid. C++ does something called name mangling, so calling a C++ function from C is difficult to do in a portable fashion, unless you've made a C wrapper (see above link). The reason being is that the CPP compiler (internally without you seeing this) re-writes the function names and includes such things as the parameter types, usually as a suffix to the name, so that it can do things like function overloading. The C compiler doesn't do this because function overloading isn't possible in C.
I'd say it would be better to run your main from a C++ module and call your C functions from there... that way you get around the name mangling issue.
Should I wrap my C header files with extern "C" {}
Yes, it's important in the C header files to wrap all function definitions with this. Normally you'll see something like
#ifndef HEADER_FILE_NAME
#define HEADER_FILE_NAME
#ifdef __cplusplus
extern "C" {
#endif
/// FILE CONTENTS
#ifdef _cplusplus
}
#endif
#endif // HEADER_FILE_NAME
What this does is tell the CPP compiler that these function names should not be mangled. This way the correct symbol names will be used when linking with the C functions.
When compiling a CPP module __cplusplus
should be defined but when compiling a C module it should not. This means that when the CPP module includes your C header file, it will not mangle the function names and can therefore call the C functions correctly.
Or should I use extern "C" in my C++ files?
The extern "C" just tells the compiler that the function has C language linkage so the generated symbol won't be mangled. So I think, if it is a function that has not been overloaded then doing this (in the H file for the function definition) will prevent the function name being mangles so you'll be able to call it from C. However, if you extern "C" a class for example, that will still have C++ linkage, same for class member functions etc... depends if you use these or not... doesn't look like it from your code sample.
In understand I can not have two main functions. Can I simply rename my C++ main function? Could it be an option (to simplify things) to convert my main function from C to C++?
Yes, I think this is the best option. If only the main()
function needs to call both types of code then you're fine. The one main()
function written in a CPP compilation unit.
If however there is a C module that needs to call a C++ module then you will need to consider compiling that as a CPP file or making sure that the CPP function is extern "C"
and isn't overloaded.
Finally, there are some macros in each program, which are repeated (same name, same implementation). Is there a conflict with this? Should I only keep one set of macros?
If the macros are defined in C/CPP files then you're OK. If they are in header files then there could be a conflict if one file includes two header files which both contain the same macro. In either case i would recommend taking all common macros out into shared header files so that there is only one instance of the macro... much more maintainable... go with the "do not repeat yourself" mantra :)
I haven't addressed all your points, but hope this is enough to get you started :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With