logo
Contato | Sobre...        
rebarba rebarba

Rodrigo Strauss :: Blog


Bug escabroso no Visual C++ 6.0

Como eu já disse antes, o Visual C++ 6.0 é muito velho e tem sérios problemas para compilar o Boost. E como ficar alocando e desalocando memória na mão é coisa de quem não sabe nada de C++ moderno, meu chefe/gerente criou uma classe de smart pointer com contador de referência para usarmos nos nossos projetos (sim, meu gerente programa e muito bem). Ele me passou a classe para colocar em alguns projetos, e depois de apagar todos os operadores de conversão automática (safety first!), eu encontrei um bug muito estranho no código que usava essa classe.

O que eu consegui isolar é que o bug acontece quando existe um operador de atribuição que é um template. O VC6 se perde completamente, e não gera o código que chama o operador, fazendo com que a atribuição não seja feita e que ele compile um código que é inválido. Eu resumi o código ao menor caso de teste possível, e mudei o nome da classe para CStupidPtr. Bom, vamos ao código:

//
// Rode isso no Visual C++ 6.0 e veja o que acontece
//

template<typename T>
class CStupidPtr
{
  T* m_pT;
public: 

  CStupidPtr()
  {
    m_pT = 0;
  }
  
  template< typename TSrc >
  CStupidPtr<T>& operator=(const CStupidPtr<TSrc>& p ) throw()
  {
    //
    // vamos colocar um break point forçado aqui para ver
    // se ele passa
    //
    __asm int 3;

    m_pT = p.m_pT;
    return( *this );
  }

public:

  T* operator->() const throw()
  {
    return m_pT;
  }
};

struct TEST
{
  int d;
};

int main()
{
  CStupidPtr<TEST> p;
    
  //
  // essa linha NÃO É GERADA PELO VC6, 
  // e não compila em uma versão mais nova do VC, pois é inválida
  // rode esse programa no debugger do VC6, passo a passo, e você verá
  // que essa linha é pulada pelo debugger.
  //
  p = new TEST();
  

  //
  // tã!
  //
  p->d = 10;

  return 0;
}

Esse código funcionaria se houvesse uma conversão possível de T* para CStupidPtr, o que parace acontecer quando esse código é compilado. Mas note que não existe nenhum operador ou construtor que faça a conversão. O que acontece é que não há conversão, não acontece nenhum erro durante a compilação (mesmo o código sendo inválido) e o VC6 não gera código para a linha de atribuição. E no final ganhamos um 0xC00000005 (access violation) de presente, já que o operador -> retorna NULL.

Olha que o acontece quando eu compilo esse código no Visual C++ 8:

Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 14.00.50727.42 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

vc6_bug.cpp
vc6_bug.cpp(49) : error C2679: binary '=' : no operator found which takes a 
                  right-hand operand of type 'TEST *' (or there is no acceptable conversion)
        vc6_bug.cpp(35): could be 'CStupidPtr &CStupidPtr::operator =(const CStupidPtr &)'
        with
        [
            T=TEST
        ]
        while trying to match the argument list '(CStupidPtr, TEST *)'
        with
        [
            T=TEST
        ]

Pois é, o VC6 já está bem velhinho...


Em 25/11/2005 03:42, por Rodrigo Strauss


  
 
 
Comentários
Wanderley Caloni Junior | website | em 24/11/2005 | #
Lamentável! Pior que a própria existência de uma função que recebe uma referência const mascara o problema de conversão. Nessas horas até um internal compiler error seria bem-vindo. Tudo menos o silêncio. Parece que foi feito um try-catch em volta desse trecho e o catch ficou quietinho...

Interessante que se usarmos a notação direta o erro vem à tona (apesar de diferente do VC mais novo):

p.operator = ( new TEST() );

error C2784: 'class CStupidPtr<struct TEST> &__thiscall CStupidPtr<struct TEST>::operator =(const class CStupidPtr<T> &)' : could not deduce template argument for 'const class CStupidPtr<`template-param
Rodrigo Pinho Pereira de Souza | em 06/12/2005 | #
Estão discutindo na lista do boost, sobre parar o suporte para os compiladores que não suportam C++ 100%.

Em fim, eles querem reduzir o numero de Workarrounds(Gambiarras) dentro do boost.

Alguns argumentos que foi utilizado foram:
- Eu quero ver o boost compilando sem warnings ou erros
- Quero um código mais limpo


A Sugestões foram várias, até uma bem escabrosa:
- Se o desenvolvedor estiver usando um compilador não conformante, ele que faça o workarround. Teve um cara que usa Borland C que não gostou muito da ideia, mas .... acho que isto não vai ser aceito.

Porém, tenho certeza que eles não mais oferecerão suporte ao Visual C 6.0. Ou seja, qualquer erro que dê, quando vc compilar com VC 6.0, não poderá ser submetido como bug. Os test cases serão excluídos, e se vc quiser usar, vc que arrume o boost para se adequar ao boost.


Particularmente, eu até tentei usar o boost com o VC 6, mas é muito ruim vc compilar uma coisa simples, e ver mais de 100 warnings .... Como se sentir seguro que aquele código rodará sem falhas depois ????

Meu sonho, é que um dia a empresa em que trabalhamos decida usar o VC 7.1 ao menos, para que então, possa usar o boost.
rebarba rebarba
  ::::