bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH] bypass broken inline strtod() definition in <stdlib.h> on Mi


From: Bruno Haible
Subject: Re: [PATCH] bypass broken inline strtod() definition in <stdlib.h> on Mingw
Date: Mon, 14 Jun 2010 01:43:09 +0200
User-agent: KMail/1.9.9

Hi Ben,

> The problem is that AC_FUNC_STRTOD assumes that strtod does not
> exist when cross-compiling, which in turn makes the strtod module
> assume that it does not need to handle an existing declaration.

Ah, so AC_FUNC_STRTOD makes gnulib think that strtod() does not exist,
although in fact it does exist. Just because of the worst-case guess
that says "ac_cv_func_strtod=no".

> --- a/m4/strtod.m4
> +++ b/m4/strtod.m4
> @@ -1,4 +1,4 @@
> -# strtod.m4 serial 14
> +# strtod.m4 serial 15
>  dnl Copyright (C) 2002-2003, 2006-2010 Free Software Foundation, Inc.
>  dnl This file is free software; the Free Software Foundation
>  dnl gives unlimited permission to copy and/or distribute it,
> @@ -11,7 +11,13 @@ AC_DEFUN([gl_FUNC_STRTOD],
>    dnl Note: AC_FUNC_STRTOD does AC_LIBOBJ([strtod]).
>    if test $ac_cv_func_strtod = no; then
>      HAVE_STRTOD=0
> -    gl_PREREQ_STRTOD
> +
> +    # AC_FUNC_STRTOD assumes that strtod is missing when cross-compiling,
> +    # but to reliably provide it we need to know whether it is declared.
> +    AC_CHECK_DECLS_ONCE([strtod])
> +    if test "$ac_cv_have_decl_strtod" = yes; then
> +      REPLACE_STRTOD=1
> +    fi
>    else
>      AC_CACHE_CHECK([whether strtod obeys C99], [gl_cv_func_strtod_works],
>        [AC_RUN_IFELSE([AC_LANG_PROGRAM([[
> @@ -77,10 +83,12 @@ numeric_equal (double x, double y)
>          [gl_cv_func_strtod_works="guessing no"])])
>      if test "$gl_cv_func_strtod_works" != yes; then
>        REPLACE_STRTOD=1
> -      gl_PREREQ_STRTOD
> -      dnl Use undocumented macro to set POW_LIB correctly.
> -      _AC_LIBOBJ_STRTOD
> -    fi
> +    fi  
> +  fi
> +  if test $HAVE_STRTOD = 0 || test $REPLACE_STRTOD = 1; then
> +    gl_PREREQ_STRTOD
> +    dnl Use undocumented macro to set POW_LIB correctly.
> +    _AC_LIBOBJ_STRTOD
>    fi
>  ])

This is better. I like about it that it introduces the now common idiom
    if test $HAVE_STRTOD = 0 || test $REPLACE_STRTOD = 1; then

But there are still two problems:

  - In the first branch it can happen that you set HAVE_STRTOD=0 and
    REPLACE_STRTOD=1. The macro should set one or the other, not both.
    Reason: The macros in the generated <stdlib.h> do the wrong thing
    in C++ mode if HAVE_STRTOD = 0 && REPLACE_STRTOD = 1.

  - When we are cross-compiling, we should avoid pessimistic guesses
    such as "ac_cv_func_strtod=no". The reason is that the most frequent
    use of cross-compiling is done for embedded Linux devices (devices
    with BusyBox, or everything-on-a-chip systems). In such cases,
    it is not well seen if gnulib provides a replacement function when
    in fact it is not needed at all. So, please, when cross-compiling,
    give a thought to the embedded Linux case (with glibc or uClibc).

    So: Do current embedded Linuxes have the strtod bugs or not? Take a
    look at the AC_FUNC_STRTOD macro in autoconf/lib/autoconf/functions.m4:

7bd04c77        (Akim Demaille  2000-08-01 11:16:33 +0000       1537)# 
AC_FUNC_STRTOD
7bd04c77        (Akim Demaille  2000-08-01 11:16:33 +0000       1538)# 
--------------
7134f17d        (Akim Demaille  2002-09-28 14:06:07 +0000       
1539)AN_FUNCTION([strtod], [AC_FUNC_STRTOD])
7bd04c77        (Akim Demaille  2000-08-01 11:16:33 +0000       
1540)AC_DEFUN([AC_FUNC_STRTOD],
1492d7c5        (Akim Demaille  2001-11-12 15:47:35 +0000       
1541)[AC_SUBST(POW_LIB)dnl
1492d7c5        (Akim Demaille  2001-11-12 15:47:35 +0000       
1542)AC_CACHE_CHECK(for working strtod, ac_cv_func_strtod,
6a2a74c2        (Akim Demaille  2001-07-04 14:37:52 +0000       
1543)[AC_RUN_IFELSE([AC_LANG_SOURCE([[
11ee0ab5        (Paul Eggert    2004-05-03 20:15:44 +0000       
1544)]AC_INCLUDES_DEFAULT[
11ee0ab5        (Paul Eggert    2004-05-03 20:15:44 +0000       1545)#ifndef 
strtod
7bd04c77        (Akim Demaille  2000-08-01 11:16:33 +0000       1546)double 
strtod ();
11ee0ab5        (Paul Eggert    2004-05-03 20:15:44 +0000       1547)#endif
7bd04c77        (Akim Demaille  2000-08-01 11:16:33 +0000       1548)int
7bd04c77        (Akim Demaille  2000-08-01 11:16:33 +0000       1549)main()
7bd04c77        (Akim Demaille  2000-08-01 11:16:33 +0000       1550){
7bd04c77        (Akim Demaille  2000-08-01 11:16:33 +0000       1551)  {
7bd04c77        (Akim Demaille  2000-08-01 11:16:33 +0000       1552)    /* 
Some versions of Linux strtod mis-parse strings with leading '+'.  */
7bd04c77        (Akim Demaille  2000-08-01 11:16:33 +0000       1553)    char 
*string = " +69";
7bd04c77        (Akim Demaille  2000-08-01 11:16:33 +0000       1554)    char 
*term;
7bd04c77        (Akim Demaille  2000-08-01 11:16:33 +0000       1555)    double 
value;
7bd04c77        (Akim Demaille  2000-08-01 11:16:33 +0000       1556)    value 
= strtod (string, &term);
7bd04c77        (Akim Demaille  2000-08-01 11:16:33 +0000       1557)    if 
(value != 69 || term != (string + 4))
a71c24a7        (Paul Eggert    2006-04-03 03:18:39 +0000       1558)      
return 1;
7bd04c77        (Akim Demaille  2000-08-01 11:16:33 +0000       1559)  }
7bd04c77        (Akim Demaille  2000-08-01 11:16:33 +0000       1560)
7bd04c77        (Akim Demaille  2000-08-01 11:16:33 +0000       1561)  {
7bd04c77        (Akim Demaille  2000-08-01 11:16:33 +0000       1562)    /* 
Under Solaris 2.4, strtod returns the wrong value for the
7bd04c77        (Akim Demaille  2000-08-01 11:16:33 +0000       1563)       
terminating character under some conditions.  */
7bd04c77        (Akim Demaille  2000-08-01 11:16:33 +0000       1564)    char 
*string = "NaN";
7bd04c77        (Akim Demaille  2000-08-01 11:16:33 +0000       1565)    char 
*term;
7bd04c77        (Akim Demaille  2000-08-01 11:16:33 +0000       1566)    strtod 
(string, &term);
7bd04c77        (Akim Demaille  2000-08-01 11:16:33 +0000       1567)    if 
(term != string && *(term - 1) == 0)
a71c24a7        (Paul Eggert    2006-04-03 03:18:39 +0000       1568)      
return 1;
7bd04c77        (Akim Demaille  2000-08-01 11:16:33 +0000       1569)  }
a71c24a7        (Paul Eggert    2006-04-03 03:18:39 +0000       1570)  return 0;
7bd04c77        (Akim Demaille  2000-08-01 11:16:33 +0000       1571)}
6a2a74c2        (Akim Demaille  2001-07-04 14:37:52 +0000       1572)]])],
b44e483c        (Akim Demaille  2003-05-17 10:30:07 +0000       1573)          
ac_cv_func_strtod=yes,
b44e483c        (Akim Demaille  2003-05-17 10:30:07 +0000       1574)          
ac_cv_func_strtod=no,
b44e483c        (Akim Demaille  2003-05-17 10:30:07 +0000       1575)          
ac_cv_func_strtod=no)])
7bd04c77        (Akim Demaille  2000-08-01 11:16:33 +0000       1576)if test 
$ac_cv_func_strtod = no; then
7bd04c77        (Akim Demaille  2000-08-01 11:16:33 +0000       1577)  
_AC_LIBOBJ_STRTOD
7bd04c77        (Akim Demaille  2000-08-01 11:16:33 +0000       1578)fi
7bd04c77        (Akim Demaille  2000-08-01 11:16:33 +0000       1579)])

    With two old old old tests and no reasonable cross-compiling behaviour I
    would say that the best thing to do is to move these two blocks of C code
    into gl_FUNC_STRTOD's test, and stop using AC_FUNC_STRTOD. Then the
    autoconf documentation can mark this macro obsolescent, and on the gnulib
    side this test runs one program, not two, and we have full control over
    the cross-compilation behaviour.

So, I would propose
  1) to apply the patch below,
  2) to use the common idiom
        if test $HAVE_STRTOD = 0 || test $REPLACE_STRTOD = 1; then
     like you did above,
  3) to improve the cross-compiling guess.

Bruno


2010-06-13  Bruno Haible  <address@hidden>

        * m4/strtod.m4 (gl_FUNC_STRTOD): Stop using AC_FUNC_STRTOD.

--- m4/strtod.m4.orig   Mon Jun 14 01:40:50 2010
+++ m4/strtod.m4        Mon Jun 14 01:38:49 2010
@@ -1,4 +1,4 @@
-# strtod.m4 serial 14
+# strtod.m4 serial 15
 dnl Copyright (C) 2002-2003, 2006-2010 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -7,11 +7,17 @@
 AC_DEFUN([gl_FUNC_STRTOD],
 [
   AC_REQUIRE([gl_STDLIB_H_DEFAULTS])
-  AC_FUNC_STRTOD
-  dnl Note: AC_FUNC_STRTOD does AC_LIBOBJ([strtod]).
-  if test $ac_cv_func_strtod = no; then
+  dnl Test whether strtod is declared.
+  dnl Don't call AC_FUNC_STRTOD, because it does not have the right guess
+  dnl when cross-compiling.
+  dnl Don't call AC_CHECK_FUNCS([strtod]) because it would collide with the
+  dnl ac_cv_func_strtod variable set by the AC_FUNC_STRTOD macro,
+  AC_CHECK_DECLS_ONCE([strtod])
+  if test $ac_cv_have_decl_strtod != yes; then
     HAVE_STRTOD=0
     gl_PREREQ_STRTOD
+    dnl Use undocumented macro to set POW_LIB correctly.
+    _AC_LIBOBJ_STRTOD
   else
     AC_CACHE_CHECK([whether strtod obeys C99], [gl_cv_func_strtod_works],
       [AC_RUN_IFELSE([AC_LANG_PROGRAM([[
@@ -28,6 +34,24 @@
 }
 ]], [[
   {
+    /* In some old versions of Linux (2000 or before), strtod mis-parses
+       strings with leading '+'.  */
+    const char *string = " +69";
+    char *term;
+    double value = strtod (string, &term);
+    if (value != 69 || term != (string + 4))
+      return 1;
+  }
+  {
+    /* Under Solaris 2.4, strtod returns the wrong value for the
+       terminating character under some conditions.  */
+    const char *string = "NaN";
+    char *term;
+    strtod (string, &term);
+    if (term != string && *(term - 1) == 0)
+      return 1;
+  }
+  {
     /* Older glibc and Cygwin mis-parse "-0x".  */
     const char *string = "-0x";
     char *term;





reply via email to

[Prev in Thread] Current Thread [Next in Thread]