web-dev-qa-db-ja.com

C ++での実行時に、関数を動的に作成することは可能ですか?

C++は静的なコンパイル済み言語であり、テンプレートはコンパイル時に解決されます...

しかし、実行時にソースコードに記述されておらず、コンパイル中に機械語に変換されていない関数を作成して、ユーザーがソースで予期されていないデータをスローすることは可能ですか?

私はこれが簡単な方法で起こることはないことを知っていますが、確かにそれは可能であるに違いありません。コンパイルされていないプログラミング言語がたくさんあり、CまたはC++で実装されるそのようなものを動的に作成します。

すべてのプリミティブ型のファクトリが、ユーザータイプや関数などのより複雑なオブジェクトに編成するための適切なデータ構造とともに作成された場合、おそらくこれは達成可能でしょうか?

このテーマに関する情報やオンライン資料へのポインタは大歓迎です。ありがとう!

編集:私はそれが可能であることを知っています、それは私が実装の詳細に興味を持っているようです:)

54
dtech

はい、もちろん、ツールなし他の回答で言及されていますが、単にC++コンパイラを使用です。

c ++プログラム内からこれらの手順に従うだけです(Linuxでは、他のOSでも同様である必要があります)

  1. ofstreamを使用して、C++プログラムをファイル(/tmp/prog.ccなど)に書き込みます。
  2. system("c++ /tmp/prog.cc -o /tmp/prog.so -shared -fPIC");を介してプログラムをコンパイルします
  3. プログラムを動的にロードします。 dlopen()を使用
44
Walter

また、バイトコードを関数に直接渡し、以下に示すように関数タイプとしてキャストするだけで渡すこともできます。

例えば.

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

はい、JITコンパイラーは常にそれを行います。 OSによって特別な実行権が与えられたメモリの一部を割り当て、それをコードで埋め、ポインターを関数ポインターにキャストして実行します。ものすごく単純。

編集:Linuxでそれを行う方法の例を次に示します。 http://burnttoys.blogspot.de/2011/04/how-to-allocate-executable-memory-on.html

15
Milan

前述のメソッドに基づいたC++ランタイムコンパイルの例(出力ファイルへのコードの書き込み、system()を介したコンパイル、dlopen()およびdlsym()を介したロード)。 関連する質問 の例を参照してください。ここでの違いは、関数ではなくクラスを動的にコンパイルすることです。これは、動的にコンパイルされるコードにCスタイルのmaker()関数を追加することで実現されます。参照:

この例はLinuxでのみ動作し(WindowsにはLoadLibraryおよびGetProcAddress関数があります)、ターゲットマシンで同一のコンパイラが利用可能である必要があります。

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

output

$ 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
9
user1059432

単に組み込みスクリプト言語を使用するだけでなく( Lua は埋め込みに最適です)、または実行時に使用するC++用の独自のコンパイラを作成します。C++を本当に使用する場合は、既存のコンパイラを使用できます。

たとえば、 Clang は、別のプログラムに簡単に組み込むことができるライブラリとして構築されたC++コンパイラです。さまざまな方法でC++ソースを分析および操作する必要があるIDEなどのプログラムから使用するように設計されましたが、バックエンドとして [〜#〜] llvm [〜#〜] コンパイラインフラストラクチャも使用します実行時にコードを生成し、生成されたコードを実行するために呼び出すことができる関数ポインターを渡すことができます。

5
bames53

libtcc ;をご覧ください。シンプル、高速、信頼性が高く、ニーズに合っています。 C関数を「オンザフライ」でコンパイルする必要があるときはいつでも使用します。

アーカイブには、ファイルexamples/libtcc_test.cがあります。これにより、良いスタートを切ることができます。この小さなチュートリアルも役立つかもしれません: http://blog.mister-muffin.de/2011/10/22/discovering-tcc/

ライブラリの使用中に問題が発生した場合は、コメント欄で質問してください!

4
Mathieu Rodic

基本的に、プログラム内でC++コンパイラーを作成し(簡単なタスクではなく)、JITコンパイラーがコードを実行するのと同じことを行う必要があります。実際、あなたはこの段落でその道の90%を占めていました。

私はこれが簡単な方法で起こることはないことを知っていますが、確かにそれは可能であるに違いありません。コンパイルされていないプログラミング言語がたくさんあり、CまたはC++で実装されるそのようなものを動的に作成します。

正確に-これらのプログラムはインタープリターを持ちます。 python MyProgram.pyと言ってpythonプログラムを実行します--- pythonはコンパイル済みのCコードであり、プログラムをその場で解釈して実行することができます。行ですが、C++コンパイラを使用しています。

動的な関数が必要な場合thatひどく、別の言語を使用してください:)

4
riwalk

このための典型的なアプローチは、C++(または書かれているもの)プロジェクトをスクリプト言語と結合することです。
Lua は、よく文書化されており、小さく、多くの言語のバインディングがあるため、最も人気のあるものの1つです。

しかし、その方向を検討していない場合、おそらく動的ライブラリを使用することを考えられるでしょうか?

3

Runtime Compiled C++ (または RCC++ blog and videos をご覧ください)、または alternatives のいずれかを試してください。

1
Doug Binks

.NETのExpressionTreesを見てください-これは基本的にあなたが達成したいことだと思います。部分式のツリーを作成し、それらを評価します。オブジェクト指向の方法では、の各ノードは、サブノードへの再帰によって、それ自体を評価する方法を知っている場合があります。その後、視覚言語がこのツリーを作成し、簡単なインタープリターを作成して実行できます。

また、 Ptolemy II もご覧ください。例として、Javaのようなビジュアルプログラミング言語の記述方法をご覧ください。

1
Daren Thomas

はい-いくつかの追加機能を備えたC++でC++のコンパイラを作成できます-独自の関数を作成し、自動的にコンパイルして実行します(またはしない)...

1
Luchian Grigore

私にとってはこのように機能しました。 -fpermissiveフラグを使用する必要があります。 CodeBlocks 17.12を使用しています。

#include <cstddef>

using namespace std;
int main()
{
    char func[] = {'\x90', '\x0f', '\x1'};
    void (*func2)() = reinterpret_cast<void*>(&func);
    func2();
    return 0;
}
0
Anyone

パフォーマンスを探していない場合に利用できる最も簡単なソリューションは、スクリプト言語インタープリターを埋め込むことです。 Lua または Python の場合。

0
Vlad