0

main()学生に課した演習に対処する を作成したとします。

すべての学生は、同じ API を使用して独自の関数を作成することになっています。そして、すべての関数とそれらを呼び出す main を含む単一のファイルが作成されます。

としましょう:int studentname(int a, int b)は関数パターンです。

これに対処する 1 つの方法は、関数へのポインターのベクトルを使用することでしたint (*func[MAX])()。ただし、ベクトルを 1 つずつ満たす必要がありますfunc[0]=studentname;

どういうわけか、関数をその名前で呼び出す方法はありますか?

のようなもの: int student1(int a , int b)student2()など。

どういうわけか、mainを呼び出すことができますsscanf(funcname,"student%d",i); funcname();

他に何か考えはありますか?多分

int studentname(int a, int b, char *fname)
{
    strcpy(fname, "studentname");

クリエイティブならなんでもします!:)

ありがとう!ベコ

PS。関数のベクトルだけを試してみましたが、C では許可されません! :)

int func[2]()={{;},{;}};

このようにして、各生徒に番号を与えることができました。でも面白かったです。


編集済み: Linux を使用しています。

編集2:ありがとう!私は助けになった回答を受け入れましたが、完全な例を回答として以下に文書化しました。

4

7 に答える 7

2

@Jamey-Sharpが提案したものと同様:

  • 各生徒に.c、指定された名前/署名のエントリ機能を備えたファイルを提供するように依頼します
  • それぞれを共有ライブラリにコンパイル.cし、学生の名前で名前を付けるか、任意の一意の名前を付けます。このステップは、makeまたは単純なスクリプトを使用して簡単に自動化できます。
  • .so特定のディレクトリ内のすべてのファイルを列挙し、 と を使用してエントリ ポイント関数に到達する単純なホスト アプリケーションをdlopen()作成dlsym()します。
  • これで、各生徒の実装を簡単に呼び出すことができます。

ところで、それがプラグインの通常の実装方法ですよね。

編集:これは、実際の概念実証です(そして、各学生がエントリポイント関数の同じ名前を使用できるという証明です)。

ここにありstudent1.cます:

#include <stdio.h>

void student_task()
{
    printf("Hello, I'm Student #1\n");    
}

ここにありstudent2.cます:

#include <stdio.h>

void student_task()
{
    printf("Hello, I'm Student #2\n");    
}

メインプログラムは次のtester.cとおりです。

#include <stdio.h>
#include <dlfcn.h>

/* NOTE: Error handling intentionally skipped for brevity! 
 * It's not a production code!
 */

/* Type of the entry point function implemented by students */
typedef void (*entry_point_t)(void);

/* For each student we have to store... */
typedef struct student_lib_tag {
    /* .. pointer to the entry point function, */
    entry_point_t entry;
    /* and a library handle, so we can play nice and close it eventually */ 
    void* library_handle;
} student_solution_t;

void load(const char* lib_name, student_solution_t* solution)
{
    /* Again - all error handling skipped, I only want to show the idea! */

    /* Open the library. RTLD_LOCAL is quite important, it keeps the libs separated */
    solution->library_handle = dlopen(lib_name, RTLD_NOW | RTLD_LOCAL);

    /* Now we ask for 'student_task' function. Every student uses the same name.
     * strange void** is needed for C99, see dlsym() manual.
     */
    *(void**) (&solution->entry) = dlsym(solution->library_handle, "student_task");

    /* We have to keep the library open */
}

int main()
{
    /* Two entries hardcoded - you need some code here that would scan
     * the directory for .so files, allocate array dynamically and load 
     * them all.
     */
    student_solution_t solutions[2];

    /* Load both solutions */
    load("./student1.so", &solutions[0]);
    load("./student2.so", &solutions[1]);

    /* Now we can call them both, despite the same name of the entry point function! */
    (solutions[0].entry)();
    (solutions[1].entry)();

    /* Eventually it's safe to close the libs */
    dlclose(solutions[0].library_handle);
    dlclose(solutions[1].library_handle);
    return 0;
}

すべてをコンパイルしましょう。

czajnik@czajnik:~/test$ gcc -shared -fPIC student1.c -o student1.so -Wall
czajnik@czajnik:~/test$ gcc -shared -fPIC student2.c -o student2.so -Wall
czajnik@czajnik:~/test$ gcc tester.c -g -O0 -o tester -ldl  -Wall 

そして、それが機能することを確認してください:

czajnik@czajnik:~/test$ ./tester 
Hello, I'm Student #1
Hello, I'm Student #2
于 2012-11-08T00:26:25.937 に答える
2

少し複雑すぎるかもしれませんが、自発的なアイデア:

  • すべての生徒のソース ファイルを 1 つの共有ライブラリにコンパイルし、生徒の関数をエクスポートします。
  • 次に、公開されているすべての関数を列挙し、それらを呼び出してテストします。

別の方法として:

  • 事前定義された関数名を一意の名前 (「func1」、「func2」など) に置き換えるプリプロセッサ定義を使用して、すべての「学生ユニット」をコンパイルする小さなツールを作成します。
  • 次に、テストなどの実行中にこれらすべての関数を呼び出す小さなユニットをツールに記述させます。

さらに別のアイデア:

  • C++ を使用して、派生クラスをオブジェクト ファクトリに登録し、extern "C". ただし、実装によっては、これは少し混乱し、複雑すぎるように見えるかもしれません。
  • 次に、ファクトリを使用してそれぞれのインスタンスを 1 つ作成し、コードを実行します。

dlopen()andを使用したアプローチの例dlsym()(ライブラリごとに 1 つの関数のみか、すべての関数かは関係ありません):

void *pluginlib = dlopen("student1.so", RTLD_NOW); // RTLD_NOW will load the file right away
if (!pluginlib)
    ; // failed to load
studentproc func = (studentproc)dlsym(pluginlib, "student1"); // this loads the function called "student1"
if (!func)
    ; // failed to resolve
func("hello world!"); // call the lib
dlclose(pluginlib); // unloads the dll (this will make all further calls invalid)
于 2012-11-08T00:11:11.023 に答える
1

私は別のアプローチを取ります:

  1. すべての生徒に同じ関数名を使用するように要求し、各生徒のコードを別のソース ファイルに配置します。
  2. main標準名を呼び出すソース ファイルをもう 1 つ記述します。
  3. 、次になどとリンクmain.cして、別の実行可能ファイルを生成します。これを自動化するために、メイクファイルまたはシェル スクリプトでワイルドカードを使用できる場合があります。student1.cmain.cstudent2.c

とはいえ、少なくとも Unix ライクな OS では、要求したことを実行できます。

  1. を呼び出しdlopen(NULL)て、メイン プログラム内のシンボルのハンドルを取得します。
  2. そのハンドルと必要な関数名を渡しますdlsym。結果のポインターを正しい型の関数ポインターに強制し、それを呼び出します。
于 2012-11-08T00:10:31.430 に答える
1

これは醜いプリプロセッサのハックです:

#Makefile

FILE_NAME=student

${FILE_NAME}: main.c
        cc -Wall -DFILE_NAME=\"${FILE_NAME}.c\" -o $@ main.c -lm

先生のmain.c:

#include <math.h>
#include <stdio.h>

#include FILE_NAME

char *my_name(void);
double my_sin(double val);

int main(void)
{
double dd;
dd = my_sin(3.1415923563);

printf("%s: %f\n", my_name(), dd);
return 0;
}

生徒の .c ファイル:

#include <math.h>

char * my_name(void);
double my_sin(double val);

char * my_name(void)
{
return "Wildplasser-1.0";
}

double my_sin(double val)
{
return sin (val);
}

秘訣は、生徒の .c ファイルを文字通りインクルードすることにあります。

これを避けるために、次のような別の make 行を使用することもできます。

 cc -Wall -o $@ ${FILE_NAME}.c main.c -lm

#include FILENAME(もちろん、醜いを削除します)

于 2012-11-08T01:19:47.533 に答える
1

ありがとうございます。質問を解決するためのインスピレーションを与えてくれた回答を受け入れました。ここに、それを文書化するために、私の完全な解決策があります:

ファイルshamain.c

/* Uses shared library shalib.so
 * Compile with:
 *    gcc shamain.c -o shamain -ldl -Wall
 */


#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

int main(void)
{
  void *libstud;
  int (*student[2])(int, int);
  char fname[32];
  int i,r;

  libstud = dlopen("./shalib.so", RTLD_NOW);
  if (!libstud)
  {
    fprintf(stderr, "error: %s\n", dlerror());
    exit(EXIT_FAILURE);
  }
  dlerror();    /* Clear any existing error */

  for(i=0; i<2; i++)
  {
    sprintf(fname, "func%d", i);
    *(void **) (&student[i]) = dlsym(libstud, fname); /* c99 crap */
    //student[i] = (int (*)(int, int)) dlsym(libstud, fname); /* c89 format */
  }

  for(i=0; i<2; i++)
  {
    r=student[i](i, i);
    printf("i=%d,r=%d\n", i, r);
  }

  return 0;
}

ファイル shalib.c

/* Shared library.
 * Compile with:
 *  gcc -shared -fPIC shalib.c -o shalib.so -Wall
 */

#include <stdio.h>

int func0(int one, int jadv)
{
  printf("%d = Smith\n", one);
  return 0;
}

int func1(int one, int jadv)
{
  printf("%d = John\n", one);
  return 0;
}
于 2012-11-08T01:37:21.743 に答える
0

共有ライブラリを使用してからしばらく経ちますが、DLL/shlib から名前付き関数を抽出できると感じています。すべての実装を含む DLL/共有ライブラリを作成し、メインから名前でアクセスできますか?

于 2012-11-08T00:08:57.167 に答える
0

@william-morris の提案によるdlsym()と、関数の動的ルックアップを使用して運が良かったかもしれません。(dlsym()特定のプラットフォームで使用するライブラリ呼び出しである場合とそうでない場合があります。)

于 2012-11-08T00:10:30.253 に答える