检查系统是否实现了一个函数

9

我正在创建一款跨系统应用程序。它使用例如 itoa 函数,在某些系统上已经实现,但不是所有系统都有。如果我只是提供自己的 itoa 实现:

header.h:115:13: error: conflicting types for 'itoa'
 extern void itoa(int, char[]);

In file included from header.h:2:0,
                 from file.c:2:0,
c:\path\to\mingw\include\stdlib.h:631:40: note: previous declaration of 'itoa' was here
 _CRTIMP __cdecl __MINGW_NOTHROW  char* itoa (int, char*, int);

我知道我可以检查宏是否预定义并在未预定义时定义它们:
#ifndef _SOME_MACRO
#define _SOME_MACRO 45
#endif

有没有一种方法可以检查C函数是否已预先实现,如果没有,则实现它?或者仅仅取消对一个函数的实现?

12
有自动配置系统 autoconf,它旨在处理此类复杂性。没有许多更简单且可靠的系统;我不确定是否知道其他的。还有与 autoconf 一起使用的 automakelibtool,但您主要是需要 autoconf。您可以调查其他构建系统,例如 cmake - Jonathan Leffler
2
关于仅在某些系统上可用的函数,另一个棘手的部分是它们在不同系统上往往具有太多的功能差异。我建议使用包装函数。 - chux - Reinstate Monica
1
包装函数:编写 void my_itoa(int, char[]); 在具有匹配 itoa() 功能/签名的系统上,让 my_itoa() 调用 itoa(),可以使用 inline#define。在具有 char * itoa(int value, char *str, int base); 的平台上(例如此处),调用 itoa(value, str, 10);。对于其他变体平台也是如此。对于缺乏任何类似 itoa() 的功能的系统,请调用您自己的 代码 - chux - Reinstate Monica
1
你不能自动地做到这一点;C预处理器无法访问C代码正在执行的内容。但是,您可以将代码包装在#if块中,有条件地包含您的实现:#if NEED_ITOA int itoa(char *) { ... } #endif。然后在编译时,您可以为没有它的系统使用-DNEED_ITOA。在幕后,这就是像autoconf这样的“魔术”工具所做的所有事情(它们还帮助自动确定您需要哪些-D标志)。但是,在C预处理器本身内部无法做到这一点。 - Sean Werkema
1
针对这个特定问题设计的autoconf功能是AC_REPLACE_FUNCS(在页面底部,您需要阅读整个页面才能理解它的作用)。 - zwol
显示剩余3条评论
4个回答

6

如果你已经编写了自己的 itoa() 实现,我建议你将其重命名并在所有地方使用它。这样,至少你可以确保在所有平台上获得相同的行为,并避免链接问题。

不要忘记在代码注释中解释你的选择...


4

我假设您正在使用GCC,因为我可以在您的路径中看到MinGW...GNU链接器有一种方法可以为您处理这个问题。因此,您不知道是否存在itoa实现。请尝试以下内容:

创建一个新文件(没有任何头文件),名为my_itoa.c

char *itoa (int, char *, int);

char *my_itoa (int a, char *b, int c)
{
    return itoa(a, b, c);
}

现在创建另一个文件,impl_itoa.c。在这里,编写itoa的实现,但添加一个弱别名:
char* __attribute__ ((weak)) itoa(int a, char *b, int c)
{
     // implementation here
}

编译所有文件,将 impl_itoa.c 放在最后。这样,如果标准库中没有 itoa,会链接这个文件。无论是否可用,您都可以确信它会编译。

无法工作。我得到了重定位截断以适应:R_X86_64_PC32针对未定义符号'itoa'的错误信息。 - MD XF
@MDXF 你能否确认你是否只对MinGW感兴趣?这是一个很好的建议,不幸的是MinGW不支持它。 - jerry
好的,这似乎是最好的解决方案。谢谢。如果没有更好的解决方案出现,我会接受并奖励。 - MD XF

2

Ajay Brahmakshatriya的建议是一个好的建议,但不幸的是,据我所知,MinGW不支持弱定义(见https://groups.google.com/forum/#!topic/mingwusers/44B4QMPo8lQ)。

然而,我相信弱引用在MinGW中确实可行。看看这个最小化的例子:

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

__attribute__ ((weak)) char* itoa (int, char*, int);

char* my_itoa (int a, char* b, int c)
{
    if(itoa != NULL) {
        return itoa(a, b, c);
    } else {
        // toy implementation for demo purposes
        // replace with your own implementation
        strcpy(b, "no itoa");
        return b;
    }        
}

int main()
{
    char *str = malloc((sizeof(int)*3+1));

    my_itoa(10, str, 10);
    printf("str: %s\n", str);
    return 0;
}

如果系统提供了itoa的实现,那么应该使用它,并且输出会是:

str: 10

否则,你将得到:

str: no itoa


@MDXF 所以你在cmd下看到它工作了,但是在cygwin下却没有?可能是版本差异(我用的是gcc 4.9.3),但至少在我的系统上通过cygwin可以正常工作。当我说“正常工作”时,我的意思是成功连接并打印 "str: no itoa"。 - jerry
@MDXF 刚刚更新了Cygwin(使用gcc版本5.4.0)并测试了本地的MinGW,两者在我的系统上都可以正常运行。您使用的是哪个版本?是32位还是64位? - jerry

0

在“不要这样做”的线路上,有两个非常重要的相关点值得提出:

  • 不要使用atoi,因为它不安全
  • 不要使用atoi,因为它不是标准函数,而且有好的标准函数(例如snprintf)可用于完成你想要的功能。

但是,暂且不谈这些,我想向您介绍autoconf,GNU构建系统的一部分。autoconf是一个非常全面、非常可移植的工具集,旨在使编写能够在广泛的目标系统上成功构建的代码更加容易。有人会认为,autoconf是一个过于复杂的系统,无法仅通过一个库函数解决您提出的问题,但随着任何程序的增长,它可能会面临更多类似的障碍,现在为您的程序设置autoconf将使您在未来处于更强大的位置。

从一个名为Makefile.in的文件开始,其中包含:

CFLAGS=--ansi --pedantic -Wall -W

program: program.o
program.o: program.c

clean:
  rm -f program.o program

还有一个名为configure.ac的文件,其中包含:

AC_PREREQ([2.69])
AC_INIT(program, 1.0)
AC_CONFIG_SRCDIR([program.c])
AC_CONFIG_HEADERS([config.h])

# Checks for programs.
AC_PROG_CC

# Checks for library functions.
AH_TEMPLATE([HAVE_ITOA], [Set to 1 if function atoi() is available.])
AC_CHECK_FUNC([itoa],
              [AC_DEFINE([HAVE_ITOA], [1])]
              )

AC_CONFIG_FILES([Makefile])
AC_OUTPUT

还有一个名为 program.c 的文件,其中包含:

#include <stdio.h>
#include "config.h"

#ifndef HAVE_ITOA

/*
 * WARNING: This code is for demonstration purposes only. Your
 * implementation must have a way of ensuring that the size of the string
 * produced does not overflow the buffer provided.
 */

void itoa(int n, char* p) {
  sprintf(p, "%d", n);
}
#endif

int main(void) {
  char buffer[100];
  itoa(10, buffer);
  printf("Result: %s\n", buffer);
  return 0;
}

现在依次运行以下命令:

  1. autoheader:这将生成一个名为config.h.in的新文件,我们稍后会用到它。
  2. autoconf:这将生成一个名为configure的配置脚本。
  3. ./configure:这将运行一些测试,包括检查您是否有一个可用的C编译器,并且(因为我们要求它)是否有一个itoa函数可用。它将其结果写入文件config.h以供以后使用。
  4. make:这将编译和链接程序。
  5. ./program:最后运行程序。

./configure步骤中,您将看到相当多的输出,其中包括类似以下内容:

checking for itoa... no

在这种情况下,您会看到config.h文件包含以下行:
/* Set to 1 if function atoi() is available. */
/* #undef HAVE_ITOA */

或者,如果您确实有atoi可用,您将看到:

checking for itoa... yes

并且这段代码在config.h文件中:

/* Set to 1 if function atoi() is available. */
#define HAVE_ITOA 1

你会发现程序现在可以读取config.h头文件并选择定义itoa(如果不存在的话)。

是的,这是一个解决问题的漫长过程,但你现在已经开始使用一个非常强大的工具,它可以在很多方面帮助你。

祝你好运!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接