Stratos: Punto de Encuentro de Desarrolladores

¡Bienvenido a Stratos!

Acceder

Foros





Diferencias Release - Debug VC6

Iniciado por Pogacha, 24 de Enero de 2008, 07:31:14 PM

« anterior - próximo »

Pogacha

Que tal,
Me encuentro en aprietos con un problema bastante hincha-huevos.

En modo debug el programa anda perfecto, en modo release genera un error en la etapa atexit() lo cual no me permite hacer manual debug.

Que diferencias concretas existen entre el modo debug y release.
Las que ya conozco son:
* Orden de evaluacion tras la coma, ej: F( A(), B() ) no esta garantizado el orden en que A y B se ejecutaran.
* Obviamente inizialización de datos, en modo debug la mayoria de los datos son inicializados con valores afines para el debug.

Ninguno de estos es el caso de este bug.
Estoy empezando a sospechar que es un error del compilador.

Que otra cosa se les ocurre?

shephiroth

Hace ya un tiempo q me pase a java, pero me acuerdo que las inicializaciones de arrays/punteros siempre me hacian sudar al cambiar de debug a release

tamat

Estos problemas suelen ser siempre variables que no se inicializaron, en los visuals nuevos suele detectarlo rapidamente y te avisa pero a veces con temas de arrays pasa desapercibido. Se que lo normal en todo programador es asumir por orgullo que no es este el caso pero a veces se cuelan de la manera mas tonta.
Por un stratos menos tenso

Pogacha

OBVIO que es mas facil hecharle la culpa al compilador, pero ya no se que pensar. Vamos que seguramente el error debe ser mio.
Ya comprobé todos los punteros y datos y demas ... el juego se destruye completamente sin presentar errores antes de llegar a la fase atexit.
Pero por alguna razon misteriosa, 3 singletones no son destruidos, particularmente los 3 primeros en inicializarze.
O sea la cadena atexit se corta. Estube verificando que no haya llamadas a atexit dentro de esta fase (por que es una practica conocidamente peligrosa), pero sin suerte, lo unico que queda es que halla un error interno y el proceso se termine prematuramente y que este error no sea reportado.
Tuve problemas con los singletones desde un principio, pero luego de actualizar el compilador al service pack 6 este empezo a andar magicamente.

Probe a cambiar las optimizaciones a "debug=ninguna" en modo release y el error igual aparece por lo que me hace pensar que esto viene por el uso de la libreria standart en modo release.

Sigo investigando pero los papeles se me quemaron hace rato.

Saludos!

PD: otro punto, pareciera como que se desborda la pila del atexit, pues cuando no todos los singletons son inicializados este se ejecuta correctamente.

tamat

Creo que he recordado otra cosa, el tema de la destruccion de variables estáticas, algo me suena sobre que podria generar problemas.

Un viejo truco que yo usaba para detectar fallos en release era el de ir comentando codigo y ejecutando para ver si da el error, si no lo da entonces está en la parte de codigo comentado.

Y sobre todo, DEJA DE USAR VISUAL 6, estaba plagado de bugs!
Por un stratos menos tenso

davur

No es el tema del hilo, pero el orden de evaluación de los argumentos de una función es un comportamiento no especificado en el estándar de C++.

Estándar que sí especifica que el orden de destrucción de variables estáticas es el inverso al de su construcción.

Los singletones en general, y las interacciones y dependencias entre ellos en particular, suelen dar más quebraderos de cabeza que beneficios (en mi experiencia personal, pero desde luego es uno de esos temas algo "religiosos" y propensos a debates eternos).

Dicho lo cual, VC++ 6 tiene muchas carencias, y entre ellas su limitada adecuación al estándar del lenguaje.

Pogacha

He resumido el codigo dejando tan solo 2 singletones y si pongo "inline function expansion" = "*Disable" en modo release con optimizaciones al máximo, el programa funciona de maravilla.

Es muy posible que sea un error del compilador a fin de cuentas, pues he verificado el orden de creación de los objetos segun el standard y en modo debug funciona y en modo release el orden es el mismo, pero en algun punto la cadena atexit se corta antes de llegar al segundo singleton.

Nada, gracias igual

Pogacha

Si, si una clase estatica en su constructor invoca a un singleton, el singleton se construye, pero el destructor nunca es llamado.

Error del compilador.

Eso es todo.

ethernet

podrías poner un poco más de info? un poco de código, sobretodo de como tienes hecho el singletón.

Pogacha

template<class T> class Singleton {

// Holder is like an autoptr which also creates if needed
class Holder {
T* pmInstance;
public:
Holder() : pmInstance(0) { }
~Holder() { if(pmInstance) delete pmInstance; }
T* Get_Instance()
{
if(!pmInstance) pmInstance = new T;
return pmInstance;
}
void Set_Instance(T* t) { assert(pmInstance==0); pmInstance = t; }
bool Instanciated() { return pmInstance!=0; }
};

static Holder* Get_Holder() {
T::Solve_Dependencies();
{
static Holder s_holder;
return &s_holder;
}
}

Singleton( const Singleton& ); // disallow copy constructors
Singleton& operator = ( const Singleton& );

friend class Holder;

protected:

// to solve life range problems
static void Solve_Dependencies() {
// You should define your own dependencies solver
// adding a list of classes and force them to born
}

Singleton() {}
virtual ~Singleton() {}

public:

// to solve life range problems
static void Force_Born() {
Get_Holder();
}

// for polymorphic classes
template< class S > static void Specialize() {
assert( Get_Holder()->Instanciated() == false );
Get_Holder()->Set_Instance( new S() );
}

// To get the instance!
static T* Get_Instance() {
static T* s_instance = 0;
if(!s_instance) {
// Mutex::Lock lock(pMutex); // to make it multithreading safe
// if(!s_instance)
s_instance = Get_Holder()->Get_Instance();
}
return s_instance;
}
};

#define SINGLETON_FRIENDSHIP( x ) friend class Singleton< x >; friend class Holder;


Y un ejemplo de uso:


class S_Log : public Singleton<S_Log> {
  SINGLETON_FRIENDSHIP( S_Log )
  FILE f;
  S_Log() { f = fopen( ... ); }
  ~S_Log() { fclose(f); }
public:
  void Out( ... ) { fprintf( ... ); }
};


bool log_on_a = false;

class S_A : public Singleton<S_A> {
 SINGLETON_FRIENDSHIP( S_A )
  S_A() { if(log_on_a) S_Log::Get_Instance()->Out( "S_A()" ); }
  ~S_A() { if(log_on_a) S_Log::Get_Instance()->Out( "~S_A()" );  }
public:
  Solve_Dependencies() {
     S_Log::Force_Born();
  }  
  void foo() { };
  void foo2() { };
};


class B {
  B()  { S_A::Get_Instance()->foo(); }
  ~B()  { S_A::Get_Instance()->foo2(); }
};

B b; // execute S_A::foo() before start up

int main()
{
  S_Log::Get_Instance()->Out("main");
  log_on_a = true;
  return 1;
}


Como veran maneja el tema del life range problem de una manera ultra elegante, nada de holders externos ni necesidad de PhoenixSingletons. Por mas que log_on_a esté en false o no a lo primero S_Log se destruira inequivocamente despues de ser requerido en todos los casos.
Estoy muy orgulloso de esta clase, facilmente se puede expandir a politicas.

Pero resulta que no siempre me compila adecuadamente en un caso similar a este donde una instancia estatica de una clase invoca al singleton.

En este ejemplo el codigo que se tendria que generar seria:

CONSTRUCTOR_B()
  CONSTRUCTOR_S_A_Holder()
      CONSTRUCTOR_S_Log_Holder()
         atexit( DESTRUCTOR_S_Log_Holder )
      atexit( DESTRUCTOR_S_A_Holder )
  atexit( DESTRUCTOR_B );

Pero DESTROY_S_Log y DESTROY_S_A nunca son llamados en algunos casos de compilación, lo que puede significar muchas cosas. Que un error ocurre previo a eso o que el stack del atexit se corrompa. Esto puede pasar por ejemplo cuando el orden en que se ponen los atexit sea incorrecto por el tema del inlining y el Log se cierre antes que S_A o que S_A se cierre antes que B el cual la usa en su destructor.
Como solución podría quitar las instancias estaticas que invocan al singleton, pero en teoria el codigo deberia estar andando sin mas.

Saludos

Martinez

El problema no estaba en el compilador sino en la progrmacion:


template<class T> class Holder
{
   T *pInstance;

   public:
       Holder()
       : pInstance(0) { fputs("Constructor Holder\n",file); }
       ~Holder()
       {
           if(pInstance)
           {
               delete pInstance;
               fputs("Destructor Holder\n",file);
           }
       }
       T* getInstance()
       {
           if(!pInstance)
           {
                pInstance=new T;
           }
           return pInstance;
       }
};

template<class T> class Singleton
{

protected:

   Singleton()
   {

       fputs("Singleton::Singleton()\n",file);
   }
   ~Singleton()
   {
       fputs("Singleton::~Singleton()\n",file);

   }

public:
   static Singleton* getSingleton()
   {
       static Holder<T> holder;
       static Singleton *pInstance=holder.getInstance();
       return pInstance;
   }
};



Y una implementacion:


class CA: public Singleton<CA>
{
   protected:
   friend class Holder<CA>;
   CA()
   {
       fputs("\t\tConstructor CA\n",file);
   }
   ~CA()
   {
       fputs("Destructor CA\n",file);
   }
};

class CB: public Singleton<CB>
{
   friend class Holder<CB>;
   protected:

   CB()
   {
       fputs("\t\tConstructor CB()\n",file);
   }
   ~CB()
   {
       fputs("Destructor CB\n",file);
   }
};



int main()
{
   file=fopen("res.txt","wb");
   fprintf(file,"%d\n",int(time(NULL)));
   CA::getSingleton();
   CA::getSingleton();
   CB::getSingleton();
   CA::getSingleton();


return 0;
}


Salida del ejecutable:

1201364292
Constructor Holder
Singleton::Singleton()
Constructor CA
Constructor Holder
Singleton::Singleton()
Constructor CB()
Destructor CB
Singleton::~Singleton()
Destructor Holder
Destructor CA
Singleton::~Singleton()
Destructor Holder


PD: Ahora lo pruebo en el VS 2005 que esta para el MinGW. Pero deberia funcionar igual. Espero que te sirva, nos vemos.

Pogacha

Cita de: "Martinez"El problema no estaba en el compilador sino en la progrmacion:

No veo donde...
Me lo podrias explicar.

El codigo funciona perfectamente bien excepto cuando hay inlining, donde supongo que el orden de las llamadas a atexit's se cambia por error del compilador (o por error mio por suponer que se tendria que respetar ).

Martinez

Ya se donde esta el error. Jeje sorry pero me he precipitado, tu codigo y el mio son correctos. Si se ejecuta solo que no lo ves, cierras el archivo del log antes de llamar a los destructores. El orden de destruccion es la inversa de la construccion. Me ha pasado lo mismo que a ti, por eso saque file de las clases , pero antes de eso llege a pensar que solo se creaba un tipo Holder y por eso solo se borraba uno. Simplemente no cierres el archivo.






Stratos es un servicio gratuito, cuyos costes se cubren en parte con la publicidad.
Por favor, desactiva el bloqueador de anuncios en esta web para ayudar a que siga adelante.
Muchísimas gracias.