c - 重构遗留 C 代码——使用外部声明来帮助拆分模块(潜在的链接和运行时问题)

标签 c linker refactoring runtime

我正在重构一些用 C 语言编写的旧遗留代码。这些代码耦合非常紧密,我正在努力将其重构为逻辑清晰、耦合松散的模块。

在我的早期迭代中,我设法确定了逻辑模块——但是紧密耦合给我带来了问题,因为许多功能对系统的其他部分有深入的了解。

我打算解决这个问题的方法是使用外部声明。希望下面的伪代码解释了这种情况:

假设我有两个逻辑上独立的模块 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() 将被解析(函数定义在模块中)。

我上面描述的方案是否会像我预期的那样工作,或者我是否遗漏了一些问题?

最佳答案

我试过你将你的文件编译成共享库然后使用它们(我使用 cygwin)。

这是给 Foo 的:

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

使用 bin util nm 您可以检查符号(grep for 'foo' 以限制输出)

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

它给出了一个偏移量并用“T”表示终止,告诉您该符号已在库中定义。

现在 FooBar 库必须链接到 Foo 才能拥有 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

所以它似乎工作正常。

您可以改进模块化 C 代码的方法,方法是让头文件仅包含公共(public)数据类型、导出的函数原型(prototype)(声明为 extern),甚至可能包括公共(public)全局变量或常量。这样的头文件声明了模块的接口(interface),并且必须包含在使用模块的地方。

这本精彩的书 'Functional C' 对此进行了更详细的解释。 (Hartel, Muller, 1997, Addison Wesley, link)在关于模块的章节中。

好处是您的依赖项更清晰可见(如包含在源文件中),并且您不必在 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);
}

关于c - 重构遗留 C 代码——使用外部声明来帮助拆分模块(潜在的链接和运行时问题),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14146280/

相关文章:

c - 用 C 直接读写压缩文件

c - 关于使用运算符 "or"的小细节

c++ - 链接器和图书馆员中的 VC++ 2012 Additional Dependencies 选项

javascript - 使用 jQuery 实现来回移动 div 的更简洁方法?

c++ - 一次从文件中读取字符 block ,并在 C 的下一个循环中读取下一个 block

c - printf 语句中用于宽度和精度的子说明符

c++ - 链接问题 : fatal error LNK1112: module machine type 'x64' conflicts with target machine type 'X86'

c++ - CMake "OBJECT"库 : clang not linking properly

angularjs - 将react-redux集成到现有的Angularjs应用程序中

构造函数实例变量 Pharo