5

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

Piotr Kukielka
  • 3,622
  • 3
  • 29
  • 40
  • btw is this your actual code or a simplified example? – Armen Tsirunyan Sep 06 '11 at 19:59
  • No, it's totally single threaded. When I need multithreading I'm using proper singletons, this is only lightweight version. And it's simplified example of a problem, but structure is identical to real problem. – Piotr Kukielka Sep 06 '11 at 19:59
  • 1
    Have you tried stepping through your code in a debugger to see where the calls are going, and what data they are accessing? –  Sep 06 '11 at 20:01
  • 6
    @kuki: Well, this simplified example works as expected. Therefore, the structure is not ***identical*** to the real problem. Please post a minimal example that ***reproduces*** the problem. There's a fat chance that you will find your bug while constructing that example – Armen Tsirunyan Sep 06 '11 at 20:03
  • I think I'll try debugger tomorrow, I don't have access to codebase right now. Previously I just had print in GetString, and strFilename was always empty when called from PrintToScreen(). And why are you saying that it works as expected? Look at the second line of the output: it doesn't print proper variable value. – Piotr Kukielka Sep 06 '11 at 20:07
  • 1
    This code is fine. Your bug is in the code that you didn't show us. – David Heffernan Sep 06 '11 at 20:07
  • Also I forgot to mention very important thing: I'm almost sure it's more or less connected to way of the linking. On windows, MacOS and AIX it works fine, but fails on linux, HP-UX and solaris... – Piotr Kukielka Sep 06 '11 at 20:09
  • 1
    @kuki: It works differently as you've posted, i.e. it prints "abc" two times, as it should. Your code definitely contains some other bug which you will find when constructing a minimal example that reproduces the problem. If you don't, we'll happily help you find it when you update your question. Alternatively, I would suggest that you delete the question and post a fresh one with the good example after you've debugged your code and haven't found the issue. I, for one, am voting to close this – Armen Tsirunyan Sep 06 '11 at 20:10
  • Well, as I said for me it was printing different values depends on platform ;) But I'll try to improve example if I won't find the problem. Actually I get some idea now - I'll check address of the static variable to be sure it's not duplicated somehow during the linking stage. – Piotr Kukielka Sep 06 '11 at 20:18

2 Answers2

3

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.

Martin York
  • 234,851
  • 74
  • 306
  • 532
  • I was thinking exactly the same at first but I double checked that I'm not linking the same library twice. Well, I think I'll check third time tomorrow... – Piotr Kukielka Sep 06 '11 at 20:16
  • @kuki: It would be interesting if you could tell us the exact libraries involved and how they are all linked together. If you are on a *nix like box `nm` will dump the symbols so you can see if GetString() is in more than one location. – Martin York Sep 06 '11 at 20:19
  • Wouldn't this require the *function* to be static? Shouldn't it give a linker error otherwise? – bitmask Sep 06 '11 at 20:21
  • @bitmask: No that is not necessary (but that is another way of achieving the same affect). In the situation described above each shared library is independent and can have overlapping exported identifiers and be successfully linked with the executable (The link stage is completely independent to compilation and is only trying to resolve symbols (not do duplicate detection). – Martin York Sep 06 '11 at 20:24
  • @Tux-D: I have shared.lib (which contains both of the functions) and two other libraries merged into one server.lib library. Then at the end this server.lib is linked with the test files. Unlucky whole process is a bit more complicated (we are relinking server.lib before, etc.). I think I'll check nm output as you said, as well as print out address of strFilename during the execution. Update will come in ~12h from now ;) – Piotr Kukielka Sep 06 '11 at 20:30
  • I'm not so up to speed with shared libraries, but if I link several object files together, then it *does* matter to the linker. This is why I wondered that it suddenly *does not* matter any more if the symbols are sitting in so files. – bitmask Sep 06 '11 at 20:38
-1

Actually there was one missing think in example. It should look like this:

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

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

Strange. Anyway, I refactored it to something like this:

extern "C" {
  string* GetString() {
    static string strFilename;
    return &strFilename;
  }

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

And it works without problem now.
Still it seems strange to me that compiler was not complaining.
Thanks to all for their contribution, problem is solved now.

---------------------------------- EDIT ----------------------------------

I experienced this problem again later so it was not proper fix.
The real problem was some singleton which was initialized
in meantime and had in the class constructor:

GetString() = "";

So, simple problem, but really hard to track...

Piotr Kukielka
  • 3,622
  • 3
  • 29
  • 40
  • Its good that you found the solution. Accept the answer so that it will be promoted to first place and people will be able to see it easily. – vrbilgi Sep 07 '11 at 12:08
  • I'll do that, but I get the message that I can do this tomorrow ;) – Piotr Kukielka Sep 07 '11 at 13:00
  • I don't think this solved the problem (it it existed). The problem is caused by the way you were linking and loading the shared libraries. You have just hidden the problem. Unfortunately the problem is related to the link and runtime load order. But without the exact commands you use to build the executable it is not dignosable and if you change the order the problem may re-surface. – Martin York Sep 07 '11 at 13:25
  • I'm not using share libraries (.so) files at all, everything is statically linked. So I cannot have situation like in the example you gave me before. Not to mention that I didn't changed way of building the project nor added/deleted any files, so I don't see why problem should go away if it's not real fix. Changing 2 lines is enough to make it working... – Piotr Kukielka Sep 07 '11 at 14:55