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

时间:2015-01-09 03:04:10

标签: r stdio gcc4.8

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

我一直在尝试使用GCC 4.8.4编译R for Windows(7 64位)(Rtools仍然存在于4.6.3预发布中,4.9中的链接时优化已经复活some {{3 }} old),并且可以解决一个问题。在其中一个文件中(确切地说bugs)既包含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")导致各种检查崩溃。 /src/extra/trio/compat.c这是标准的Windows方法,因此删除本地R版本几乎肯定会导致此问题。

我的问题是,有什么方法可以继续将compd.c包含在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(两次,因为第一次使用来自MSYS2的tar 1.28返回simlink错误。使用Rtolls中的patched tar 1.2.1不会返回错误)
  4. 将R64(Rtools)中的Tcl和bitmapdll子目录复制到适当的位置
  5. 修改MkRules.dist以强制执行64位,Windows 64平台,HTML帮助以及Cairo,Inno和qpdf的正确目录,并将其另存为MkRules.local。对于这些测试,我没有使用基于OpenBLAS的Rblas,除了为速度添加-pipe之外没有任何特定于处理器的EOPTS调用
  6. 运行make all
  7. 我使用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
    
    来自GCC 4.9.2的stdio.h(与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
    

1 个答案:

答案 0 :(得分:3)

正如您所观察到的,导致MinGW GCC 4.7引入的compat.c中的编译错误 通过在vsprintf中新添加snprintfstdio.h(以及其他)的内联定义。 这会破坏之前的代码,例如拒绝标准库的compat.c 以前的外部定义并提供自己的定义。

根据预处理器的定义,您可以根据问题函数恢复现状的恢复 宏,即仅适当定义宏的客户端代码将在状态下编译该函数 quo ante ;其他代码将在现状下编译它。

要做到这一点,你必须对问题标题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

(注意保留最初在第561行的#endif,紧跟结束此黑客的那个。{/ p>

然后,在之前编辑compat.c并在顶部右侧

#include <stdio.h>

添加以下行:

#define NO_INLINE_VSNPRINTF 1
#define NO_INLINE_SNPRINTF 1

(这两个宏没有传统意义。我只是创造了它们。)

保存这些更改后,compat.c将无错误地编译。至少它对我有用。

我没有遇到过R的完整Windows版本的麻烦,所以你可能会遇到更多问题 破损。如果有任何相同的解决方案,解决方案的模式是:如果是标题 由header.h编辑的#include文件foo.c提供内联函数定义 形式:

__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