2012-06-13 14 views
40

C++ es un lenguaje estático, compilado, las plantillas se resuelven durante el tiempo de compilación y así sucesivamente ...¿Es posible crear una función de forma dinámica durante el tiempo de ejecución en C++?

Pero es posible crear una función en tiempo de ejecución, que no se describe en el código fuente y no se ha convertido para mecanizar el lenguaje durante la compilación, de modo que un usuario pueda arrojar datos que no se hayan anticipado en la fuente?

Soy consciente de que esto no puede suceder de manera directa, pero seguramente debe ser posible, hay un montón de lenguajes de programación que no se compilan y crean ese tipo de cosas de forma dinámica que se implementan en C o C++.

Tal vez si se crean fábricas para todos los tipos primitivos, junto con estructuras de datos adecuadas para organizarlos en objetos más complejos como tipos de usuario y funciones, ¿esto se puede lograr?

Cualquier información sobre el tema, así como punteros a los materiales en línea son bienvenidos. ¡Gracias!

EDIT: Soy consciente de que es posible, es más como estoy interesado en la implementación detalles :)

+1

¿Puede dar un ejemplo de lo que se ¿Esperaría? –

+0

Los compiladores a menudo se escriben en C++. Gran parte de .NET está escrito en C++. La respuesta es sí. –

+0

escribir un intérprete es en realidad bastante simple ... –

Respuesta

1

Sí - puede escribir un compilador para C++, en C++, con algunas características adicionales - escribir su propio funciones, compilar y ejecutar automáticamente (o no) ...

+0

No tengo muchas ganas de compilar los objetos, creados dinámicamente en tiempo de ejecución para el código de la máquina, simplemente ejecútelos, aunque no con el máximo rendimiento y eficiencia. – dtech

1

La solución más simple disponible, si no está buscando el rendimiento es incrustar un intérprete de lenguaje de scripts, por ejemplo para Lua o Python.

+0

No tengo ganas de incorporar un idioma interpretado por un tercero, sino más bien crear esas instalaciones por mi cuenta de acuerdo con mis propias necesidades. – dtech

+0

-1. No creo que esto responda la pregunta. No, dónde preguntó "¿Qué idiomas respaldan esto?" Él preguntó: "¿Puedo hacerlo en C++?" – riwalk

+0

Estimado Vlad @Vlad, ¿sabe de algún proyecto de código abierto que integre python, deseo que esto parezca en acción, gracias! – Taozi

4

Esencialmente necesitarás escribir un compilador C++ dentro de tu programa (no es una tarea trivial), y hacer lo mismo que hacen los compiladores JIT para ejecutar el código. Usted era en realidad el 90% del camino allí con este párrafo:

Soy consciente de que esto no puede suceder de una manera directa, pero seguramente debe ser posible, hay un montón de lenguajes de programación que se no compilados y crea ese tipo de cosas dinámicamente que son implementadas en C o C++.

Exactamente - esos programas llevan consigo el intérprete. Ejecuta un programa python diciendo python MyProgram.py --python es el código compilado C que tiene la capacidad de interpretar y ejecutar su programa sobre la marcha. Necesitaría hacer algo en esa línea, pero usando un compilador de C++.

Si necesita funciones dinámicas que mal, utilizan un lenguaje diferente :)

0

Un enfoque típico para esto es combinar un C++ (o lo que sea que está escrita) proyecto con lenguaje de script.
Lua es uno de los principales favoritos, ya que está bien documentado, es pequeño y tiene enlaces para muchos idiomas.

Pero si no está mirando en esa dirección, ¿tal vez podría pensar en hacer uso de las bibliotecas dinámicas?

+2

No se puede encontrar un motivo para un voto a la baja. La próxima vez que rechace, proporcione el motivo. –

5

Además de simplemente usar un lenguaje de scripting incorporado (Lua es ideal para incrustación) o escribir su propio compilador para C++ para usar en tiempo de ejecución, si realmente desea usar C++ puede usar un compilador existente.

Por ejemplo, Clang es un compilador de C++ creado como bibliotecas que podrían incorporarse fácilmente en otro programa. Fue diseñado para ser utilizado desde programas como IDE que necesitan analizar y manipular la fuente C++ de varias maneras, pero usando la infraestructura del compilador LLVM como backend también tiene la capacidad de generar código en tiempo de ejecución y proporcionarle un puntero de función que pueda llamar para ejecutar el código generado.

31

, por supuesto, sin necesidad de herramientas mencionados en las otras respuestas, pero simplemente usando el compilador de C++.

sólo tienes que seguir estos pasos desde dentro de su programa en C++ (en Linux, sino que debe ser similar en otros sistemas operativos)

  1. escribir un programa en C++ en un archivo (por ejemplo, en /tmp/prog.cc), usando un ofstream
  2. compilar el programa a través de system("c++ /tmp/prog.cc -o /tmp/prog.so -shared -fPIC");
  3. cargar el programa dinámicamente, por ejemplo utilizando dlopen()
+9

... dado que el compilador está instalado en la máquina de destino. –

+0

@MathieuRodic correcto. – Walter

1

Tener una mirada en ExpressionTrees en .NET - Creo que esto es básicamente lo que quiere lograr. Crea un árbol de subexpresiones y luego evalúalas. De forma orientada a objetos, cada nodo en el podría saber cómo evaluarse a sí mismo, por recursión en sus subnodos. Su lenguaje visual entonces crearía este árbol y usted puede escribir un intérprete simple para ejecutarlo.

Además, consulte Ptolemy II, como ejemplo en Java sobre cómo se puede escribir un lenguaje de programación visual.

21

También puede simplemente dar el bytecode directamente a una función y simplemente pasarlo fundido como el tipo de función como se muestra a continuación.

p. Ej.

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

¡Qué hack!¿Cómo se conocen los códigos de bytes de cualquier función? ¿Esto realmente funciona? – Walter

+2

¿Cómo obtener el bytecode de una función? – Coolwater

+7

Compruebe el manual de la CPU – Jay

3

Echa un vistazo a libtcc; es simple, rápido, confiable y se adapta a sus necesidades. Lo uso cada vez que necesito compilar funciones C "sobre la marcha".

En el archivo, encontrará el archivo examples/libtcc_test.c, que le puede dar una buena ventaja. Este pequeño tutorial también podría ayudarlo: http://blog.mister-muffin.de/2011/10/22/discovering-tcc/

¡Haga preguntas en los comentarios si encuentra algún problema al usar la biblioteca!

5

A continuación un ejemplo para C++ compilación en tiempo de ejecución basado en el método mencionado antes (código de escritura al archivo de salida, a través de compilar system(), carga a través de dlopen() y dlsym()). Vea también el ejemplo en un related question. La diferencia aquí es que compila dinámicamente una clase en lugar de una función. Esto se logra agregando una función C-style maker() al código que se compilará dinámicamente. Referencias:

El ejemplo sólo funciona bajo Linux (Windows tiene LoadLibrary y GetProcAddress funciones en su lugar), y requiere el compilador idéntica a estar disponible en la máquina objetivo.

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; 
} 

salida

$ 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

maldita sea, no es lo que estaba buscando, pero esto es bastante hack genial – AoeAoe

Cuestiones relacionadas