Velocity Reviews

Velocity Reviews (http://www.velocityreviews.com/forums/index.php)
-   C++ (http://www.velocityreviews.com/forums/f39-c.html)
-   -   Confusing difference in raw versus shared_ptr behavior w/ static initializers (http://www.velocityreviews.com/forums/t805468-confusing-difference-in-raw-versus-shared_ptr-behavior-w-static-initializers.html)

Rudi Cilibrasi 11-02-2011 10:59 PM

Confusing difference in raw versus shared_ptr behavior w/ static initializers
 
Hi everybody,

I am struggling to understand the different behavior exhibited in the
raw versus shared_ptr behavior of the static initializers in the
following small test program. Can anybody explain this to me, or is
it perhaps a compiler bug?
I am trying to get good at RAII and cannot figure this puzzle out.

Any helpful info will be much appreciated. Code pasted below. Best
regards,

Rudi

// Compiled with g++ 4.5.2 using command:
//
// g++ main.cc -o noshared -std=c++0x
// or
// g++ main.cc -o yesshared -std=c++0x -DUSED_SHARED=1
//
// How come the first case produces two lines of output and the second
only 1?
//
// % ./noshared
// cmdstring
// hello
// %
// % ./yesshared
// hello
// %



#include <string>
#include <vector>
#include <iostream>

#include <memory>


class Anac {
std::vector<std::string> m_strings;
Anac(void);
public:
static Anac& getInstance(void);
void printStrings(void);
void addString(std::string val);
};

class Cmd {
public:
Cmd(void);
};

static Cmd c;

Cmd::Cmd(void)
{
Anac::getInstance().addString("cmdstring");
}

using namespace std;

#if USED_SHARED
static std::shared_ptr<Anac> singleton;
#else
static Anac *singleton;
#endif

Anac::Anac(void) : m_strings() {
}

void Anac::addString(std::string val) {
m_strings.push_back(val);
}

void Anac::printStrings(void) {
for (auto i = m_strings.begin(); i != m_strings.end(); ++i) {
cout << (*i) << '\n';
}
}

Anac& Anac::getInstance(void) {
if (!singleton) {
#if USED_SHARED
singleton = std::shared_ptr<Anac>(new Anac());
#else
singleton = new Anac();
#endif
}
return *singleton;
}

using namespace std;

int main(int argc, char **argv)
{
Anac::getInstance().addString("hello");
Anac::getInstance().printStrings();
return 0;
}

Rudi Cilibrasi 11-03-2011 12:43 AM

Re: Confusing difference in raw versus shared_ptr behavior w/ static initializers
 
I wanted to give my best theory in another post to let an expert
decide if it's right.

THEORY:
It looks like the problem could be in the order of global static
initializers. In other words, if the smart_ptr constructor is called
after the Cmd constructor, it would "zero out" the pointer that was
stored there from the first getInstance() call. This seems to be what
happens and thus it erases the first instance of Anac and replaces it
with a new one.

My detailed questions are as follows:

1) In cases like these where one static initializer calls another but
the reverse is not true and there are no cycles, why doesn't the
compiler do the static initializers in a topologically sorted order?
Wouldn't it make sense for the compiler to sort them all and print a
warning when there is a constructor call cycle among static
initializers?

2) It seems like "simple primitive" types have an advantage over "real
objects" here in that their default constructors happen before any
object constructors due to the way normal executable loaders
initialize a large block of memory to "all zero" bit patterns for
stuff like int within static or global scopes. Doesn't it make sense
for objects to support this "early construction using the all-zero bit
pattern" as well so that they can be made to safely initialize without
any worry of cycles before any objects that require non-zero bit
patterns in the default construction? I was thinking something like a
"zero_default" tag on the default constructor meaning that you cannot
define a method body but it just winds up initializing everything to
all zeros and so is ideal for static initialization. Then these kind
of objects could only contain primitives or other objects that also
have a "zero_default" type default constructor. These would have no
ordering problems and would all happen automatically before any
complex construction. I have found this pattern of construction to be
handy in object-oriented-style C as well in the past. Is there a
clean way to implement it in C++ nowadays?

3) What is the best solution to this problem in general in C++? I
want to retain the modularity of static (non externally visible) data
if possible. What is the best Singleton pattern to use to avoid this
kind of dependence on the order of static construction?

Best regards,

Rudi

Richard Damon 11-03-2011 02:43 AM

Re: Confusing difference in raw versus shared_ptr behavior w/ staticinitializers
 
On 11/2/11 8:43 PM, Rudi Cilibrasi wrote:
> I wanted to give my best theory in another post to let an expert
> decide if it's right.
>
> THEORY:
> It looks like the problem could be in the order of global static
> initializers. In other words, if the smart_ptr constructor is called
> after the Cmd constructor, it would "zero out" the pointer that was
> stored there from the first getInstance() call. This seems to be what
> happens and thus it erases the first instance of Anac and replaces it
> with a new one.
>
> My detailed questions are as follows:
>
> 1) In cases like these where one static initializer calls another but
> the reverse is not true and there are no cycles, why doesn't the
> compiler do the static initializers in a topologically sorted order?
> Wouldn't it make sense for the compiler to sort them all and print a
> warning when there is a constructor call cycle among static
> initializers?
>
> 2) It seems like "simple primitive" types have an advantage over "real
> objects" here in that their default constructors happen before any
> object constructors due to the way normal executable loaders
> initialize a large block of memory to "all zero" bit patterns for
> stuff like int within static or global scopes. Doesn't it make sense
> for objects to support this "early construction using the all-zero bit
> pattern" as well so that they can be made to safely initialize without
> any worry of cycles before any objects that require non-zero bit
> patterns in the default construction? I was thinking something like a
> "zero_default" tag on the default constructor meaning that you cannot
> define a method body but it just winds up initializing everything to
> all zeros and so is ideal for static initialization. Then these kind
> of objects could only contain primitives or other objects that also
> have a "zero_default" type default constructor. These would have no
> ordering problems and would all happen automatically before any
> complex construction. I have found this pattern of construction to be
> handy in object-oriented-style C as well in the past. Is there a
> clean way to implement it in C++ nowadays?
>
> 3) What is the best solution to this problem in general in C++? I
> want to retain the modularity of static (non externally visible) data
> if possible. What is the best Singleton pattern to use to avoid this
> kind of dependence on the order of static construction?
>
> Best regards,
>
> Rudi


The order of construction of "Dynamically initialized" (initialized via
a constructor or with a non-constant value) objects is specified by the
standard, to be the order they are declared. It is the programmers
responsibility to make sure that order is acceptable. "Statically
Initialized" object (no constructor and with a constant) happen at load
time.

The authors of the language did not think that every object needed an
initialized flag, that was checked before each access, and if not
initialized, to force the object to be initialized, which is basically
what would be needed to sequence these properly. Note that to detect the
order of usages and detect the issue is akin to the halting problem. In
general, remember that the execution path may go into other translation
units. This makes it impossible to always detect it. Compilers are of
course allowed to warn about it in cases they can detect, as they are
allowed to warn about anything at all, even things that aren't "wrong".
I don't think may detect this as they can only find the simple cases,
and only after a reasonable amount of work. And the easy cases are
probably the ones that the programmer is most apt to find out for
themselves, and some simple rules can eliminate many of them.



Juha Nieminen 11-03-2011 06:48 AM

Re: Confusing difference in raw versus shared_ptr behavior w/ static initializers
 
Rudi Cilibrasi <cilibrar@gmail.com> wrote:
> for (auto i = m_strings.begin(); i != m_strings.end(); ++i) {


Not related to your question, but since you are using C++11 anyways,
why not save trouble and use: for(auto& str: m_strings) ...

zindorsky 11-03-2011 05:17 PM

Re: Confusing difference in raw versus shared_ptr behavior w/ static initializers
 
On Nov 3, 12:48*am, Juha Nieminen <nos...@thanks.invalid> wrote:
> Rudi Cilibrasi <cilib...@gmail.com> wrote:
> > *for (auto i = m_strings.begin(); i != m_strings.end(); ++i) {

>
> * Not related to your question, but since you are using C++11 anyways,
> why not save trouble and use: for(auto& str: m_strings) ...


Because range-based for is not supported in GCC 4.5 (the compiler the
OP is using).
http://gcc.gnu.org/projects/cxx0x.html

cartec69@gmail.com 11-04-2011 11:13 PM

Re: Confusing difference in raw versus shared_ptr behavior w/ static initializers
 
Your theory about the order of static initialization is correct. The fix tothis in your program is to ensure the dynamic initializers happen in the proper order:

static XXXX singleton;

static Cmd c;

Your code actually works fine unchanged in gcc 4.6, since 4.6 adds support for the C++11 constexpr keyword., shared_ptr's default constructor is declared constexpr and evaluated at compile time, so singleton's initialization all happens at load time just as it would for a native pointer. (Order of dynamic initializers doesn't matter if you don't have dynamic initializers.)

Last but not least, unique_ptr is more idiomatically correct than shared_ptr for singletons.

Rudi Cilibrasi 11-06-2011 02:53 AM

Re: Confusing difference in raw versus shared_ptr behavior w/ static initializers
 
Thank you for the awesome answers, Cartec! I really appreciate the
guidance and the gcc versions interest me greatly. I have yet to get
deeply into constexpr but it does sound like just what I need! I am
very happy to have made the connection. It will give me a good
context for studying it soon. Cheers,

Rudi

On Nov 4, 4:13*pm, carte...@gmail.com wrote:
> Your theory about the order of static initialization is correct. The fix to this in your program is to ensure the dynamic initializers happen in theproper order:
>
> static XXXX singleton;
>
> static Cmd c;
>
> Your code actually works fine unchanged in gcc 4.6, since 4.6 adds support for the C++11 constexpr keyword., shared_ptr's default constructor is declared constexpr and evaluated at compile time, so singleton's initialization all happens at load time just as it would for a native pointer. (Order ofdynamic initializers doesn't matter if you don't have dynamic initializers..)
>
> Last but not least, unique_ptr is more idiomatically correct than shared_ptr for singletons.




All times are GMT. The time now is 05:26 AM.

Powered by vBulletin®. Copyright ©2000 - 2014, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.