logo
Contato | Sobre...        
rebarba rebarba

Rodrigo Strauss :: Blog

follow us in feedly

C++: O esquecido operador vírgula

Um operador que é pouco usado, mas bastante útil em C++, é o operador vírgula. Resumidamente, ele avalia todas as instruções separadas pela vírgula, mas retorna sempre a última. Olhe um exemplo:

int a, b, c; b = 2; c = 3; a = (b,c);

Nesse caso, o valor da variável a será 3.

Uma das particularidades do operador vírgula, é que ele tem a menor precedência entre todos os operadores. Vamos tirar os parênteses:

int a, b, c; b = 2; c = 3; a = b,c;

Agora o valor da variável a será 2, já que pela precedência, a = b é a primeira expressão, e c é a segunda expressão.


Usos realmente úteis

O uso mais comum para o operador vírgula é inicializar mais de uma variável em um loop, como no exemplo abaixo:

int b,c; for(int a = 10, b = 20, c = 30 ; a < 10 ; a += --b, c++) { }

Nesse caso, nós declaramos a variável a e atribuímos o valor 20 à variável b.

Outro ótimo uso do operador vírgula é para fazer validação de casts não seguros. Existem lugares onde, apesar do parâmetro ser do tipo void* ou void**, você sempre deve passar um tipo determinado. Um exemplo clássico, é a função CoCreateInstance, que tem a seguinte assinatura:

STDAPI CoCreateInstance( REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID * ppv );

O último parâmetro, apesar de ser LPVOID* (ou seja, void**), espera um tipo definido. Ele quer um ponteiro para um ponteiro que possa ser convertido para IUnknown*.

Olhe o código a seguir, que tem um dos erros mais comuns em programação COM:

HRESULT hr; IUnknown* pUnk; hr = CoCreateInstance(CLSID_BLA, NULL, CLSCTX_ALL, IID_IUNKNOWN, (void**)pUnk);

Encontrou o erro? Na realidade, &pUnk deveria ser passado, e não somente pUnk como foi passado. Com isso você vai ganhar um belo GPF de presente. Como estamos usando cast C, o compilador não reclama, e nosso código é compilado sem problemas. E aqui não podemos usar static_cast, porque IUnknown** não pode ser convertido para void**.

Vamos agora a uma solução usando o operador vírgula e uma macro:

HRESULT hr; IUnknown* pUnk; // // IC = interface cast // #define IC(ppUnk) (static_cast(*ppUnk),(void**)ppUnk) hr = CoCreateInstance(CLSID_BLA, NULL, CLSCTX_ALL, IID_IUNKNOWN, IC(&pUnk));

O que fizemos aqui foi o seguinte: a macro primeiro checa se o apontado do que foi passado pode ser convertido para IUnknown*, usando static_cast. Essa primeira expressão (static_cast(*ppUnk)) vai verificar a validade do ponteiro em tempo de compilação, mas não surtirá nenhum efeito em tempo de execução. A segunda expressão ((void**)ppUnk) fará então o cast que deve ser feito para a chamada da função. Vamos agora tentar compilar com o mesmo erro do primeiro exemplo:

HRESULT hr; IUnknown* pUnk; #define IC(ppUnk) (static_cast(*ppUnk),(void**)ppUnk) hr = CoCreateInstance(CLSID_BLA, NULL, CLSCTX_ALL, IID_IUNKNOWN, IC(pUnk)); ----------------------------------- error C2440: 'static_cast' : cannot convert from 'IUnknown' to 'IUnknown *' No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called

E como todo ponteiro para interface COM pode ser convertido para IUnknown*, resolvemos nosso problema.

Se você quiser mais informações sobre verificações em tempo de compilação e códigos nesse estilo, procure sobre metaprogramming e estude muito, mas muito mesmo sobre templates.


Em 30/07/2004 18:57, por Rodrigo Strauss


  
 
 
Comentários
Algo a dizer?
Nome:


Site:


E-mail:


Escreva o número vinte e seis:


 Não mostre meu e-mail no site, não serve pra nada mesmo...

Comentário





Os comentários devem ser sobre assuntos relativos ao post, eu provavelmente apagarei comentários totalmente offtopic. Se quiser me enviar uma mensagem, use o formulário de contato. E não esqueça: isso é um site pessoal e eu me reservo o direito de apagar qualquer comentário ofensivo ou inapropriado.
rebarba rebarba
  ::::