有没有办法包含 stdio.h 但忽略其中的某些功能?

Is there a way to include stdio.h but ignore some of the functions therein?

让我先声明这个问题,我几乎是一个编程新手,并且在 C 或 GNU 环境中都没有受过良好的训练。另外,我真的无法提供可重现的示例,因为我还没有编写这段代码,我只是在尝试构建它。我已经自学了足够的东西来编译一些程序,给出了一个很好的预先存在的 makefile,并且多次使用 Rtools 为 Windows 编译了 R。

我一直在尝试使用 GCC 4.8.4 为 Windows(7 64 位)编译 R(Rtools 仍然存在于 4.6.3 预发布版中,并且 4.9 中的链接时优化已经复活 some old bugs), and can do so with one problem. In one of the files (/src/extra/trio/compat.c 准确地说)既包含 stdio.h 又包含 sprintfvsprintf 的定义。因此,构建崩溃并出现以下错误:

In file included from compat.c:3:0:
F:/MinGW64/x86_64-w64-mingw32/include/stdio.h:553:5: note: previous
definition of 'snprintf' was here
 int snprintf (char * __restrict__ __stream, size_t __n, const char *
__restrict__ __format, ...)
     ^
compat.c:75:5: error: redefinition of 'vsnprintf'
 int vsnprintf(char *buffer, size_t bufferSize, const char *format,
va_list args)
     ^
In file included from compat.c:3:0:
F:/MinGW64/x86_64-w64-mingw32/include/stdio.h:543:7: note: previous
definition of 'vsnprintf' was here
   int vsnprintf (char * __restrict__ __stream, size_t __n, const char
* __restrict__ __format, va_list __local_argv)
       ^
../../gnuwin32/MkRules:218: recipe for target 'compat.o' failed
make[4]: *** [compat.o] Error 1
Makefile:120: recipe for target 'rlibs' failed
make[3]: *** [rlibs] Error 1
Makefile:179: recipe for target '../../bin/x64/R.dll' failed
make[2]: *** [../../bin/x64/R.dll] Error 2
Makefile:104: recipe for target 'rbuild' failed
make[1]: *** [rbuild] Error 2
Makefile:14: recipe for target 'all' failed
make: *** [all] Error 2

compat.c中的行是65-79中的行:

int snprintf(char *buffer, size_t max, const char *format, ...)
{
    int res;
    va_list(ap);
    va_start(ap, format);
    res = trio_vsnprintf(buffer, max, format, ap);
    va_end(ap);
    return res;
}

int vsnprintf(char *buffer, size_t bufferSize, const char *format, va_list args)
{
    return trio_vsnprintf(buffer, bufferSize, format, args);
}

如果我删除这些行,构建完成,但 R 输出有一个三位十进制科学记数法指数而不是两位数,(例如“3.11e-004”而不是“3.11e-04”)这会导致各种检查崩溃。 I have been informed 这是标准的 Windows 方法,所以我删除本地 R 版本几乎肯定会导致这个问题。

我的问题是,有什么方法可以继续将 stdio.h 包含在 compat.c 文件中,同时将 snpritfvsnprintf 重新定义为compat.c?

中的本地

谢谢。

尝试 1 失败

我尝试添加直接 #undef 声明以及

#ifdef snprintf
#undef snprintf
#endif

和 compat.c 中对应的 vsnprintf,但均无效。由于“重新定义”,我遇到相同的构建错误而崩溃。

尝试 2 失败

将 compat.c 中的代码更改为:

#ifdef snprintf
#undef snprintf
int snprintf(char *buffer, size_t max, const char *format, ...)
{
    int res;
    va_list(ap);
    va_start(ap, format);
    res = trio_vsnprintf(buffer, max, format, ap);
    va_end(ap);
    return res;
}
#endif

允许程序编译,但它有相同的符号错误,这意味着它在功能上等同于只删除这些行。

根据评论请求更新

  1. 安装 Mingw-64 的一些风格并将 \bin 放在 PATH 的开头
  2. 安装 MSYS2:基本安装,然后添加 tar、make、zlib、zip、unzip 和 rsync,并将其 bin 放在路径的第二个
  3. Untar R-3.1.2.tar.gz(两次,作为第一次 returns 使用来自 MSYS2 的 tar 1.28 的 simlink 错误。使用修补的 tar 来自 Rtolls 的 1.2.1 没有 return 错误)
  4. 将 R64 (Rtools) 中的 Tcl 和 bitmapdll 子目录复制到适当的位置
  5. 修改MkRules.dist强制执行64位,Windows64平台,HTML帮助,以及Cairo、Inno和qpdf的正确目录,并将其另存为MkRules.local.对于这些测试,我没有使用基于 OpenBLAS 的 Rblas,除了为速度
  6. 添加 -pipe 之外,没有任何特定于处理器的 EOPTS 调用
  7. 运行make all

我已经使用 Rtools 中的二进制文件多次构建 R,包括使用 OpenBLAS(愚弄 R 以为它是 ATLAS)。我注意到但不明白的是 stdio.h 在 GCC 4.7(因此是 4.6)和 4.8 及后续版本之间发生了变化。包含错误中抛出的行号的新版本直接定义了 snprintfvsnprintf。 4.6.3 的版本没有。我复制了以下部分:

stdio.h 来自 GCC 4.6.3 第 494–514 行:

#if !defined (__USE_MINGW_ANSI_STDIO) || __USE_MINGW_ANSI_STDIO == 0
/* this is here to deal with software defining
 * vsnprintf as _vsnprintf, eg. libxml2.  */
#pragma push_macro("snprintf")
#pragma push_macro("vsnprintf")
# undef snprintf
# undef vsnprintf
  int __cdecl vsnprintf(char * __restrict__ d,size_t n,const char * __restrict__ format,va_list arg)
    __MINGW_ATTRIB_DEPRECATED_MSVC2005 __MINGW_ATTRIB_DEPRECATED_SEC_WARN;

#ifndef __NO_ISOCEXT
  int __cdecl snprintf(char * __restrict__ s, size_t n, const char * __restrict__  format, ...);
#ifndef __CRT__NO_INLINE
  __CRT_INLINE int __cdecl vsnprintf(char * __restrict__ d,size_t n,const char * __restrict__ format,va_list arg)
  {
    return _vsnprintf (d, n, format, arg);
  }
#endif /* !__CRT__NO_INLINE */
#endif /* !__NO_ISOCEXT */
#pragma pop_macro ("vsnprintf")
#pragma pop_macro ("snprintf")
#endif

stdio.h 来自 GCC 4.9.2(与 4.8.4 相同)第 531–565 行:

#if !defined (__USE_MINGW_ANSI_STDIO) || __USE_MINGW_ANSI_STDIO == 0
/* this is here to deal with software defining
 * vsnprintf as _vsnprintf, eg. libxml2.  */
#pragma push_macro("snprintf")
#pragma push_macro("vsnprintf")
# undef snprintf
# undef vsnprintf
  int __cdecl __ms_vsnprintf(char * __restrict__ d,size_t n,const char * __restrict__ format,va_list arg)
    __MINGW_ATTRIB_DEPRECATED_MSVC2005 __MINGW_ATTRIB_DEPRECATED_SEC_WARN;

  __mingw_ovr
  __MINGW_ATTRIB_NONNULL(3)
  int vsnprintf (char * __restrict__ __stream, size_t __n, const char * __restrict__ __format, va_list __local_argv)
  {
    return __ms_vsnprintf (__stream, __n, __format, __local_argv);
  }

  int __cdecl __ms_snprintf(char * __restrict__ s, size_t n, const char * __restrict__  format, ...);

#ifndef __NO_ISOCEXT
__mingw_ovr
__MINGW_ATTRIB_NONNULL(3)
int snprintf (char * __restrict__ __stream, size_t __n, const char * __restrict__ __format, ...)
{
  register int __retval;
  __builtin_va_list __local_argv; __builtin_va_start( __local_argv, __format );
  __retval = __ms_vsnprintf (__stream, __n, __format, __local_argv);
  __builtin_va_end( __local_argv );
  return __retval;
}
#endif /* !__NO_ISOCEXT */

#pragma pop_macro ("vsnprintf")
#pragma pop_macro ("snprintf")
#endif

正如您所观察到的,compat.c 中引入的 MinGW GCC 4.7 中的编译错误是由于 通过在 stdio.h 中新包含 vsprintfsnprintf(以及其他)的内联定义。 这打破了之前的代码,比如 compat.c 拒绝了标准库的 以前是外部定义并提供自己的定义。

您可以根据问题函数恢复原状,这取决于预处理器的定义 宏,即只有适当定义宏的客户端代码才能在 状态下编译该函数 从前;其他代码将在现状.

下编译它

要做到这一点,您必须对问题进行一些预处理 header F:/MinGW64/x86_64-w64-mingw32/include/stdio.h.

替换[541-560]行,即:

  __mingw_ovr
  __MINGW_ATTRIB_NONNULL(3)
  int vsnprintf (char * __restrict__ __stream, size_t __n, const char * __restrict__ __format, va_list __local_argv)
  {
    return __ms_vsnprintf (__stream, __n, __format, __local_argv);
  }

  int __cdecl __ms_snprintf(char * __restrict__ s, size_t n, const char * __restrict__  format, ...);

#ifndef __NO_ISOCEXT
__mingw_ovr
__MINGW_ATTRIB_NONNULL(3)
int snprintf (char * __restrict__ __stream, size_t __n, const char * __restrict__ __format, ...)
{
  register int __retval;
  __builtin_va_list __local_argv; __builtin_va_start( __local_argv, __format );
  __retval = __ms_vsnprintf (__stream, __n, __format, __local_argv);
  __builtin_va_end( __local_argv );
  return __retval;
} 

与:

#if NO_INLINE_VSNPRINTF == 0
  __mingw_ovr
  __MINGW_ATTRIB_NONNULL(3)
  int vsnprintf (char * __restrict__ __stream, size_t __n, const char * __restrict__ __format, va_list __local_argv)
  {
    return __ms_vsnprintf (__stream, __n, __format, __local_argv);
  }
#else
  extern int vsnprintf (char * __restrict__ __stream, size_t __n, const char * __restrict__ __format, va_list __local_argv);
#endif`

  int __cdecl __ms_snprintf(char * __restrict__ s, size_t n, const char * __restrict__  format, ...);

#ifndef __NO_ISOCEXT
#if NO_INLINE_SNPRINTF == 0
__mingw_ovr
__MINGW_ATTRIB_NONNULL(3)
int snprintf (char * __restrict__ __stream, size_t __n, const char * __restrict__ __format, ...)
{
  register int __retval;
  __builtin_va_list __local_argv; __builtin_va_start( __local_argv, __format );
  __retval = __ms_vsnprintf (__stream, __n, __format, __local_argv);
  __builtin_va_end( __local_argv );
  return __retval;
}
#else
extern int snprintf (char * __restrict__ __stream, size_t __n, const char * __restrict__ __format, ...);
#endif

(注意保留 #endif,最初在第 561 行,紧跟在结束此 hack 的那个之后。)

然后,编辑 compat.c 并在顶部右侧 before:

#include <stdio.h>

添加行:

#define NO_INLINE_VSNPRINTF 1
#define NO_INLINE_SNPRINTF 1

(这两个宏没有约定俗成的意思,我自己造的。)

保存这些更改后,compat.c 将无错误地编译。至少对我来说是这样。

我还没有解决 R 的完整 Windows 构建的问题,因此您可能会遇到更多问题 破损。如果有同类的话,解法是这样的:如果一个header #includefoo.c 编辑的文件 header.h 提供了一个内联函数定义 形式:

__some_decorator
[__maybe_some_more_decorators...]
some_return_type func_name(some arg_type arg0[,some_other_arg_type arg1...])
{
    ....
}

然后在 header.h 中将其替换为:

#if NO_INLINE_FUNC_NAME == 0
__some_decorator
[__maybe_some_more_decorators...]
some_return_type func_name(some arg_type arg0[,some_other_arg_type arg1...])
{
    ....
}
#else
extern some_return_type func_name(some arg_type arg0[,some_other_arg_type arg1...]);
#endif

并在 foo.c 中,就在 #include <header.h> 之前插入:

#define NO_INLINE_FUNC_NAME 1