Discussion:
C++ aliases in <netdb.h>
(too old to reply)
Gisle Vanem
2016-12-15 12:11:12 UTC
Permalink
Raw Message
I get errors from MSVC in <netdb.h> i C++ mode. E.g. in compiling
test-netdb-c++.cc:

netdb.h(189): error C2440: 'return': cannot convert
from 'INT (__stdcall *)(PCSTR,PCSTR,const ADDRINFOA *,PADDRINFOA *)' to
'gnulib_::_gl_getaddrinfo_wrapper::type'

Ditto error for '_gl_freeaddrinfo_wrapper'.

Some of the pre-processed output of the _GL_CXXALIAS_SYS() macro at
line 189 in netdb.h is:
namespace gnulib_ {
static const struct _gl_getaddrinfo_wrapper {
typedef int (*type) (const char * nodename,
const char * servname,
const struct addrinfo * hints,
struct addrinfo ** res);
__inline operator type () const
{
return ::getaddrinfo; << !! error is here
}
}
getaddrinfo = {};
}

Instead this requires a 'reinterpret_cast<type>'.
Hence with this patch, it compiles and runs fine:

--- a/netdb.in.h 2016-01-30 20:42:17
+++ b/netdb.in.h 2016-12-15 12:53:28
@@ -170,7 +170,7 @@
struct addrinfo **restrict res)
_GL_ARG_NONNULL ((4)));
# endif
-_GL_CXXALIAS_SYS (getaddrinfo, int,
+_GL_CXXALIAS_SYS_CAST (getaddrinfo, int,
(const char *restrict nodename,
const char *restrict servname,
const struct addrinfo *restrict hints,
@@ -184,7 +184,7 @@
_GL_FUNCDECL_SYS (freeaddrinfo, void, (struct addrinfo *ai)
_GL_ARG_NONNULL ((1)));
# endif
-_GL_CXXALIAS_SYS (freeaddrinfo, void, (struct addrinfo *ai));
+_GL_CXXALIAS_SYS_CAST (freeaddrinfo, void, (struct addrinfo *ai));
_GL_CXXALIASWARN (freeaddrinfo);

# if @REPLACE_GAI_STRERROR@

---------

This is because of Winsock's __stdcall I assume?

I've not checked all test-*.cc files for such errors.
--
--gv
Gisle Vanem
2016-12-15 13:13:30 UTC
Permalink
Raw Message
Post by Gisle Vanem
I've not checked all test-*.cc files for such errors.
Another issue with sys_select.in.h:
test-sys_select-c++.cc
sys/select.h(305): error C2440: 'return': cannot convert from
'int (__cdecl *)(int,fd_set *,fd_set *,fd_set *,timeval *)' to
'gnulib_::_gl_select_wrapper::type'
sys/select.h(305): note: This conversion requires a reinterpret_cast, a C-style cast or function-style cast

But I fail to resolve this one.
--
--gv
Pedro Alves
2016-12-16 18:27:56 UTC
Permalink
Raw Message
Post by Gisle Vanem
Post by Gisle Vanem
I've not checked all test-*.cc files for such errors.
test-sys_select-c++.cc
sys/select.h(305): error C2440: 'return': cannot convert from
'int (__cdecl *)(int,fd_set *,fd_set *,fd_set *,timeval *)' to
'gnulib_::_gl_select_wrapper::type'
sys/select.h(305): note: This conversion requires a reinterpret_cast, a C-style cast or function-style cast
But I fail to resolve this one.
Could it be due to a mismatch of "timeval" types? Note there's
the system struct timeval type, and then there's the typedef in
the gnulib namespace, that will point to the rpl_timeval replacement
type on Windows.

Thanks,
Pedro Alves
Pedro Alves
2016-12-16 18:24:35 UTC
Permalink
Raw Message
Post by Gisle Vanem
I get errors from MSVC in <netdb.h> i C++ mode. E.g. in compiling
netdb.h(189): error C2440: 'return': cannot convert
from 'INT (__stdcall *)(PCSTR,PCSTR,const ADDRINFOA *,PADDRINFOA *)' to
'gnulib_::_gl_getaddrinfo_wrapper::type'
Ditto error for '_gl_freeaddrinfo_wrapper'.
Some of the pre-processed output of the _GL_CXXALIAS_SYS() macro at
namespace gnulib_ {
static const struct _gl_getaddrinfo_wrapper {
typedef int (*type) (const char * nodename,
const char * servname,
const struct addrinfo * hints,
struct addrinfo ** res);
__inline operator type () const
{
return ::getaddrinfo; << !! error is here
}
}
getaddrinfo = {};
}
Instead this requires a 'reinterpret_cast<type>'.
Can't see how that can run fine? The compiler will set up the call
assuming cdecl convention, while the called function has stdcall
convention.
Post by Gisle Vanem
--- a/netdb.in.h 2016-01-30 20:42:17
+++ b/netdb.in.h 2016-12-15 12:53:28
@@ -170,7 +170,7 @@
struct addrinfo **restrict res)
_GL_ARG_NONNULL ((4)));
# endif
-_GL_CXXALIAS_SYS (getaddrinfo, int,
+_GL_CXXALIAS_SYS_CAST (getaddrinfo, int,
(const char *restrict nodename,
const char *restrict servname,
const struct addrinfo *restrict hints,
@@ -184,7 +184,7 @@
_GL_FUNCDECL_SYS (freeaddrinfo, void, (struct addrinfo *ai)
_GL_ARG_NONNULL ((1)));
# endif
-_GL_CXXALIAS_SYS (freeaddrinfo, void, (struct addrinfo *ai));
+_GL_CXXALIAS_SYS_CAST (freeaddrinfo, void, (struct addrinfo *ai));
_GL_CXXALIASWARN (freeaddrinfo);
---------
This is because of Winsock's __stdcall I assume?
I assume so.

m4/inet_pton.m4 has this:

dnl Most platforms that provide inet_pton define it in libc.
dnl Solaris 8..10 provide inet_pton in libnsl instead.
dnl Solaris 2.6..7 provide inet_pton in libresolv instead.
dnl Native Windows provides it in -lws2_32 instead, with a declaration in
dnl <ws2tcpip.h>, and it uses stdcall calling convention, not cdecl
dnl (hence we cannot use AC_CHECK_FUNCS, AC_SEARCH_LIBS to find it).
HAVE_INET_PTON=1
INET_PTON_LIB=
gl_PREREQ_SYS_H_WINSOCK2
if test $HAVE_WINSOCK2_H = 1; then
AC_CHECK_DECLS([inet_pton],,, [[#include <ws2tcpip.h>]])
if test $ac_cv_have_decl_inet_pton = yes; then
dnl It needs to be overridden, because the stdcall calling convention
dnl is not compliant with POSIX.
REPLACE_INET_PTON=1
INET_PTON_LIB="-lws2_32"
else
HAVE_DECL_INET_PTON=0
HAVE_INET_PTON=0
fi
else


Given the "stdcall calling convention is not compliant with POSIX",
I wonder whether the right fix would be to somehow cause those
functions to be replaced too?

Are all MSVC C run time functions __stdcall, or just a few?
It's been a long while since I used MSVC.

I suppose a possible fix would be to change from using
a conversion operator to a function call operator. Like:

inline rettype operator() parameters \
{ \
return ::func arguments; \
} \


But, then you'd need to tweak the _GL_CXXALIAS_SYS macro too,
to pass down the right "arguments", since that "arguments"
doesn't exist today. Like:

-_GL_CXXALIAS_SYS (freeaddrinfo, void, (struct addrinfo *ai))
+_GL_CXXALIAS_SYS (freeaddrinfo, void, (struct addrinfo *ai), (ai))

Thanks,
Pedro Alves
Gisle Vanem
2016-12-17 09:32:57 UTC
Permalink
Raw Message
Post by Pedro Alves
Can't see how that can run fine? The compiler will set up the call
assuming cdecl convention, while the called function has stdcall
convention.
I would expect the 'reinterpret_cast<type>(::getaddrinfo)' to fix that.
With this little example:
#include <config.h>
#include <netdb.h>
int main (void)
{
struct addrinfo *res;
gnulib_::getaddrinfo ("www.google.com", "http", NULL, &res);
return (0);
}

A dis-assembly shows:
.rdata, "dr2"
?***@gnulib_@@***@1@B: 00 00 00 00
$SG37638: "http"
$SG37639: "www.google.com"

.text, "crx4"
_main:
push ebp
mov ebp,esp
push ecx
lea eax,-0x4[ebp]
push eax
push 0x00000000
push $SG37638
push $SG37639
mov ecx,?***@gnulib_@@***@1@B
call ??***@gnulib_@@QBEP6AHPBD0PBUaddrinfo@@PAPAU2@@ZXZ
call eax
add esp,0x00000010
xor eax,eax
mov esp,ebp
pop ebp
ret

??***@gnulib_@@QBEP6AHPBD0PBUaddrinfo@@PAPAU2@@ZXZ:
push ebp
mov ebp,esp
push ecx
mov dword ptr -0x4[ebp],ecx
mov eax,dword ptr ***@16
mov esp,ebp
pop ebp
ret


Running the program shows that '***@16' gets the arguments
in the right order. So '__stdcall' must be in effect.
Post by Pedro Alves
Are all MSVC C run time functions __stdcall, or just a few?
It's been a long while since I used MSVC.
getaddrinfo() etc. is not a 'run time function' but a SDK functions.
Almost all functions in the Windows SDK are __stdcall. The runtime
functions are __cdecl.
--
--gv
Bruno Haible
2016-12-18 00:51:01 UTC
Permalink
Raw Message
Post by Gisle Vanem
Post by Pedro Alves
Can't see how that can run fine? The compiler will set up the call
assuming cdecl convention, while the called function has stdcall
convention.
I would expect the 'reinterpret_cast<type>(::getaddrinfo)' to fix that.
But this will cause undefined behaviour. reinterpret_cast is really the most
dangerous cast that exist.

MSVC gives an error from implicit type conversion from __cdecl to __stdcall:

/home/bruno/msvc/compile cl -nologo -DHAVE_CONFIG_H -DEXEEXT=\".exe\" -I. -I../../gltests -I.. -DGNULIB_STRICT_CHECKING=1 -DIN_GNULIB_TESTS=1 -I. -I../../gltests -I.. -I../../gltests/.. -I../gllib -I../../gltests/../gllib -D_WIN32_WINNT=_WIN32_WINNT_WINXP -I/usr/local/msvc32/include -MD -c -o test-getaddrinfo.obj `cygpath -w '../../gltests/test-getaddrinfo.c'`
test-getaddrinfo.c
C:\cygwin64\home\bruno\testdir-posix\gltests\test-getaddrinfo.c(25): error C2440: 'initializing': cannot convert from 'void (__stdcall *)(PADDRINFOA)' to 'void (__cdecl *)(addrinfo *)'
C:\cygwin64\home\bruno\testdir-posix\gltests\test-getaddrinfo.c(27): error C2440: 'initializing': cannot convert from 'INT (__stdcall *)(PCSTR,PCSTR,const ADDRINFOA *,PADDRINFOA *)' to 'int (__cdecl *)(const char *,const char *,const addrinfo *,addrinfo **)'
make[4]: *** [Makefile:8042: test-getaddrinfo.obj] Error 2

Therefore you cannot assume that these can be used interchangeably.
They wouldn't give an error if such a conversion was a harmless no-op.

Meanwhile, I'm fixing this compilation error by disabling the signature check
(like we do for functions that are actually defined as macros, e.g. in
test-strtol.c).


2016-12-17 Bruno Haible <***@clisp.org>

getaddrinfo tests: Avoid compilation error on MSVC.
* tests/test-getaddrinfo.c: Don't check the prototypes of freeaddrinfo,
getaddrinfo on native Windows.

diff --git a/tests/test-getaddrinfo.c b/tests/test-getaddrinfo.c
index 0251632..f7d4caa 100644
--- a/tests/test-getaddrinfo.c
+++ b/tests/test-getaddrinfo.c
@@ -22,11 +22,16 @@
#include <netdb.h>

#include "signature.h"
-SIGNATURE_CHECK (freeaddrinfo, void, (struct addrinfo *));
SIGNATURE_CHECK (gai_strerror, char const *, (int));
+/* On native Windows, these two functions may have the __stdcall calling
+ convention. But the SIGNATURE_CHECK works only for functions with __cdecl
+ calling convention. */
+#if !((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__)
+SIGNATURE_CHECK (freeaddrinfo, void, (struct addrinfo *));
SIGNATURE_CHECK (getaddrinfo, int, (char const *, char const *,
struct addrinfo const *,
struct addrinfo **));
+#endif

#include <arpa/inet.h>
#include <errno.h>
Bruno Haible
2016-12-18 01:16:39 UTC
Permalink
Raw Message
Post by Pedro Alves
if test $ac_cv_have_decl_inet_pton = yes; then
dnl It needs to be overridden, because the stdcall calling convention
dnl is not compliant with POSIX.
REPLACE_INET_PTON=1
INET_PTON_LIB="-lws2_32"
else
HAVE_DECL_INET_PTON=0
HAVE_INET_PTON=0
fi
else
Given the "stdcall calling convention is not compliant with POSIX",
I wonder whether the right fix would be to somehow cause those
functions to be replaced too?
From the point of C programs, replacing 'getaddrinfo' and 'freeaddrinfo'
is overkill, IMO. Most programs just call these functions the way they are
declared in the .h file, through direct call, not through function pointers.

I just added some similar replacements for <math.h> functions that are defined
as inline functions by MSVC. The justification is that <math.h> functions are
occasionally called through function pointers (e.g. you can imagine a 'plot'
function, an 'integrate' function, etc. that takes a float->float function
as a pointer).

From the point of C++ programs:
- if you get an error in the .h file, from the _GL_CXXALIAS_SYS or
_GL_CXXALIASWARN invocation for example, or for GNULIB_NAMESPACE,
it's worth providing the replacement.
- if you only get an error in a SIGNATURE_CHECK, it's probably not worth it.
Just conditionalize the the SIGNATURE_CHECK then.
But if you want to provide the fix through REPLACE_xxx=1, I won't stop you.

Bruno

Loading...