简体   繁体   中英

Build a C program from a C++ program

So, What I want to do: Basically I want to build a C/C++ program (ready to execute) from another c++ program (main program).

Let's take this example: main.cpp

int main()
{
   //do something
   //there I will modify source of program which I want to build 
   sourceCode = ""; //maybe from a file
   //then a function like this:
   buildProgramFromSource(sourceCode, "program.exe");
}

Source of program which I want to generate from main.cpp

int main()
{
    //do another thing
    int value1 = value From main.cpp
    int value2 = value frim main.cpp too
}

So, I have source stored on main.cpp, and I want to modify that source then build that program.

How should I implement this?

Or, maybe there is any other way to generate a .exe program from a main C++ program?

Later edit: Assuming that environment doesn't have any compiler installed. The compiler should be integrated on main program.

In other words I don't want to use functions like system() or shellexecute which are using compilers from environment.

The application will be used under Windows.

An example would be very usefully.

It is operating system specific . And things are different if the second (built) program is a standalone program or a library (or some code loaded by the first).

On Linux (and other Posix systems), you can generate a C or C++ file (by using usual text output functions; I recommend building in memory some AST of the generated C++ file). Then you can run some C++ compiler on it by forking a process (in the simple case, use popen(3) or system(3) , or use the lower level fork(2) & execve(2) , etc....). See Advanced Linux Programming for more.

Then you could dynamically load the obtained shared object with dlopen(3) and retrieve some symbols (function pointers) using dlsym

Linux details

  • compile and link the main program with -rdynamic ie g++ -Wall -g -rdynamic main.cc -o prog ; the -rdynamic flag is needed to make the program functions visible from the loaded code. It is useful to declare the signature of loaded functions and to declare global function pointers, like:

      typedef int somefun_t (const std::string&); somefun_t *globfun; 
  • generate at runtime a C++ file genfoo.cc with functions to be accessed by the main program declared as extern "C" .

  • fork a compilation into a shared object ( shared library ) with position independent code: so run from inside your program: g++ -Wall -fPIC -g -O -shared genfoo.cc -o genfoo.so

  • call dlopen and dlsym from your main program (notice that dlopen do weird things if you pass it a filename without any / ):

      void* hdl = dlopen("./genfoo.so", RTLD_NOW); if (!hdl) { std::cerr << "dlopen:" << dlerror() << std::endl; exit(EXIT_FAILURE); }; globfun = dlsym(hdl, "func"); if (!globfun) { std::cerr << "dlsym func:" << dlerror() << std::endl; exit(EXIT_FAILURE); }; 
  • use the function pointer globfun as you need, eg

     std::string s; int r = (*globfun) (s); 
  • if you are serious you should dlclose(hdl) before exiting.

If you just want to compile and run a different program things are much simpler: just compile the generated program as usual ( gcc -Wall -g genfoo.cc -o genprog ) and execute it as usual ( system , fork + execve , ....)

If the target system does not have any C compiler (but consider also tinycc which is able to compile from a string -containing C code- in memory with its libtcc ), you cannot do that. You need to use some JIT compilation library like libjit , GNU lightning , asmjit . .... Alternatively consider embedding some scripting interpreter like Lua or Guile

Windows details

I don't know Windows, but it is rumored to have equivalent dynamic loading functionality, eg LoadLibrary . If you want to avoid running a C compiler at runtime, you'll also need some JIT library. Details are different ( dllexport )

Some frameworks or libraries ( Poco , Qt , Boost ....) are providing system-neutral wrappers for these dynamic loading features.


NB: On Linux you can practically do a lot of dlopen (hundreds of thousands), see manydl.c . My MELT system (a domain specific language to extend GCC ) is generating C++ code and dlopen -ing it on the fly.

Notice also that some programming languages, like Common Lisp (and also Meta-Ocaml ), are much more suited for runtime evaluation & meta-programming ( multi-staged ). Notably its SBCL implementation of Common Lisp is able to compile arbitrary expressions then run them at runtime! See also J.Pitrat's blog .

The easiest solution would likely be including a standalone compiler in your program's files that are distributed. When the program needs to be recompiled:

  1. Create another copy of the original main.cpp source on the system
    • Something like: system("copy main.cpp new_main.cpp"); on windows
  2. Open a stream to new_main.cpp (assuming your code named the new copy new_main.cpp )
  3. Append your additional code to the end/beginning of the file
  4. Flush and close the output stream
  5. Call your compiler and pass it the path to the new file
    • If you're using g++, for example, you would write something like: g++ new_main.cpp -o new_main.exe
    • If you use the Microsoft Visual C++ Compiler, you can access it by running system("vcvarsall"); (found in C:\\Program Files (x86)\\Microsoft Visual Studio 11.0\\VC for me) followed by system("cl 'path/to/project/new_main.cpp'); (found in C:\\Program Files (x86)\\Microsoft Visual Studio 11.0\\VC\\bin for me).

After this you have new_main.exe which is the compiled form of main.cpp + some extra code.

Warning: This option will require that you either install the compiler or include its necessary files if you distribute the application. You do not need to install/include them with new_main.exe , but you will need to install/include them with the program that compiled new_main.exe .


If you want to compile from memory, the process is similar:

  1. Open a stream to main.cpp
  2. Read main.cpp to a char buffer (assuming main.cpp only contains ASCII characters)
  3. Close the stream to main.cpp
  4. Append your code to the start/end of the buffer ( make sure you have room in the buffer! )
  5. Call your compiler library
    • This call is dependent on your compiler library, but similar to: compileCode(String_Version_Of_Code_Read_From_File);

As an extra bit of information, if you are in a situation where you need to to dynamically modify and build a program, there's typically better solution. This is not always the case, but usually there is a better approach.

A third option could be writing your own compiler, but I would highly advise against that.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM