Jump to content
  • Advertisement

Archived

This topic is now archived and is closed to further replies.

amag

static members in templates - a bug in VC?

This topic is 5854 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I have a templated abstract factory where the classes to be constructed register themselves. This is done by every file that wishes to register a class to the factory like this:
  
namespace
{
  register_in_factory<image_io, bmp_image> register_me("bmp");
};
  
The ctor of register_in_factory looks like this:
  
register_in_factory(const classid_t &key)
{
  generic_factory<parent_t>::instance().register_type(key, create_instance);
}
  
Where generic_factory is a singleton and you get the instance of it through the instance()-method. The instance method constructs the factory if it isn''t already constructed. This is done by having a static auto_ptr in the class. My problem is that this sometimes happens twice, which obviously is bad since some classes register to the "wrong" instance, the one that "silently" disappears... This one has puzzled me for about 6 months, so if anybody would have a go at it I would be very pleased. /Andreas

Share this post


Link to post
Share on other sites
Advertisement
Hmm, this is just a shot at the moon... But maybe you''ve accidentaly called a recopy constructor?

Here''s a trick to prevent ''automatic recopies'' of an object, from the effective C++ author:

  
class CWhatever {
private:
// by setting the recopy and assignation private, we disallow the copy of this object.

// Any fancy use of this object should be with containers of pointers.

CWhatever (const CWhatever & Other){}
CWhatever & operator = (const CWhatever & Other){}


If I''m wrong... Well, I''ll have been no help.

Share this post


Link to post
Share on other sites
quote:
Original post by amag
This is done by having a static auto_ptr in the class.
My problem is that this sometimes happens twice, which obviously is bad since some classes register to the "wrong" instance, the one that "silently" disappears...

This seems like a static initialization order problem to me. Your singleton is constructed on demand just fine - the problem is the auto_ptr. The singleton can be constructed before the pointer, and its address gets shoved into the space allocated for the auto_ptr before it even exists. Later, the auto_ptr constructor zeroes out the address. At the next registration attempt, the instance method detects the null pointer and creates a new singleton.

The C++ FAQ has a good description of the problem and how to solve it.

[edited by - Krunk on September 12, 2002 11:29:16 AM]

Share this post


Link to post
Share on other sites
Spacecat:
Thanks, but I already do that.
quote:

This seems like a static initialization order problem to me


Yes, the symptoms are of that...but since it''s a templated class I need to have the initialisation of the static auto_ptr in the header-file where the factory resides. That basically means that the initialisation will end up in all translation units that include the factory-header. Normally (for a non-templated class) if you did this you''d get a linker error, but as far as I see it VC silently replaces all but one instance...

To further complicate the issue, I wrote a test with different (simpler) classes, but the same access pattern. The would-be factory template had a static auto_ptr in the same way the real factory template has. The test was succesful (i.e. it worked) if I instantiated the template with a builtin type (int), but had the described problems when instantiated with a user-type.

I feel that either I''m out in the undefined behaviour jungle or something is not working alright with VC...

Share this post


Link to post
Share on other sites
Why are you even using an auto_ptr?

why not just:

template<typename T>
generic_factory<T>& generic_factory<T>::instance()
{
static generic_factory<T> m_instance;
return m_instance;
}


That way, m_instance would be constructed the first time you call generic_factor<T>::instance()

Share this post


Link to post
Share on other sites
quote:
Original post by daerid
Why are you even using an auto_ptr?
That''s one I had to look up myself too

quote:
Originally posted in here
By storing an auto object (one that is allocated on the stack), we cannot store derived classes.

No problem, we store a local pointer instead of local instance. This would allow Get_Instance() to return a reference to its parent interface (perhaps an abstract interface).

static Parent& MyChild::Get_Instance()
{
static Parent *p = new MyChild();
return *p;
}

Problem 1 is solved, except that the singleton now leaks because the destructor is never called. The obvious solution here then would be to store a local static smart pointer (like std::auto_ptr) instead of the raw pointer.


Share this post


Link to post
Share on other sites
You should be able to keep the same signature and use a stack-allocated object instead:

  
class Parent
{
};

class MyChild : public Parent
{
public:
static Parent &Get_Instance ();
};

Parent& MyChild::Get_Instance()
{
static MyChild c;
return c;
}


Not sure if this solves your initial problem, though. I''m not an expert on templates, but if you have a templated class with a static member, doesn''t a different static member exist for each type of template that''s instantiated?

Share this post


Link to post
Share on other sites
quote:
Original post by civguy
That''s one I had to look up myself too



But wait a minute.. doesn''t this prove that wrong?


  
#include <iostream>
using namespace std;

class base
{
protected:
base(){}
public:
static base* get_instance()
{
static base s_base;
return &s_base;
}
virtual void foo()
{
cout << "base::foo()" << endl;
}
};

class derived : public base
{
private:
derived(){}
public:
static base* get_instance()
{
static derived s_derived;
return &s_derived;
}
void foo()
{
cout << "derived::foo()" << endl;
}
};

main()
{
base::get_instance()->foo();
derived::get_instance()->foo();
}


I may not be getting something here...

Share this post


Link to post
Share on other sites
quote:
Original post by daerid
But wait a minute.. doesn''t this prove that wrong?
Nah. That way you can have 2 instances. One for the base class and one for the derived class.

But the point is to have just 1 instance which can (polymorphically) represent any of the derived classes.

myRenderer::getInstance()->foo();

Where myRenderer could represent OpenGL or DirectX renderer, depending on which of the derived classes it initializes it''s *instance with.

Share this post


Link to post
Share on other sites

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!