Rodrigo Strauss :: Blog
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




