4

私はCで書かれた古いレガシーコードをリファクタリングしているところです。コードは非常に緊密に結合されており、明確な論理的で緩く結合されたモジュールにリファクタリングするのに苦労しています。

私の初期の反復では、論理モジュールを確認することができましたが、多くの関数がシステムの他の部分についての深い知識を持っているため、密結合が問題を引き起こしています。

これを修正する方法は、extern宣言を使用することです。以下の擬似コードは、状況を説明していると思います。

論理的に分離した2つのモジュールFooとFooBarがあると仮定します。各モジュールは個別のライブラリに組み込まれます(FooBarモジュールはFooモジュールに依存しています)。

/*######################################*/
/*             Foo Module               */
/*######################################*/
/*  Foo.h */
#ifndef FOO_MODULE_H
#define FOO_MODULE_H

void foo(int id);
int do_something();
...

#endif /* FOO_MODULE_H */


/* Foo.c */
#include "Foo.h"
extern int foobar();  /* defined in FooBar module (a separate library) */


void foo(int id){
    int var;

    switch (id){
        case 1:
            var = do_something();
            break;

        case 2:
            /* the module that gets here, has the required functions defined in it */
            var = foobar();
    }

    return var;
}



/*###############################*/
/*        FOOBar module          */
/*###############################*/

/* FooBar.h */

#ifndef FOOBAR_MODULE_H
#define FOOBAR_MODULE_H

#include "Foo.h"

int foobar();
void do_something_else();
...

#endif /* FOOBAR_MODULE_H */


/* source file */
int foobar(){
    return 42;
}

void do_something_else(){
   int ret = foo(2);
   printf("Function returned: %d", ret);
}

これは、libfoo.soおよびlibfoobar.soにリンクする実行可能ファイルが引き続き正しく機能することを可能にしながら、既存のコードを論理的に分離したモジュールにリファクタリングする有効な方法ですか?

私の基本的な仮定は、libfoo.soにリンクするモジュールは解決できないということfoobar()ですが、その関数を必要としないので問題ないはずです。したがって、関数を呼び出す必要はありません。一方、libfoobar.soにリンクするモジュールの場合、それらを呼び出すとfoo()、 `foobar()が解決されます(関数定義はモジュール内にあります)。

上記で説明したスキームは期待どおりに機能しますか、それとも私が見逃している落とし穴がありますか?

4

1 に答える 1

2

私はあなたのファイルを共有ライブラリにコンパイルしてからそれらを使用しようとしました(私はcygwinを使用しています)。

これがFooの場合です。

cm@Gregor-PC ~/src
$ gcc -I. -c --shared -o libFoo.so Foo.c

bin util nmを使用すると、シンボルをチェックできます(出力を制限するための「foo」のgrep)

cm@Gregor-PC ~/src
$ nm libFoo.so | grep foo
0000000a T _foo
         U _foobar

オフセットを与え、シンボルがライブラリで定義されていることを示す終了を表す「T」と表示されます。

fooシンボルを取得するには、FooBarlibをFooに対してリンクする必要があります。

cm@Gregor-PC ~/src
$ gcc -I. -L. --shared -o libFooBar.so FooBar.c libFoo.so

cm@Gregor-PC ~/src
$ nm libFooBar.so | grep foo
61f0111e T _foo
61f010e0 T _foobar

これにより、FooBarに対してのみコンパイルし、既知のシンボルとしてfooを取得できます。

cm@Gregor-PC ~/src
$ gcc -I. -o tst1 tst1.c libFooBar.so
cm@Gregor-PC ~/src
$ ./tst1
Function returned: 42

だからそれはうまくいくようです。

パブリックデータ型、エクスポートされた関数プロトタイプ(externとして宣言)、さらにはパブリックグローバル変数または定数のみを含むヘッダーファイルを作成することで、Cコードをモジュール化するアプローチを改善できます。このようなヘッダーファイルは、モジュールへのインターフェイスを宣言し、モジュールが使用される場所にインクルードする必要があります。

これは、モジュールの章にあるすばらしい本「Functional C」(Hartel、Muller、1997、Addison Wesley、リンク)でさらに詳しく説明されています。

利点は、依存関係が(ソースファイルに含まれているように)より明確に表示され、Fooソースでその見苦しいextern宣言を行う必要がないことです。

あなたの例のために:

/*  Foo.h */
extern int foo(int id); /* exported to FooBar */

/* FooBar.h */

extern int foobar(); /* export to Foo */
extern void do_something_else(); /* export to tst1 */


/* Foo.c */
#include <Foo.h>
#include <FooBar.h>


int do_something() {
  return 11;
}

int foo(int id){
    int var;

    switch (id){
        case 1:
            var = do_something();
            break;

        case 2:
            /* the module that gets here, has the required functions defined in it */
            var = foobar();
    }

    return var;
}

/* FooBar.c */

#include <stdio.h>
#include <Foo.h>
#include <FooBar.h>

int foobar(){
    return 42;
}

void do_something_else(){
   int ret = foo(2);
   printf("Function returned: %d", ret);
}
于 2013-01-04T00:07:18.927 に答える