2012-06-13 6 views
40

C++ è un linguaggio compilato statica, i modelli sono risolti durante la fase di compilazione e così via ...È possibile creare una funzione dinamicamente, durante il runtime in C++?

Ma è possibile creare una funzione durante il runtime, che non è descritto nel codice sorgente e non è stato convertito per automatizzare il linguaggio durante la compilazione, in modo che un utente possa lanciare dati che non sono stati previsti nella fonte?

Sono consapevole che questo non può accadere in modo semplice, ma sicuramente deve essere possibile, ci sono un sacco di linguaggi di programmazione che non sono compilati e creano quel tipo di cose dinamicamente che sono implementate in C o C++.

Forse se vengono create fabbriche per tutti i tipi primitivi, insieme a strutture dati adeguate per organizzarle in oggetti più complessi come tipi e funzioni utente, ciò è possibile?

Tutte le informazioni sull'argomento e le indicazioni sul materiale online sono ben accette. Grazie!

EDIT: mi rendo conto che è possibile, è più simile Sono interessato a nell'attuazione dettagli :)

+1

Può fare un esempio di ciò che si si aspetterebbe? –

+0

I computers sono spesso scritti in C++. Molto di .NET è scritto in C++. La risposta è si. –

+0

scrivere un interprete è in realtà piuttosto semplice ... –

risposta

1

Sì - è possibile scrivere un compilatore per C++, in C++, con alcune caratteristiche extra - scrivere il proprio funzioni, compilazione ed esecuzione automatica (o meno) ...

+0

Non vedo l'ora di compilare gli oggetti, creati dinamicamente in runtime su codice macchina, ma li eseguo, anche se non con le massime prestazioni ed efficienza. – dtech

1

La soluzione più semplice disponibile, se non stai cercando prestazioni è incorporare un interprete di linguaggio di scripting, ad esempio per Lua o Python.

+0

Non vedo l'ora di incorporare un linguaggio interpretato da terze parti, ma più come creare queste strutture da solo secondo le mie esigenze. – dtech

+0

-1. Non penso che questo risponda alla domanda. No, dove ha chiesto "Quali lingue supportano questo?" Ha chiesto, "Posso farlo in C++?" – riwalk

+0

Caro Vlad @ Vlad, sai di qualsiasi progetto open source che incorpori Python, vorrei sembrare questo in azione, grazie! – Taozi

4

In sostanza, è necessario scrivere un compilatore C++ all'interno del programma (non un compito banale) e fare la stessa operazione dei compilatori JIT per eseguire il codice. Lei è stato in realtà il 90% del modo in là con questo paragrafo:

Sono consapevole che non può avvenire in modo semplice, ma sicuramente deve essere possibile, ci sono un sacco di linguaggi di programmazione che sono non compilati e crea quel tipo di cose dinamicamente che sono implementate in C o C++.

Esattamente - questi programmi portano con sé l'interprete. Si esegue un programma Python dicendo python MyProgram.py --python è il codice C compilato che ha la capacità di interpretare ed eseguire il programma al volo. Avresti bisogno di fare qualcosa lungo queste linee, ma usando un compilatore C++.

Se avete bisogno di funzioni dinamiche che male, utilizzano una lingua diversa :)

0

Un approccio tipico per questo è di combinare un C++ (o qualsiasi altra cosa è scritto su) progetto con linguaggio di scripting.
Lua è uno dei migliori preferiti, poiché è ben documentato, di piccole dimensioni e dispone di collegamenti per molte lingue.

Ma se non stai guardando in quella direzione, forse potresti pensare di fare un uso delle librerie dinamiche?

+2

Impossibile trovare un motivo per un downvote. La prossima volta che fai un downvote, ti preghiamo di fornire il motivo. –

5

Oltre a utilizzare semplicemente un linguaggio di scripting incorporato (Lua è ideale per l'incorporamento) o scrivere il proprio compilatore per C++ da utilizzare in fase di esecuzione, se si desidera utilizzare C++ è sufficiente utilizzare un compilatore esistente.

Ad esempio Clang è un compilatore C++ costruito come librerie che potrebbe essere facilmente incorporato in un altro programma. È stato progettato per essere utilizzato da programmi come IDE che hanno bisogno di analizzare e manipolare la sorgente C++ in vari modi, ma usando il compilatore LLVM come back-end, ha anche la capacità di generare codice in fase di esecuzione e di fornire un puntatore a funzione che è possibile chiama per eseguire il codice generato.

31

, naturalmente, senza attrezzi menzionati nelle altre risposte, ma semplicemente utilizzando il compilatore C++.

basta seguire questi passaggi dall'interno del programma C++ (su Linux, ma deve essere simile su altri sistemi operativi)

  1. scrivere un programma C++ in un file (ad esempio in /tmp/prog.cc), utilizzando un
  2. compila il programma via system("c++ /tmp/prog.cc -o /tmp/prog.so -shared -fPIC");
  3. carica il programma dinamicamente, ad es. utilizzando dlopen()
+9

... dato che il compilatore è installato sul computer di destinazione. –

+0

@ MathieuRodic corretto. – Walter

1

Date un'occhiata in ExpressionTrees in .NET - penso che questo è fondamentalmente ciò che si vuole raggiungere. Crea un albero di sottoespressioni e poi valutale. In un modo orientato agli oggetti, ciascun nodo nel potrebbe sapere come valutare se stesso, mediante ricorsione nei suoi sottonodi. Il tuo linguaggio visivo creerebbe quindi questo albero e potrai scrivere un semplice interprete per eseguirlo.

Inoltre, dai un'occhiata a Ptolemy II, come esempio in Java su come un tale linguaggio di programmazione visuale può essere scritto.

21

È anche possibile assegnare il bytecode direttamente a una funzione e passarla semplicemente come tipo di funzione come illustrato di seguito.

ad es.

byte[3] func = { 0x90, 0x0f, 0x1 } 
*reinterpret_cast<void**>(&func)() 
+15

Che trucco!Come conosci i codici byte di qualsiasi funzione? Funziona davvero? – Walter

+2

Come ottenere il bytecode di una funzione ?! – Coolwater

+7

Controlla il manuale CPU – Jay

3

Dai un'occhiata a libtcc; è semplice, veloce, affidabile e adatto alle tue esigenze. Lo uso ogni volta che ho bisogno di compilare le funzioni C "al volo".

Nell'archivio, si trova il file esempi/libtcc_test.c, che può dare un buon vantaggio. Questo piccolo tutorial potrebbe anche aiutarti: http://blog.mister-muffin.de/2011/10/22/discovering-tcc/

Fai domande nei commenti se incontri problemi usando la libreria!

5

Di seguito un esempio per la compilazione di runtime C++ basata sul metodo menzionato in precedenza (scrivere il codice nel file di output, compilare tramite , caricare tramite dlopen() e dlsym()). Vedi anche l'esempio in related question. La differenza qui è che compila dinamicamente una classe piuttosto che una funzione. Ciò si ottiene aggiungendo al codice una funzione stile maker() per la compilazione dinamica. Riferimenti:

L'esempio funziona solo sotto Linux (Windows ha LoadLibrary e GetProcAddress funzioni invece), e richiede il compilatore identica a essere disponibili sul computer di destinazione.

baseclass.h

#ifndef BASECLASS_H 
#define BASECLASS_H 
class A 
{ 
protected: 
    double m_input;  // or use a pointer to a larger input object 
public: 
    virtual double f(double x) const = 0; 
    void init(double input) { m_input=input; } 
    virtual ~A() {}; 
}; 
#endif /* BASECLASS_H */ 

main.cpp

#include "baseclass.h" 
#include <cstdlib>  // EXIT_FAILURE, etc 
#include <string> 
#include <iostream> 
#include <fstream> 
#include <dlfcn.h>  // dynamic library loading, dlopen() etc 
#include <memory>  // std::shared_ptr 

// compile code, instantiate class and return pointer to base class 
// https://www.linuxjournal.com/article/3687 
// http://www.tldp.org/HOWTO/C++-dlopen/thesolution.html 
// https://stackoverflow.com/questions/11016078/ 
// https://stackoverflow.com/questions/10564670/ 
std::shared_ptr<A> compile(const std::string& code) 
{ 
    // temporary cpp/library output files 
    std::string outpath="/tmp"; 
    std::string headerfile="baseclass.h"; 
    std::string cppfile=outpath+"/runtimecode.cpp"; 
    std::string libfile=outpath+"/runtimecode.so"; 
    std::string logfile=outpath+"/runtimecode.log"; 
    std::ofstream out(cppfile.c_str(), std::ofstream::out); 

    // copy required header file to outpath 
    std::string cp_cmd="cp " + headerfile + " " + outpath; 
    system(cp_cmd.c_str()); 

    // add necessary header to the code 
    std::string newcode = "#include \"" + headerfile + "\"\n\n" 
          + code + "\n\n" 
          "extern \"C\" {\n" 
          "A* maker()\n" 
          "{\n" 
          " return (A*) new B(); \n" 
          "}\n" 
          "} // extern C\n"; 

    // output code to file 
    if(out.bad()) { 
     std::cout << "cannot open " << cppfile << std::endl; 
     exit(EXIT_FAILURE); 
    } 
    out << newcode; 
    out.flush(); 
    out.close(); 

    // compile the code 
    std::string cmd = "g++ -Wall -Wextra " + cppfile + " -o " + libfile 
         + " -O2 -shared -fPIC &> " + logfile; 
    int ret = system(cmd.c_str()); 
    if(WEXITSTATUS(ret) != EXIT_SUCCESS) { 
     std::cout << "compilation failed, see " << logfile << std::endl; 
     exit(EXIT_FAILURE); 
    } 

    // load dynamic library 
    void* dynlib = dlopen (libfile.c_str(), RTLD_LAZY); 
    if(!dynlib) { 
     std::cerr << "error loading library:\n" << dlerror() << std::endl; 
     exit(EXIT_FAILURE); 
    } 

    // loading symbol from library and assign to pointer 
    // (to be cast to function pointer later) 
    void* create = dlsym(dynlib, "maker"); 
    const char* dlsym_error=dlerror(); 
    if(dlsym_error != NULL) { 
     std::cerr << "error loading symbol:\n" << dlsym_error << std::endl; 
     exit(EXIT_FAILURE); 
    } 

    // execute "create" function 
    // (casting to function pointer first) 
    // https://stackoverflow.com/questions/8245880/ 
    A* a = reinterpret_cast<A*(*)()> (create)(); 

    // cannot close dynamic lib here, because all functions of the class 
    // object will still refer to the library code 
    // dlclose(dynlib); 

    return std::shared_ptr<A>(a); 
} 


int main(int argc, char** argv) 
{ 
    double input=2.0; 
    double x=5.1; 
    // code to be compiled at run-time 
    // class needs to be called B and derived from A 
    std::string code = "class B : public A {\n" 
         " double f(double x) const \n" 
         " {\n" 
         "  return m_input*x;\n" 
         " }\n" 
         "};"; 

    std::cout << "compiling.." << std::endl; 
    std::shared_ptr<A> a = compile(code); 
    a->init(input); 
    std::cout << "f(" << x << ") = " << a->f(x) << std::endl; 

    return EXIT_SUCCESS; 
} 

uscita

$ g++ -Wall -std=c++11 -O2 -c main.cpp -o main.o # c++11 required for std::shared_ptr 
$ g++ -ldl main.o -o main 
$ ./main 
compiling.. 
f(5.1) = 10.2 
+0

dannazione, non è la cosa che stavo cercando ma questo è dannatamente cool hack – AoeAoe