1

Following is a simplification of code from a larger project:

// foo.h
#ifndef FOO_H
#define FOO_H

#include <string>

class Foo
{
public:
    Foo( const std::string& s = magic_ );

    void func();

    static const std::string magic_;

private:
    std::string s_;
};

void func( const std::string& s = Foo::magic_ );

#endif
//foo.cpp
#include "foo.h"
#include <iostream

const std::string Foo::magic_ = "please";

Foo::Foo( const std::string& s )
    : s_( s )
{ }

void Foo::func() { std::cout << "[" << s_ << "]" << std::endl; }

void func( const std::string& s )
{
    Foo( s ).func();
}
// main.cpp
#include "foo.h"

int main( int argc, char* argv[] )
{
    func();
    return 0;
}

I'll call the above a MCE, not a MCVE because unfortunately I've not been able to reproduce the problem in a simplification, which I can only guess is because of quirks of static variables and shared linkage - possibly an incorrect assessment, because of a not-thorough understanding of what's involved. But I will try to explain the problem. The following aspects of the above MCE are representative of the problem code:

  1. A header file declares a class with a static member std::string.
  2. The same header defines a function with an in-arg defaulted to the class' static member variable.
  3. The header's corresponding source file defines the static member and function.
  4. The translation unit is compiled to a shared library.
  5. The executable's object code is linked with the shared library.

Compilation/output:

$ g++ -O3 -c -fPIC -o ./foo.o ./foo.cpp 
$ g++ -O3 -shared ./foo.o -o ./libfoo.so
$ g++ -O3 -c -fPIE -o ./main.o ./main.cpp
$ g++ -O3 ./main.o -o ./a.out -L./ -lfoo
$ LD_LIBRARY_PATH=$LD_LIBRARY_PATH: ./a.out 
[please]

Though not demonstrated above, is it a possibility that during executing the output may be "[]"? I.e. might Foo::magic_ be empty at the time used as the default value assigned to void func()'s in-arg?

This is hard to articulate in the absence of a demonstrable problem with the above MCE, but assuming it is truly representative of the problem code, I observe (by stdout due to gdb absence in the real build/test environment) that in void func(), in-arg s is an empty string - can anyone account for/explain why this could be?

I know initialization of static variables is not guaranteed across translation units - is that possibly involved here? (It seems like it might explain why the problem is not reproducible in an attempted simplification)

Again, apologies for the lack of a MCVE - I tried my best to create one.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
StoneThrow
  • 5,314
  • 4
  • 44
  • 86
  • I think you might have "order of initialization" problem. See: https://stackoverflow.com/q/335369/1216776 – stark Dec 16 '19 at 21:29
  • @stark - I'm aware of that issue, thank you, and was guessing that's what was going on here. But what makes me uncertain that that's the case here is that use of the static variable is in the same translation unit as where the static variable is defined. The quirk, though, is technically, the _use_ of the static variable is in the header, whereas the definition of the static variable is in the source - I'm unclear how this comes into play here, if indeed this is a factor. So it's slightly different than what's described at the link you shared. – StoneThrow Dec 16 '19 at 21:32
  • https://isocpp.org/wiki/faq/ctors#static-init-order-on-first-use – Adam Stepniak Dec 16 '19 at 21:48
  • @AdamStepniak - Tthank you for bringing that up - I tried that (did not note it in the OP in order to not clutter with too many details). Long story short: did not resolve the problem. Specifically: I tried refactoring such that `void func()` so that it took a `RandChar` reference in its arglist, which defaulted to a default-contructed `RandChar` instance which used its own static `std::string`. But the result was exactly the same - I speculate, again, because of the default value being in the header file and static variable definition in the source. I'm still at a loss for a conclusive answer – StoneThrow Dec 16 '19 at 22:01
  • @StoneThrow It is likely that your issue is not static initialization order. The example you have provided should always output `[please]`, since you only access the static data member in `main()`. There might be something else in your larger project causing this problem. – ph3rin Dec 16 '19 at 22:11
  • When is `func` called in your real program? Is it (indirectly) from `main` or from some other initialization? – Davis Herring Dec 17 '19 at 05:20

0 Answers0