Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can "construct on first use" idiom fail under any circumstances?

I'm building my program (tests actually) using some static library.
This library contains one file inside which I have functions like that:

string& GetString() {
    static string strFilename;
    return strFilename;
}

void PrintToScreen() {
    printf("String: %s\n", GetString().c_str())
}

Then in my main.cpp (outside the library) I'm doing:

GetString() = "abc";
printf("String: %s\n", GetString().c_str());
PrintToScreen();

And I get this output:

String: abc
String:

So looks like second call to the function (but done from different file, which is inside the library) somehow clear previous value, reinitialize it, or uses own copy of it.
I changed GetString function to use 'new' but result is exactly the same (btw. program never crash).
But I don't understand hot it's possible?
Any ideas what I'm doing wrong?

------------------------------- UPDATE ------------------------------

  1. Test is done is single threaded environment.
  2. It works on some platforms and on some it doesn't (works on windows, MacOS and AIX, doesn't work on linux, HP_UX, Solaris, FreeBSD...)
  3. I verified address of the strFilename during the execution (printf inside GetString) and looks like it's one variable without duplicates (address is always the same)
  4. BUT, with nm on the final lib I get something like that:

0000000000000030 T _Z16GetLogprintfFilev
0000000000000008 b _ZGVZ16GetLogprintfFilevE16strLogprintfFile
0000000000000018 b _ZZ16GetLogprintfFilevE16strLogprintfFile
U _Z16GetLogprintfFilev

and with nm on my base lib (used by final lib) I get:

0000000000000030 T _Z16GetLogprintfFilev
0000000000000008 b _ZGVZ16GetLogprintfFilevE16strLogprintfFile
0000000000000018 b _ZZ16GetLogprintfFilevE16strLogprintfFile

like image 497
Piotr Kukielka Avatar asked Nov 05 '22 15:11

Piotr Kukielka


1 Answers

Yes this is quite possible when static linking.

Example:

 libA.a   // contains GetString()
          // Contains. PrintToScreen()
          // Here the reference has been resolved.

 libX.so  // Contains a call to GetString()
          // This library is linked with libA.a
          // Thus pulls in the function GetString() into this library.

 libY.so  // Contains a call to PrintToScreen()
          // This library is linked with libA.a
          // Thus pulls in the function PrintToScreen and GetString() into this library.

 a.out    // linked against libY.so libX.so
          // This has two distinct versions of GetString()

In the above example if a.out contains a call got getString() it is OS specific which version of getString() will be called. On most systems the load order of the individual shared library is used but on others it will do a depth first search of the shared libraries (ie lib X loads XA XB and Y loads YA YB. search order could be X XA XB Y YA YB or X Y XA XB YA YB). You need to consult each OS shared library documentation to understand how symbols are searched for at runtime.

The solution here is to only link against shared libraries (the default in most situations).
That way you only get one copy of libA (assuming you made libA a shared lib) and its content loaded into runtime only once (with no copies).

Note: This is not a failure at the language level.
This a failure caused by linking which is beyond the scope of the C/C++ language.

like image 188
Martin York Avatar answered Nov 09 '22 05:11

Martin York