【问题标题】:Function with same names in different files - C不同文件中具有相同名称的函数 - C
【发布时间】:2020-05-22 10:02:59
【问题描述】:

我有两个同名的函数,想在我的应用程序中使用它。

我参考了各种答案,例如 here 和这里,但无法得到明确的解决方案。

我有以下功能

// xxxx_input.h

int8_t input_system_init(InputParams params);

int8_t input_system_easy_load(uint32_t interval_ms);
// yyyy_input.h

int8_t input_system_init(InputParams params);

int8_t input_system_easy_load(uint32_t interval_ms);

原因有两个文件是xxxx_inputyyyy_input 在内部工作方式不同。

修改函数并不容易,因为代码是由外部提供的,我们必须保留 xxxx_input 文件。

我们可以做的是修改yyyy_input.h,但是像input_system_easy_load这样的函数要保持一致,因为它们是从不同的地方调用的。

有没有办法达到同样的效果?

我尝试将xxxx_input 替换为yyyy_input.h,但由于包含目录已经包含相同的函数,它会报错。

input_system_init 多重定义(由 xxxx_input.o 和 yyyy_input.o)。

【问题讨论】:

  • 不能在同一个编译单元中同时使用两者。
  • 所以我建议更改名称,即使这很麻烦。
  • C 函数名称总是在全局范围内结束(如果需要从不同的翻译单元调用它们),因此如果您需要同时构建两者,则需要重命名其中一个。
  • 鉴于您手头的资料,编译两个单独的二进制文件并一个接一个地运行它们(可能通过相同的脚本/作业)是更好的选择,还是这些工作流真正依赖于每个其他的?
  • @possum 你认为这比重命名其中一个更容易吗? ^^

标签: c


【解决方案1】:

如果您拥有xxxx_input.hyyyy_input.h 中定义的函数的源代码,您可以使用命令行选项通过预处理器重新定义函数名称来编译这两个模块:

gcc -Dinput_system_init=xxxx_input_system_init -Dinput_system_easy_load=xxxx_input_system_easy_load xxxx_input.c
gcc -Dinput_system_init=yyyy_input_system_init -Dinput_system_easy_load=yyyy_input_system_easy_load yyyy_input.c

然后,您将使用修改后的原型编译您的代码,并将所有 3 个模块链接在一起。

如果模块仅以对象形式提供,您可以定义包装函数 xxxx_input_system_initxxxx_input_system_easy_load,您将与 xxxx_input.o 链接以生成动态库,yyyy 替代项也是如此。您将在模块中使用修改后的原型,并将其与动态库链接。

Mike Kinghan 在objcopy 可用的系统上为目标文件和库展示了simpler approach

要自动获取修改后的原型,您可以使用这个包含文件:

my_input_system.h

#define input_system_init  xxxx_input_system_init
#define input_system_easy_load  xxxx_input_system_easy_load
#include "xxxx_input.h"
#undef input_system_init
#undef input_system_easy_load

#define input_system_init  yyyy_input_system_init
#define input_system_easy_load  yyyy_input_system_easy_load
#include "yyyy_input.h"
#undef input_system_init
#undef input_system_easy_load

/* prevent direct use of the redefined functions */
#define input_system_init       do_not_use_input_system_init@
#define input_system_easy_load  do_not_use_input_system_easy_load@

【讨论】:

    【解决方案2】:

    如果您的供应商为您提供编译的目标文件,我将介绍一个您可以使用的解决方案 在 GNU/Linux 计算机上使用 GCC 或 Clang。

    我的供应商给了我一个标题 foo_a.h 文件,声明了函数 foo

    $cat foo_a.h
    #pragma once
    
    extern void foo(void);
    

    和匹配的对象文件foo.o:

    $ nm foo_a.o
    0000000000000000 T foo
                     U _GLOBAL_OFFSET_TABLE_
                     U puts
    

    定义foo

    同样,他们给了我一个标题foo_b.h,它也声明了foo

    $ cat foo_b.h
    #pragma once
    
    extern void foo(void);
    

    和匹配的目标文件foo_b.o

    $ nm foo_b.o
    0000000000000000 T foo
                     U _GLOBAL_OFFSET_TABLE_
                     U puts
    

    这也定义了foo

    foo_a.o:foofoo_b.o:foo 的功能不同 事物(或同一事物的不同变体)。我想在同一个程序中做这两件事, prog.c:

    $ cat prog.c
    extern void foo_a(void);
    extern void foo_b(void);
    
    int main(void)
    {
        foo_a();    // Calls `foo_a.o:foo`
        foo_b();    // Calls `foo_b.o:foo`
        return 0;
    }
    

    我可以制作这样一个程序:

    $ objcopy --redefine-sym foo=foo_a foo_a.o prog_foo_a.o
    $ objcopy --redefine-sym foo=foo_b foo_b.o prog_foo_b.o
    

    现在我复制了foo_a.oprog_foo_a.o,其中符号foo 是 重命名为foo_a,以及foo_b.oprog_foo_b.o 副本,其中 符号foo 重命名为foo_b

    然后我像这样编译和链接:

    $ gcc -c -Wall -Wextra prog.c
    $ gcc -o prog prog.o prog_foo_a.o prog_foo_b.o
    

    prog 运行如下:

    $ ./prog
    foo_a
    foo_b
    

    也许我的供应商在静态库liba.a 中给了我foo_a.o 还包含引用foo_a.o:foo 的其他目标文件?同样地 foo_b.o

    没关系。而不是:

    $ objcopy --redefine-sym foo=foo_a foo_a.o prog_foo_a.o
    $ objcopy --redefine-sym foo=foo_b foo_b.o prog_foo_b.o
    

    我会跑:

    $ objcopy --redefine-sym foo=foo_a liba.a libprog_a.a
    $ objcopy --redefine-sym foo=foo_b libb.a libprog_b.a
    

    这会给我一个新的静态库libprog_a.a 其中foo 在所有目标文件中重命名为foo_a 图书馆。类似地,foo 在整个libprog_b.a 中被重命名为foo_b

    那我链接prog:

    $ gcc -o prog prog.o -L. -lprog_a -lprog_b
    

    请考虑此解决方案的潜在缺点。可能我的供应商有 给了我foo_a.ofoo_b.o,里面有调试信息,我想 用它来调试我的proggdb

    我已将原始符号名称 foo_a.o:foo 更改为 foo_afoo_b.o:foofoo_b,但我没有更改相关的调试信息 与那些符号。使用gdb 进行调试仍然有效,但其中一些 调试输出将不正确并且可能令人困惑。例如。如果我把 foo_agdb 上的断点将运行到它并停止,但它会说 它从文件foo_a.c 停止在foo。如果我在foo_b 的断点,gdb 将运行到 它一遍又一遍地说它位于foo,但来自文件foo_b.c。如果这个人在做 调试不知道程序是如何构建的,这肯定是 令人困惑。

    但是通过二进制文件为您提供调试信息并不遥远 源代码,因此您可能没有源代码 调试信息,不关心。

    【讨论】:

    • 多么详尽的答案!谢谢
    • 比基于预处理器的重命名更​​好,更通用!结合我对头文件的方法,将更容易解释原型的修改和/或foo_a.hfoo_b.h 中的其他定义。紫外线
    猜你喜欢
    • 2017-09-29
    • 2015-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-14
    • 1970-01-01
    相关资源
    最近更新 更多