lmi
[Top][All Lists]
Advanced

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

[lmi] MinGW 3.4.5, stdio formatting functions and multiple definitions


From: Vadim Zeitlin
Subject: [lmi] MinGW 3.4.5, stdio formatting functions and multiple definitions
Date: Sun, 7 Dec 2014 00:00:43 +0100

 Hello,

 There is a problem with the use of std::sprintf() and other formatting
functions with MinGW 3.4.5. There is a comment obliquely referring to this
problem in workhorse.make:

        # This library also seems to require
        #   -Wl,--allow-multiple-definition
        # for reasons unknown, at least with MinGW gcc-3.4.5 .

and I'd like to explain (to some degree) these reasons as well as discuss
how could we avoid them.

 First, let me illustrate the problem with the simplest possible example I
could find:

        % car bar.cpp
        zsh: correct 'car' to 'cat' [nyae]? e
        % cat bar.cpp
        #include <cstdio>

        void another()
        {
            char buf[16];
            std::sprintf(buf, "%d", 17);
        }
        % cat foo.cpp
        #include <cstdio>

        int main()
        {
            char buf[16];
            return std::sprintf(buf, "%d", 17);
        }
        [twilight7:/tmp]% /MinGW_/bin/g++ -posix bar.cpp foo.cpp
        C:\cygwin-lmi\tmp/ccJtbfDa.o:foo.cpp:(.text+0x48): multiple definition 
of `sprintf'
        C:\cygwin-lmi\tmp/ccODeztN.o:bar.cpp:(.text+0x24): first defined here
        collect2: ld returned 1 exit status

IOW, std::sprintf() can't be used in more than one file in the entire
program when its MinGW-specific version is used. The choice of MinGW
version, rather than Microsoft one, used by default, is due to the use of
"-posix", as per http://article.gmane.org/gmane.comp.gnu.mingw.user/27539
also referenced from workhorse.make. And I can confirm that any of the ways
to choose it mentioned there (e.g. -std=c++98, -D__USE_MINGW_ANSI_STDIO=1
and so on) is sufficient to reproduce the bug, i.e. it's not really
"-posix" fault.

 I can also say that the problem does *not* happen in any of the following
cases:

(1) Including <stdio.h> and using ::sprintf() instead of std::sprintf().

(2) Adding "using namespace std;" directive and then using std::sprintf()
    without explicitly qualifying it.

(3) Using a custom function with a using declaration for it inside a
    namespace paralleling the way the standard headers do it with sprintf.

The combination of (2) and (3) makes me pretty sure that it's some sombre
compiler bug because there is no way "using namespace std" + "sprintf"
could behave differently from "std::sprintf" on purpose. Yet, in the former
case a static sprintf() is emitted into the object file when compiling
while in the latter one, an extern function is generated.

 Unfortunately I didn't find any way to work, or even hack, around this
bug and I don't know what else to try (I've already tried a few things I
would be ashamed to mention in a polite company of C++ programmers, but
they didn't work anyhow).

 But I don't think we should leave it like this neither. While lmi itself
doesn't use std::sprintf() anywhere, boost.regex library does and this is
what originally costed me a lot of grief as I couldn't understand why I was
getting link errors when I started using it in the GUI test. It finally
turned out (and yes, I could have found this sooner, but I'm also pretty
confident that I could have wasted even more time on this as well...) that
to use it, I had to add --allow-multiple-definition linker option, as the
code already using boost.regex did -- and this is what I did for my patch. 
However, again, this can't be the right solution. Even if we prominently
document that using boost.regex requires this linker option (and I don't
even see where/how this could be done), it's just a bad idea to use it in
the first place as it could make other errors and potentially result in
run-time problems which would be much more difficult to debug than
link-time ones.

 Hence I'd like to suggest patching boost.regex to use just sprintf()
instead of std::sprintf(). This is not ideal neither, but the worst problem
I see with this is maintenance: normally, we would need to update this
patch when we upgrade to a newer version of boost.regex and in this
particular case this problem probably will never happen as we will
hopefully update to a newer compiler (which doesn't have this bug at all)
before/together with upgrading boost.regex.

 So far I've just quickly tested that doing this indeed works, i.e. we can
omit -Wl,--allow-multiple-definition and everything still links (and runs)
successfully if we patch boost.regex to use ::sprintf(). Is it worth it to
prepare a real patch to lmi doing this and maybe also looking at mpatrol
which also currently uses --allow-multiple-definition (but probably for
some other reason and, anyhow, using it for compilation of third party code
doesn't bother me that much, it's only using it for our own code that does)?
I'd really like to do it because using --allow-multiple-definition is IMO a
rather dangerous hack, but I'll wait for your reply before continuing with
this.

 Thanks in advance,
VZ

reply via email to

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