logo
Contato | Sobre...        
rebarba rebarba

Rodrigo Strauss :: Blog

follow us in feedly

Talvez você ache interessante ver a lista com todos os posts ou minha lista com os melhores posts.

1, 2, 3 testando

Teste, 1, 2, 3.

Teste de formatação e de edição

"Mais um teste"

Em 21/07/2004 21:07 - Comentários (10)

Curso de Spam no SEBRAE

Esses dias encontrei um amigo que montou uma empresa de consultoria na área de telefonia. Ele veio me mostrar os impressos de divulgação da empresa, e me contou que estava fazendo divulgação via e-mail. Quando eu perguntei como ele conseguia os e-mails, ele falou que um conhecido dele o tinha emprestado um CD com milhões de e-mails e com os programas para enviá-los...

Eu expliquei que isso era spam, que era ilegal, que nenhuma empresa decente faz esse tipo de coisa, que as pessoas ficam irritadas com e-mails não solicitados, etc, etc. Foi quando ele argumentou que o e-mail era o meio mais barato de divulgar a empresa, e que se 0,01% dos milhões de e-mails derem retorno já é uma grande coisa.

No final ele me contou que fez um curso no SEBRAE, e foi lá que ele aprendeu as vantagens da "divulgação por e-mail"..

Em 22/07/2004 09:39 - Comentários (2)

Por isso que eu não compro livro traduzido....

Lançaram o famoso livro sobre "Refactoring" em português. Os caras tiveram a capacidade de traduzir "Refactoring" para refatoração... não é de matar? Por isso que eu compro direto da amazon...

Em 23/07/2004 18:34 - Comentários (0)

Tudo muda, o tempo todo

Eu estava lendo o Programming the Microsoft Windows Driver Model, do Walter Oney para tirar umas dúvidas sobre IRPs, e acabei abrindo o livro na parte que ele explica como funcionam as exceções em kernel mode. Ele também falava sobre o que acontece quando uma exceção é disparada e como usar blocos de __try/__except e __try/__finally para tratar uma exceção.

Nessa parte ele explica que quando uma exceção é disparada, o kernel sai procurando na pilha alguém que a trate. Depois ele fala que isso pode gerar problemas de performance, já que é necessário percorrer toda a pilha atrás de alguma função que lide com essa exceção. Eu também me lembro que muita gente não usa as exceções em C++ por causa de performance (fora os motivos apontados pelo Joel).

Performance, performance, performance. Hoje em dia, muitas dessas pessoas que falavam isso estão usando .NET, que tem um problema de performance muito maior, já que a runtime roda MUITO código além do seu. (Isso me lembra de programadores C reclamando que o compilador C++ gera código que eles não pediram, como o do copy constructor). É engraçado como a noção de prioridade performance/facilidade muda com o tempo. O jeito é não dar muito ouvidos e testar qual metodologia e linguagem que melhor resolve o seu problema.

Em 24/07/2004 04:54 - Comentários (0)

Como escrever um comparativo técnico tendencioso e sem fundamentos

Um sujeito que trabalha para a Oracle, fez um comparativo simplesmente ridículo entre ASP.NET e PHP. Escreveu um monte de abobrinhas malhando o ASP.NET e favorecendo o PHP, sem apontar dados concretos sobre os problemas que ele diz que o ASP.NET tem. Eu acho o PHP realmente muito bom, mas tentar divulgá-lo as custas de um comparativo ridículo desses, já é sacanagem.

E, pelo jeito, eu não fui o único que não gostou muito desse artigo. Olhe no final o rating do artigo, a maioria dos leitores também achou o artigo uma porcaria.... :-)

Em 26/07/2004 17:15 - Comentários (0)

Kernel mode dev lives!

A MSDN está publicando artigos sobre desenvolvimento de drivers com uma certa regularidade. Parece que o pessoal de kernel mode da Microsoft resolveu aparecer... O ultimo é sobre thread context e IRQLs.

Será que isso vai ajudar a reduzir nossa dependência do pessoal da OSR e da PCAUSA?

Em 26/07/2004 17:32 - Comentários (0)

Novos iPAQs

Meu velho 3650 parece uma carroça velha perto dos novos iPAQs. Um deles tem um processador de 624 Mhz, mais do que o meu Pentium III 600 que eu estava usando para desenvolver com Visual Studio.NET / WinDbg / VMWare até uma semana atrás.

Eu acho que não vai demorar muito para que um iPAQ rode uma máquina virtual com um Windows 9x/NT dentro. Eu já li alguma coisa, não me lembro onde, dizendo que estavam portando o bochs para PocketPC.

Em 26/07/2004 17:57 - Comentários (0)

História do Pentium

O pessoal da ArsTechinica publicou um ótimo artigo sobre história do Pentium, sob o ponto de vista técnico e histórico. Bem interesante, principalmente para quem é interessado em arquitetura de computadores e tem curiosidade sobre o funcionamento de processadores.

Em 26/07/2004 18:13 - Comentários (0)

Lembra daquelas histórias de suporte?

Quem nunca juntou os amigos para tirar um sarro lembrando daquelas histórias de suporte e de perguntas estúpidas de usuários? Encontrei um site recheado dessas histórias. Olhe uma:

Cliente: "Eu quero meus CDs de volta."
Suporte: "Seu CD está em um drive com defeito?"
Cliente: "Só um que não."
Suporte: "O que?"
Cliente: "Não é uma unidade 4x? Então, eu já coloquei 3 CDs, mas ele não aceita o quarto e nem devolve meus outros 3."

Em 29/07/2004 17:29 - Comentários (0)

10 ferramentas que todo programador .NET deve ter

Na MSDN Magazine desse mês, saiu um artigo sobre as 10 ferramentas que todo programador .NET deve ter. É um artigo bem resumido, mas que é legal para quem precisa somente das referências.

Como um continuação desse artigo, eu recomendo o livro Coder To Developer. É um livro curto (+-350 páginas) que explica um pouco da metotodologia por trás das ferramentas e fala sobre como fazer um software do início ao fim, do projeto ao setup.

Em 29/07/2004 18:08 - Comentários (1)

Por que os administradores de rede não se limitam a administrar a rede?

Era uma vez uma empresa. Era uma vez uma reunião entre os gerentes da área de desenvolvimento e os administradores de rede. Tudo corria bem na reunião, até que um administrador de rede diz a seguinte pérola:

- Estamos pensando em bloquear o MSN Messenger, porque ele acaba com a produtividade das pessoas.

Depois disso, o gerente de desenvolvimento tenta explicar que o MSN Messenger agiliza o trabalho, que ele gerencia pessoas que estão em outras unidades da empresa e que essa é uma forma ágil de comunicação, etc...

Agora, a pergunta: O que um administrador de rede entende de produtividade? Ele é um administrador de rede, um psicólogo ou um profissional de RH? Provavelmente, esse administrador de rede só usa o MSN Messenger para bater papo com os amiguinhos mesmo...

Em 29/07/2004 22:24 - Comentários (65)

"Windows Internals" no forno!

Hoje recebi a newsletter do site Sysinternals, dizendo que o Mark Russinovich e o Dave Solomon estão quase terminando o "Windows Internals", que é a próxima edição melhorada do Inside Windows 2000. Essa nova versão cobrirá as modificações feitas no kernel do Windows XP e 2003, e trará mais capítulos sobre resoluções de problemas e uso de dumps.

Em 30/07/2004 17:59 - Comentários (0)

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 - Comentários (0)

Iniciando o desenvolvimento básico de drivers

Pois é, o pessoal da Microsoft resolveu retirar o desenvolvimento de drivers do ostracismo. Republicaram o artigo "How to develop a Windows Driver". Um artigo bem curto, que fala o básico sobre desenvolvimento de drivers: arrumar uma cópia do DDK, usar o checked build, compilar usando o BUILD, e, é claro, certificar seu driver junto à Microsoft.

Em 01/08/2004 02:40 - Comentários (0)

Dicas para particionamento e organização do HD, parte 1

Eu tenho visto por aí muita gente com HDs gigantescos, todos com uma partição só. Apesar de isso não causar problemas no uso diário, um melhor particionamento pode ajudar bastante quem precisa de performance e organização. Por isso eu resolvi escrever alguma coisa sobre isso. Se ficar bom, talvez eu coloque mais água no feijão e transforme isso em um artigo. (Antes eu tenho que terminar o WinDbg parte 3).

Dica 1: nunca crie somente uma partição

A primeira dica é a mais simples. Qual o motivo? Com uma partição só, fica mais difícil organizar suas coisas e mais difícil de fazer backup e seu HD fica mais fragmentado.

Crie no mínimo duas partições, uma para o sistema operacional e para os programas, e outra para os seus dados. Instale o sistema operacional e os programas que você mais usa na primeira partição e desfragmente a partição. Guarde seus dados na outra partição.

Dica 2: Mude as pastas padrão para o "Meus Documentos", "Favoritos", etc

Por padrão, o Windows coloca a pasta "My Documents" em "c:\Documents And Settings\[username]\My Documents". Isso torna infernal o processo de backup, já que se você usar xcopy para fazer backup você terá que digitar esse caminho imenso ou colocá-lo no .BAT.

Resolva o problema: baixe o TweakUI no site da Microsoft, entre em My Computer >> Special Folders e saia mudando. Mude o "My Documents" para algo como "d:\rodrigo" e "My music" para algo como "d:\mp3". Mude também a localização do "Desktop" para dentro de "My Documents", como "d:\rodrigo\desktop". Menos uma pasta separada para você lembrar na hora do backup. E a pasta do desktop é a mais fácil de esquecer na hora do backup.

Use sempre nomes fáceis para as pastas, e as coloque na partição de dados. Assim, você pode programar o backup para copiar todas essas pastas, que são mais fáceis de lembrar. Um dia você vai decidir formatar o HD, e assim ficará mais fácil lembrar o que copiar, e você terá menos medo na hora de escolher a opção "Yes, format my entire HD now!".

Dica 3: Faça backup, faça backup, faça backup, faça backup, faça backup, faça backup, faça backup

Nunca é suficiente dizer. As pessoas geralmente só lembram do backup quando alguma desgraça acontece. O backup não precisa ser em uma fita DAT com um software de milhares de dólares. Um simples arquivo RAR ou ZIP com seus arquivos mais importantes enviado para um disco virtual já resolve o problema. Você pode fazer o backup dos seus documentos uma vez por semana, e das mp3 a cada mês. O importante é que você faça isso regularmente.

Dica 4: Teste o backup!

Já vi o caso de uma empresa que fazia o backup regularmente, mas nunca testou se o backup era restaurado corretamente. Adivinhe o que aconteceu na hora de restaurar o backup? Você pode esquecer de configurar a ferramenta de backup para incluir as subpastas. Erros simples como esse se tornarão catastróficos na hora que você precisar do backup.

Futuramente...

Diferenças entre NTFS e FAT32, qual tipo usar em que caso, usando 3 ou mais partições

Veja também
Dicas para particionamento e organização do HD, parte 2
Dicas para particionamento e organização do HD, parte 3
Verdades e mitos sobre NTFS e FAT

Em 03/08/2004 06:14 - Comentários (1)

C++: a linguagem mais poderosa para programação .NET

O artigo C++: The Most Powerful Language for .NET Framework Programming só ajuda a confirmar a minha opinião de que, com as novas managed extensions incluídas no Visual C++ 8.0 (Visual Studio 2005), eu nunca terei que usar outra linguagem para programar em .NET.

Continuarei com os templates, com o pré-processador, com o otimizador, com a possibilidade de usar API Win32 sem precisar usar DllImport (que é o Declare do VB6, nada mais, nada menos), possibilidade de alocar objetos na pilha, etc. Sem contar o gerenciamento de projeto, build steps, opções para configurar compilador e linker, o respeito que a Microsoft tem com programadores Visual C++, etc.

Ah, e é claro, o fato de que a integração de controle de versão (Source Safe, Clear Case) funciona com o Visual C++ e NÃO FUNCIONA com VB.NET.

Em 04/08/2004 17:27 - Comentários (4)

Porque programar drivers (kernel mode) é mais legal do que programar aplicativos (user mode)
  • Você está dentro do kernel do sistema.
  • Você precisa de um bom conhecimento da arquitetura do kernel do Windows. Isso torna você um melhor programador user mode.
  • Programação em kernel mode é algo crítico. O BUILD configura o compilador C para tratar warnings como errors.
  • Você não pode errar. Um simples GPF ou divisão por zero é tela azul na hora!
  • Em kernel mode você é obrigado a usar seus conhecimentos de ciências da computação: arquitetura de computadores, listas ligadas, etc.
  • Os samples do DDK (Driver Development Kit) são mais bem organizados e mais bem comentados do que os samples de user mode. O código também é muito mais bonito. :-)
  • A API do kernel do Windows (Native API) é muita mais concisa e bem organizada do que a Win32 e algumas partes do .NET Framework.
  • Não existe programação para banco de dados em kernel mode. Nada de VB, nada de ADO.NET, nada de DataAdapter, nada de SQL, nada de fazer procedures. Praticamente o paraíso... :-)
  • Em kernel mode, você só pode usar Assembly, C ou C++. E muitos programadores pregam que você use somente C.
  • Temos ferramentas como o Driver Verifier e o Windows Checked Build para verificar se estamos fazendo tudo OK. Hoje temos o Application Verifier para user mode, mas isso é novo e quase ninguém conhece. O pessoal que programa em kernel mode (pelo menos os que sabem o que estão fazendo) usa o Driver Verifier sempre.
  • Especialização. Poucas pessoas programam em kernel mode.

Em 05/08/2004 04:46 - Comentários (2)

Depois dizem que computação é uma ciência exata

Eu ainda vivo na idade da pedra, e como tal, minha câmera fotográfica não é digital. Tirei umas fotos na semana passada e decidi tirar meu scanner da caixa para enviar algumas fotos para os amigos.

Meu scanner (que eu saiba) não tem driver para Windows XP, somente para Windows 2000. Fácil: pego a VMWare com Windows 2000 que eu uso pra fazer debug dos drivers que eu faço, instalo o scanner, software do scanner e pronto. Isso é o que eu pensava em minha vã inocência.

Subi a VMware e espetei o scanner na USB. O Windows XP reconheceu o "VMWare USB Device" e cinco segundos depois meu micro reinicia. Antes de xingar o scanner ou qualquer outra coisa, eu pensei como eu sou estúpido por não ter configurado o Windows para não reiniciar automaticamente depois de um BugCheck (tela azul) e para fazer o dump completo.

Com o micro reiniciado, configurei tudo, subi a VMware e espetei o scanner. Como esperado, tela azul de novo, e 5 minutos depois eu tinha o dump. Reiniciei o micro novamente e abri o dump no WinDbg para descobrir quem eu deveria xingar: a VMware, a Microsoft ou a Genius, fabricante do scanner.

Esperei uns 20 minutos até o WinDbg baixar os symbols para os drivers da minha máquina. Uma longa espera para alguém que só que encontrar um culpado. Consegui ver que algum driver tentou liberar memória duas vezes e disparou um BAD_POOL_CALLER. Quando vou olhar qual foi o driver que fez a besteira, tive uma infeliz surpresa: foi o firewall (Tiny Personal Firewall)...

Isso siginifica que o driver está fazendo hook do que não deve, até porque o TCPIP.SYS nem estava na pilha. Ou eu arrumo um outro firewall ou termino logo o meu...

Depois que você descobre como são feitas as salsichas, o molho de tomate e os drivers de antivírus com "real time scan", seus hábitos mudam bastante... :-)

Em 05/08/2004 19:06 - Comentários (1)

Visual C++ 2005 Managed Extensions: o início

Hoje eu resolvi fazer minhas primeiras experiências com Managed C++ 2005, para ver se é realmente simples como eu acredito que seja. Minha primeira experiência ridícula, serve para ilustrar como a compilação é quase tão simples como C# ou VB.NET.

Código fonte
int Main() { System::String^ str; str = L"Eu queria estar na praia"; System::Console::WriteLine(str); return 0; }
A compilação
cl mcpp1.cpp /clr:pure /link /entry:"Main" /subsystem:console Microsoft (R) C/C++ Optimizing Compiler Version 14.00.40607.16 for Microsoft (R) .NET Framework version 2.00.40607.16 Copyright (C) Microsoft Corporation. All rights reserved. mcpp1.cpp Microsoft (R) Incremental Linker Version 8.00.40607.16 Copyright (C) Microsoft Corporation. All rights reserved. /out:mcpp1.exe /clrimagetype:pure /entry:Main /subsystem:console mcpp1.obj
O resultado
Eu queria estar na praia

Simples, rápido e fácil. Agora eu tenho um autêntico e legítimo executável 100% managed para ser consumido pelo .NET Framework 2.0 beta 1. Agora é só esperar terminar o download do SDK do framework 2.0, e usar o ildasm para ver se está realmente tudo OK.

Mas, na verdade, eu gostaria de estar na praia.

Em 06/08/2004 22:42 - Comentários (0)

Como usar o Visual C++ 2005 Express para fazer programas SUBSYSTEM:Windows

No artigo sobre WinDbg, eu disse que caso o leitor não tivesse o Visual C++, ele poderia baixar o Visual C++ 2005 Express e o Platform SDK para fazer programas Windows. Agora eu vou explicar os passos necessários para isso.

1. Baixe o Visual C++ Express 2005.

2. Instale o Microsoft Platform SDK via web. Instale somente o [Core SDK >> Build Environment] e o [Internet Development SDK >> Build Environment]. Infelizmente, mas infelizmente mesmo, não é possível usar o Firefox para fazer a instalação, só Internet Explorer mesmo. (igual ao novo site da MSDN brasileira... shame...).

3. Agora vamos configurar o Visual C++. Menu "Tools" >> "Options". No TreeView, "Projects And Solutions" >> "VC++ Directories". Depois, no ComboBox "Show Directories for:" insira Program Files\Microsoft SDK\include em "Include Files" e Program Files\Microsoft SDK\lib em "Library Files".

4. Reinicie o Visual C++ 2005 Express

Para testar, abra o Visual C++ e crie um novo projeto "Win32" >> "Console Application" (é isso mesmo, console application). Quando abrir a tela com as configurações do novo projeto, escolha "Windows Application". Caso você tente compilar o projeto, verá vários erros de linker, já que o projeto não está configurado para fazer link com as libs das dlls Win32.

Agora vamos arrumar o problema do linker: entre nas propriedades do projeto, "Configuration Properties" >> "Linker" >> "Input". Em "Additional Dependencies", coloque "kernel32.lib user32.lib gdi32.lib" (assim mesmo, separadas por espaços). Isso deve ser feito em todos os projetos Win32.

Pronto. Agora divirta-se!

Em 09/08/2004 16:53 - Comentários (2)

Depois da praia, vamos nos purificar

No meu post de sexta-feita, eu usei o parâmetro de linha de comando "/clr:pure" para compilar o meu "Eu gostaria de estar na praia" (convenhamos que é melhor do que Hello World...). Agora que eu terminei de baixar o .NET Framwork SDK 2.0, usei o ildasm para verificar o código gerado. Olha o que temos:

Reconhecemos as seções do executável e várias funções da runtime do C/C++. Isso pode deixar um programador purista triste, já que carregamos algumas coisas que são do velho Visual C++.

Agora vamos mudar o parâmetro para "/clr:safe". Olhe o que temos agora:

Agora sim, um executável .NET puro, suficiente para agradar os puristas. Lembre-se que usando "/clr:safe", não podemos usar nada que não seja seguro e verificável pela runtime. Um simples "#include " irá gerar milhares de erros, já que as funções e estruturas da Win32 API não são verificáveis.

Em 10/08/2004 00:41 - Comentários (0)

PREFAST rocks!

Para quem não sabe, o Prefast é um analisador estático de código fonte, que vem junto com o DDK do Windows Server 2003. Ele foi feito para pegar erros de lógica, que o compilador não pega. Olhe esse código:

NTSTATUS s; IO_STACK_LOCATION* pIrpSp; pIrpSp = IoGetCurrentIrpStackLocation(Irp); ... switch(pIrpSp->MinorFunction) { case FUNC1: s = OnConnect(DeviceObject,Irp,NextDevice); break; case FUNC2: OnDisconnect(DeviceObject,Irp,NextDevice); break; default: IoSkipCurrentIrpStackLocation(Irp); s = IoCallDriver(NextDevice,Irp); } return s;

Achou o erro? O PREFAST achou. Olhe o log dele:

dispatch.cpp(197) : warning 1: Using uninitialized memory 's'. problem occurs in function 'OnDeviceInternalIoControl' Path includes 6 statements on the following lines: 177 178 180 182 189 197

A linha 197 é a que chama "OnDisconnect". Repare bem que o retorno dessa função não é atribuído à variável s. Caso o valor de "pIrpSp->MinorFunction" seja "FUNC2", a variável s nunca seria inicializada, e minha função retornaria o lixo que estivesse na pilha.

Em 13/08/2004 04:22 - Comentários (0)

Computação não é uma ciência exata, parte 2

Passei quinta e sexta da semana passada fazendo um dos serviços mais enfadonhos que um programador pode fazer: documentar um sistema que nem fui eu que criei. É quase uma tortura passar o dia inteiro com o Word aberto e com o Visual Studio fechado...

Estava eu terminando a documentação, quando enrosquei o pé no cabo de força do estabilizador e tudo apagou. É claro que eu nem tinha salvo a documentação, já que eu deixo meu micro em "hibernation" todo dia e o Word 2003 tem os recursos de recuperação de documentos e salvamento automático. Despreocupado, liguei o micro novamente e abri o Word na esperança de ver meu documento lá, do jeito que estava antes da infeliz fatalidade.

Pois é, ele não estava lá. Procurei os ~*.doc nos temporários do Windows e nada. Acredito que eu nem preciso descrever meu desespero, já que fazer documentação uma vez é enfadonho, e fazer duas vezes é torturante.

Sem mais opções, recomecei a escrever a documentação, e lembrei que o Word 2003 é o primeira versão que eu encontrei bugs, principalmente de redesenho de tela. Eu uso o Word desde a versão 6.0 para Windows 3.11, e nunca encontrei nenhum problema. Até aparecer o Word 2003. O pior não é o bug do redesenho, e sim o que me fez perder o documento, mesmo com a opção de salvamento automático ativada.

Passou o fim de semana, terminei minha segunda versão da documentação, e resolvi instalar o Service Pack 1 do Office 2003. Perguntei a um amigo se esse Service Pack solicitava o CD durante a instalação, porque todos os updates do Office que eu vi até hoje pediam. Ele me disse que no micro dele e de um outro amigo nosso, o Windows Installer não pediu o CD. Então, eu resolvi arriscar.

É claro que ele pediu o CD, e é claro que o CD do Office estava na sede da empresa, e não onde estou prestando consultoria. Afinal, é para isso que serve a Lei de Murphy.

Cancelei a instalação, fiquei assistindo a barra de progresso do Windows Installer retroceder enquanto eu torcia para que meu Office continuasse funcionando, para que eu não precisasse esperar que alguma boa alma me trouxesse o CD do Office ou que eu tivesse que fazer o download da MSDN.

Depois de cancelar o update, abri o Word para testar se tudo estava funcionando. Apareceu a tela do Windows Installer esperada, algumas dezenas de segundos e pronto, lá estava o Word aberto, COM O MEU DOCUMENTO PERDIDO SEMANA PASSADA RECUPERADO

Em 19/08/2004 07:01 - Comentários (0)

Por que você programa?

Tentando encontrar uma forma de explicar para um entrevistador como eu sou fascinado por programação sem parecer mentiroso ou artificial, eu me lembrei de uma coisa que li a muito tempo atrás: "Why Do You Code?".

Precisa existir algum motivo lógico? No meu caso é simplesmente inexplicável, quase uma doença. Caso alguém queira tentar fazer um diagnóstico, vou enumerar alguns sintomas:

  • Meu micro tem 512MB de RAM e uma Geforce FX 5200 128MB. Eu até fico feliz que minha máquina rode Doom3 numa velocidade razoável, mas esse não é motivo da minha felicidade. Eu fico feliz porque posso abrir duas máquinas virtuais, o Visual Studio.NET, a MSDN, o Firefox com milhares de tabs e o WinDbg ao mesmo tempo.
  • Eu trabalho com programação o dia inteiro. Sabe o que eu faço pra me divertir quando chego em casa? Abro meu Visual C++ e trabalho em um software que eu estou fazendo a 10 meses, envolvendo computação distribuída e device drivers. Ou seja, continuo programando e estudando.
  • Fico feliz pelo fato da minha HD ter 120GB. Assim eu posso instalar sem preocupação a MSDN, o DDK, o Platform SDK, o Visual Studio 6, o Visual Studio.NET, posso baixar milhares de fontes do Source Forge, criar várias máquinas virtuais para testar softwares como o Visual Studio 2005, Yukon e sistemas operacionais, posso instalar Linux ou até mesmo o Longhorn em outra partição, etc, etc.
  • Fico feliz por ganhar um salário bom. Assim eu posso gastar dinheiro comprando mais livros sobre programação todo mês na Amazon.
  • Se hoje eu tenho um bom conhecimento de inglês, é de tanto ler livros sobre programação e aperfeiçoar o inglês para estudar mais sobre programação e para trabalhar em projetos envolvendo programação no exterior. Um dia um amigo me disse que queria virar programador e perguntou por onde começar. Eu disse para começar estudando inglês.

Essa matéria que eu citei conta a história de um sujeito que queria aprender a programar mas não tinha micro, só uma WebTV (um finado aparelho no qual você podia navegar pela web com sérias limitações e enviar/receber e-mails ligando o aparelho à linha telefonica e à TV, quase um TK90X). Então ele escrevia os fontes C em um e-mail, e enviava para um amigo que tinha computador. Esse amigo compilava e mandava os erros de volta. Assim ele começou a programar.

Esse sujeito parece maluco? Pois é, mas eu comecei mais ou menos assim. Eu aprendi a programar com 12 anos, quando minha mâe trabalhava na UNESP de Botucatu e eu resolvi fuçar em computadores no polo computacional de lá. Depois eu resolvi pegar uns livros para ler. O que você faria se visse um garoto de 12 anos de idade na biblioteca da sua faculdade procurando livros de programação? Hoje eu fico imaginando como devia ser estranho...

Então eu peguei um livro de BASIC e comecei a programar em GW-BASIC usando os micro de lá. Mas eu não tinha micro em casa, não podia programar sempre. Não demorou muito tempo para que eu começasse a escrever programas em um caderno, na minha casa. Eu tinha 12 anos de idade...

Tem alguma explicação para isso? Existem pessoas que programam para viver, e pessoas que vivem para programar. Eu me encaixo na segunda categoria, e conheço algumas pessoas assim.

Por que eu programo? Sinceramente, eu não sei explicar. Só espero que o entrevistador entenda...

Em 19/08/2004 08:18 - Comentários (28)

Planeta C++

O criador do C++ (Bjarne Stroustrup) mantém uma pequena lista dos softwares que foram feitos usando C++. Acredito que mais de 95% dos softwares que rodam em plataforma Windows são feitos em C e C++. Não sei se existe uma estatística oficial sobre isso, mas a esmagadora maioria dos software conhecidos são feitos em C ou C++.

Para as pessoas que trabalham diretamente com C ou C++ isso parece óbvio, mas como no Brasil pouca gente trabalha com isso, esse post deve trazer a resposta para a pergunta que os programadores VB e Java nunca pararam para fazer.

Não se esqueçam de olhar o item "Microsoft".

Em 19/08/2004 20:34 - Comentários (4)

Diversão para programadores de drivers

Muito melhor do que jogar paciência, é fazer as palavras cruzadas da OSR e testar seu conhecimentos sobre desenvolvimento de drivers para Windows. Alguém poderia providenciar mais alguns desses sobre Win32 e C++. E talvez até .NET... :-)

Além disso, eles publicaram um artigo interessante sobre como trocar os arquivos do Windows (sem que o Windows File Protection coloque os originais de volta) usando os recursos nativos do kernel debugger. Por exemplo, o IFS Kit (usado para fazer drivers de file system) vem com os fontes do fastfat.sys, que é o driver do sistema de arquivos FAT (sim, o mesmo do Windows, feito pelo Gary Kimura). É interessante compilá-lo em modo checked e substituir o do Windows para ver como funciona, mas o WFP não deixa que você troque o arquivo. Existem algumas gambiarras possíveis, como editar uma DLL do Windows usando um editor hexadecimal, mas até eu que estou acostumado a fazer uns patches no kernel do Windows usando o WinDbg acho isso um pouco de exagero...

Em 25/08/2004 17:43 - Comentários (3)

Dicas para particionamento e organização do HD, parte 2

Nessa segunda parte, darei uma pequena explicação do sistema de arquivos FAT. Depois faremos uma comparação entre FAT e NTFS para podermos decidir qual tipo é melhor para qual finalidade.

FAT é um sistema de arquivos que foi criado na década de 80, originalmente para ser usado em disquetes. Foi o sistema de arquivos padrão da Microsoft desde as primeiras versões do MS-DOS, e continua sendo o mais utilizado até hoje, sendo suportado pelas últimas versões do Windows (XP, Server 2003 e Longhorn) e por diversos outros sistemas operacionas, como Linux e OS/2.

Primeiro vamos resumir a história do FAT:
  • FAT12: Primeira versão, foi projetada para disquetes. Nomes de arquivo no formato 8.3. O "12" vem do número de bits usados para numerar os clusters;
  • FAT16: Com o aparecimento dos primeiros HDs (naquele tempo alguns chamavam de "Winchester", lembram?), o FAT12 não era mais suficiente. Aumentaram então o número de clusters para 16 bits;
  • VFAT: Com o surgimento do Windows 95, a Microsoft queria suportar nomes de arquivos longos, como no OS/2 (que usava HPFS). Então foi feita uma adaptação no FAT para ele suportasse nomes longos e ainda ficasse compatível com os aplicativos antigos;
  • FAT32: Com o surgimento dos HDs maiores, o tamanho máximo de 2GB para um partição FAT16 tornou-se uma limitação crítica. Então o número de clusters foi aumentado para 32 bits. Apesar de resolver o problema, o novo sistema de arquivos não era compatível com os sistemas operacionais mais antigos. O Windows NT4, por exemplo, não suporta FAT32 nativamente. O FAT32 também suporta nomes longos, assim como o VFAT (não sei se podemos chamar de VFAT32...).

Por ser um sistema de arquivos antigo, muitas das funcionalidades presentes nos sistemas de arquivos mais modernos não estão disponíveis. Para facilitar, vou enumerar as vantagens e desvantagens do FAT32:

Vantagens:

  • Como a estrutura é simples, o acesso aos arquivos é rápido;
  • É compatível com praticamente todos os sistemas operacionais, como Windows 98, Windows NT 4, Linux, OS/2, etc.

Desvantagens:

  • Não tem recursos de segurança. Não permite que o usuário ou administrador limite o acesso de determinados arquivos a determinados usuários;
  • O controle anti falhas é muito simples e pouco eficiente. O FAT usa como sistema de segurança para corrompimento dos metadados uma cópia backup da tabela de alocação. Isso já se mostrou ineficiente. Uma simples queda de energia durante uma operação que modifique os metadados pode tornar a partição inacessível.

Em todas as comparações que eu fizer daqui para frente, somente o FAT32 será considerado. O FAT16 tem a limitação de 2GB no tamanho da partição, motivo suficiente para ser desconsiderado.

Na terceira parte eu descreverei o NTFS.

Veja também
Dicas para particionamento e organização do HD, parte 1
Dicas para particionamento e organização do HD, parte 3
Verdades e mitos sobre NTFS e FAT

Em 26/08/2004 22:47 - Comentários (0)

Notícias do Windows Longhorn

Microsoft decide que o WinFS não fará parte do Longhorn e que o WinFX, Avalon e Indigo serão disponibilizados para Windows XP e 2003. Acho que isso vai dar muito o que falar. Fico feliz que o "Raymond Chen camp" venceu mais uma, mas acho que a situação vai ficar bem complicada. .NET Framework 2.0 + WinFX + Avalon + Indigo, imaginem só o tamanho do download de tudo isso. Mas isso me faz pensar em algumas coisas:

  • Já era sabido que o Indigo seria portado, isso só foi colocado no mesmo pacote de divulgação;
  • Se o Avalon vai ser portável para Windows XP, isso quer dizer que não será possível muita dependência do Avalon em relação ao kernel e ao DirectX, a não ser que ele faça parte de um Service Pack (o que eu duvido que aconteça). Eu acho o managed code lento pra manipulação de gráficos. Mas isso poderia ser resolvido no Longhorn, atrelando o Avalon diretamente com o DirectX. Assim a maioria do trabalho seria feito em kernel mode, o que diminuiria a quantidade de trabalho feito em managed code;
  • Acho que isso tende a deixar as APIs managed bastante atreladas ao Win32, a não ser que a Microsoft faça do WinFX um subsystem separado. Acredito que um subsystem managed para user mode, contando com boa parte da implementação em kernel mode apoiado em DirectX iria ficar muito interessante e com um desempenho muito bom. Mas não acho que a Microsoft vá por esse lado (não testei o Longhorn ainda), porque isso tornaria difícil manter a compatibilidade e fazer a integração com o Win32, já que o WinFX estaria fora do win32k.sys;
  • Mais uma vez o Bill Gates terá seu tão sonhado sistema de arquivos adiado... Mais uma vez...
  • Não teremos nenhuma revolução como a Microsoft diz, mas sim uma evolução. Avalon será a [GDI+++ + DirectX], Indigo o Remoting/SOAP vitaminado, e o WinFX será um WinForms 3.0. Ainda muito bom, mas sem o exagero característico do pessoal de marketing da MS.

[editado em 10/09/2003. "WinFX será um WinForms 2.0" >> "WinFX será um WinForms 3.0"]

Em 30/08/2004 07:43 - Comentários (0)

Como sempre, Carmack nos surpreende

Existe uma palavra que define Doom3: medo. Além dos gráficos muito bons, que todo mundo fala, o enredo e ambiente do jogo também são ótimos. O jogo realmente dá medo, e eu não consigo jogar mais de uma hora por causa da tensão. John Carmack mais uma vez prova que continua sendo um gênio.

Agora vou ler um livro para me acalmar depois de uma sessão de Doom3...

Em 11/09/2004 06:31 - Comentários (0)

Dicas para particionamento e organização do HD, parte 3

Como prometido, agora na terceira parte vou explicar um pouco sobre NTFS. Acho desnecessário dizer, mas isso é só o básico sobre os sistemas de arquivos. Caso você precise de maiores detalhes, procure a especificação ou maiores informações na web.

O NTFS (NT File System) foi criado especialmente para o Windows NT, logo na sua primeira versão. Houve uma grande discussão durante o projeto do Windows NT sobre a viabilidade de criar-se um novo sistema de arquivos ou usar/adaptar um existente na época, como o FAT ou o HPFS. Sabemos hoje a decisão que foi tomada.

Os únicos sistemas operacionais realmente compatíveis com NTFS são os da linha Windows NT: Windows NT 3.x, Windows NT 4.0, Windows 2000 (5.0), Windows XP (5.1), Windows Server 2003 (5.2) e a próxima versão, ainda em alfa, Windows Longhorn (6.0). Existem drivers para Linux, mas somente readonly. Para Windows 9x existe o NTFS for Windows 98 dos nossos geniais amigos da SysInternals, que usa os drivers do Windows NT para acessar as partições NTFS.

Vamos às vantagens e desvantagens do NTFS.

Vantagens:

  • NTFS possui journaling, que controla todas as modificações nos metadados da partição. Os metadados são as informações de como e onde estão os dados e os arquivos na partição. Todas as modificações são gravadas em um log ANTES de serem efetuadas. Assim, caso um problema (como queda de energia) aconteça durante a gravação, é possível terminar ou reverter a operação na próxima vez que o computador for ligado. Note que isso garante a integridade da partição, mas não a integridade dos dados. O controle de dados é deixado por conta da aplicação. Os bancos de dados (como SQL Server ou Oracle) fazem controle de integridade de dados;
  • Possui suporte a metadados para os arquivos e pastas, o que permite, entre outras coisas, que as informações de permissão do arquivo sejam gravadas diretamente na partição. Por isso que você consegue determinar permissões de acesso em uma partição NTFS, e não em uma partição FAT (usando Windows NT/2000/XP, é claro);
  • Possui suporte a streams alternativas. Além de gravar no arquivo, você pode criar um novo fluxo de dados para o arquivo e gravar informações separadamente. Isso permite, por exemplo, que você mantenha diferentes versões dos dados do arquivo em streams diferentes;
  • Tem menos problemas com fragmentação do que o FAT;
  • Possui suporte nativo a criptografia e compactação (a partir do Windows 2000).

Desvantagens:

  • Só é 100% compatível com o Windows NT/2000/XP.

(tentei encontrar mais desvantagens, mas não achei. Existem boatos de que FAT é mais rápido do que NTFS, mas não achei nenhum estudo realmente conclusivo sobre isso)

Veja também
Dicas para particionamento e organização do HD, parte 1
Dicas para particionamento e organização do HD, parte 2
Verdades e mitos sobre NTFS e FAT

Em 13/09/2004 05:43 - Comentários (2)

Pode acontecer algum dia, em algum computador

Mensagem de erro mostrada por um programa de instalação quando executado no Windows Me :

Se ao invés de linguas nórdicas, o Raymond Chen estivesse estudando português, e ele por acaso lesse meu weblog, ele ficaria muito bravo... :-)

Em 13/09/2004 05:51 - Comentários (0)

Parece brincadeira, mas a Microsoft fez

Parece brincadeira, mas um dia depois de escrever o post tirando sarro do Windows Me ("Pode acontecer algum dia, em algum computador"), com uma mensagem que pouco provavelmente apareceria em um software comercial, encontrei essa mensagem no Microsoft Word 2003:

Essa mensagem estaria correta se eu não tivesse um browser instalado. Mas o detalhe é que o link que eu cliquei ("More information about this error message online") foi aberto no Firefox, que é meu browser padrão...

Além disso, o Office 2003 só roda em Windows 2000 ou superior, o que descarta a possibilidade de não haver um browser instalado.

Em 14/09/2004 08:27 - Comentários (0)

Mais uma mudança de paradigma

Quando eu comecei a acessar a Internet, em 1997, uma das primeiras coisas que você aprendia era que você deveria ser sempre anônimo. Além dos nicks para os chats, a grande maioria das pessoas tinham um e-mail com nick. Eu lembro de um colunista de uma conceituada revista de informática dizer que criar um e-mail com o próprio nome ou sobrenome era uma falta de criatividade. Hoje sabemos que isso não passa de estupidez.

Um fenômeno que eu estou percebendo de um tempo para cá é que as pessoas estão perdendo o medo da Internet, e ela está se tornando menos anônima.

Primeiros foram as páginas pessoais (faz tempo...). Todo mundo queria ter uma página pessoal, mesmo as pessoas que não tinham muito a dizer. Nem que fosse para colocar quais eram seus passatempos prediletos e onde você mora. Mas as pessoas eram receosas em colocar o nome completo ou mesmo uma foto, afinal, alguém poderia usar essas informações para fins ilegais. Lá as pessoas diziam o primeiro nome e do que gostavam.

Depois, bem depois, vieram os blogs. Além das pessoas colocarem suas preferências na rede, agora as pessoas colocam seu diário e sua opinião pessoal também. A nova mania são os fotoblogs, onde as pessoas colocam fotos em todas as situações possíveis e imagináveis. Agora as pessoas publicam sua rotina, com nome, sobrenome, e muitas fotos.

E agora, o Orkut. Lá você coloca nome, sobrenome, fotos e ainda diz quem é ou não é seu amigo. As pessoas participam de fóruns e discutem diversos assuntos (de sexo a bons bares para tomar chope alemão) com o nome estampado e um link para um profile quase sempre bem detalhado. Agora além de tudo isso, você diz quem são seus amigos, discute suas preferências e gostos abertamente. Coisa que muitas pessoas não fazem nem com os amigos no mundo real. Deixamos de ser anônimos, mas ainda usamos o fato de estar por trás de um computador para dizer coisas que não diríamos em outra situação.

E ultimamente, tenho visto que as pessoas entram em fórums de discussão usando o próprio nome e colocando um foto verdadeira como avatar. No MSN isso também tem sido mais constante.

Eu fico me perguntando o que virá depois...

Em 15/09/2004 06:46 - Comentários (1)

Somos todos estranhos

Lendo o blog do Raymond Chen, como faço todos os dias, encontrei um post muito interessante, comparando a arquitetura x86 com as demais arquiteturas disponíveis no mercado. Nesse post ele fala sobre as características da arquitetura x86, e que muitas delas não são encontradas em outras arquiteturas (como PPC, MIPS, Alpha e ia64). Como estamos acostumados com a arquitetura do x86, parece que essas arquiteturas são estranhas e exóticas. Mas não são.

Olhando a lista resumida do Raymond, vemos que a arquitetura x86 é limitada, por manter compatibilidade desde o 8086 (PC XT). E vemos também que a nova arquitetura da Intel, a ia64, agora compartilha diversas características com as outras arquiteturas. Inclusive algo que em termos de performance chega a ser óbvio, que é colocar os parâmetros em registradores, e não na pilha. Existe, dois artigos interessante sobre arquitetura ia64 na MSDN Magazine (aqui e aqui)

Mais uma prova de que nem sempre o melhor produto é o que ganha o mercado. O padrão Beta sempre foi melhor do que o VHS, mas ninguém usa. Essas outras arquiteturas são bem melhores que o x86, mas ninguém usa (em desktops).

Em 16/09/2004 19:17 - Comentários (0)

A um ano atrás eu teria orgulho disso, mas hoje...

São Paulo será a 2ª maior cidade do mundo em 2005

Em 16/09/2004 22:52 - Comentários (0)

Minha opinião sobre open source e o governo

Eu sou um programador que trabalha com tecnologia Microsoft. Uso Visual C++, C#. VB, SQL Server, etc. Gosto bastante dos softwares da Microsoft e o método que eles usam para desenvolvê-los. Mas não sou um defensor ferrenho da Microsoft. Existem alguns produtos deles que são ruins, eles tomam decisões erradas e têm algumas posições políticas pouco recomendáveis.

Como todo programador C/C++, sou bastante interessado pelo Linux. Programo em PHP e estou aprendendo Qt/GTK e Mono. Acho muito interessante que ele seja desenvolvido por programadores voluntários, que seja livre e com o código aberto. Mas eu não me importo muito com a ideologia do Linux. Eu tento julgá-lo pela qualidade, e não pelo puritanismo de usar Linux só pelo fato de não ser proprietário ou coisas assim. As empresas gastam dinheiro para desenvolver produtos e vendê-los. E eles querem garantir o direito deles a essa invenção/criação/descoberta usando patentes e coisas assim. Isso é absolutamente normal. (O sistema de patentes norte-americano é ruim e anormal, mas não vou falar disso agora). O software pode ser livre, mas a grande maioria dos outros bens de consumo (considerando que software é um bem) não são.

Eu sou contra a preferência do governo por software livre. E sou absolutamente contra a existência de qualquer lei nesse sentido, porque isso acaba com a concorrência. O critério para escolha de software pelo poder público deve ser o mesmo critério que ele usa para qualquer coisa que compra: preço e qualidade, custo e benefício. Ele não deve se prender a uma determinada "origem", seja ela o pessoal do open source, a Microsoft, a IBM ou qualquer outra empresa.

Além disso, com essa lei, o governo somente deixará de comprar serviços de algumas empresas para comprar de outras. Por acaso a IBM ou RedHat são mais boazinhas do que a Microsoft só porque promovem o Linux? O custo da licença de um software é só uma fração do seu custo ao longo do tempo. Além disso temos o custo de suporte e atualização. Isso quer dizer que, ao invés do governo ficar amarrado com a Microsoft, ficará amarrado com a IBM, RedHat, Conectiva ou qualquer empresa que trabalhe com Linux. É correto privilegiar essas empresas e seus parceiros? Uma empresa que dá suporte (help desk) Linux não necessariamente contribui com a comunidade open source. Por que uma empresa dessas deve ter preferência em detrimento de uma empresa que dê suporte Windows?

Mas sim, hoje existe privilégio por parte da Microsoft. As pessoas estão tão acostumadas a usar softwares Microsoft que muitas vezes não param para estudar se existe um alternativa que seja viável. Isso sem contar o orçamento de marketing da Microsoft. Se deve existir uma lei sobre escolha de software, ela deve exigir que o governo faça um estudo de um numero mínimo de alternativas de software para aquele problema específico. Então o governo vai comprar sistemas operacionais? Ok, então que eles façam um estudo da viabilidade de usar Linux ou Windows, estudar o custo-benefício da solução. O Linux é mais barato, mas tem problemas de compatibilidade com hardware e software. O Windows tem compatibilidade 100%, mas tem um alto custo com licenças e te deixa preso com somente um fabricante.

Resumindo, o critério deve ser qualidade e custo-benefício. Querem que o Linux seja usado por todos? Façam com que ele tenha qualidade. Do resto o mercado se encarrega.

Em 21/09/2004 01:02 - Comentários (8)

Tudo que eu já sabia

Para empresas, faltam profissionais qualificados na área de tecnologia

Em 05/10/2004 00:24 - Comentários (0)

Verdades e mitos sobre NTFS e FAT
Verdade: NTFS é mais seguro (quanto a perda de arquivos)

O NTFS conta com um sistema de journaling, que grava um log de tudo que está sendo modificado na estrutura da partição NTFS. Caso acabe a luz ou ocorra algum desastre similar, o NTFS termina (ou desfaz) as últimas modificações nos metadados da partição, deixando sua partição acessível. Quando acaba a luz e você usa FAT, só resta rezar enquanto religa máquina.

Mito: É mais fácil recuperar dados em uma partição FAT

Esse mito vem do fato que os disquetes de boot DOS e Windows 9x não acessam partições NTFS. Mas as pessoas geralmente esquecem que os disquetes de boot já não são essenciais. Todos os CDs do Windows 2000/XP podem ser usados para dar boot na máquina e acessar os arquivos da partição usando a opção de "Recovery".

Para esses casos específicos também existem as distribuições Linux que consegue fazer boot com interface gráfica, conexão com Internet e suporte a Pen Drive USB, tudo isso rodando diretamente do CD. Assim você pode copiar seus arquivos tranquilamente. Algumas permitem que você copie o Linux inteiro na RAM, retire o CD de boot e use o gravador de CD normalmente. A grande maioria das distribuições Linux hoje em dia vêm com o driver de NTFS (somente leitura). (A grande excessão é o Fedora Core, mas eles devem ter algum motivo político/ideológico para isso...)

Mito: FAT32 é mais rápido do que NTFS

Eu também tinha essa impressão. O fato do driver de FAT do Windows chamar fastfat.sys também ajuda. Mas eu nunca encontrei nenhum estudo conclusivo que dissesse isso. O que eu mais encontrei na Internet foram informações de que o NTFS é mais rápido do que o FAT em partições grandes. E como hoje em dia o menor HD que você consegue comprar é de 80 GB...

Mito: Partição NTFS não fica fragmentada

Realmente, a partição NTFS tem bem menos problema com fragmentação. Mas não é verdade que NTFS não fragmenta. Tanto não é verdade que existe um desfragmentador no Windows.

Veja também
Dicas para particionamento e organização do HD, parte 1
Dicas para particionamento e organização do HD, parte 2
Dicas para particionamento e organização do HD, parte 3

Em 05/10/2004 17:17 - Comentários (1)

MPROB (mais um post referenciando outro blog)

O pessoal da Microsoft às vezes exagera no uso de siglas:

Congrats to the XMLP WG for getting the MTOM and XOP specs to CR

Alguém pode me explicar o que o Don Box quis dizer com isso?? :-)

Em 06/10/2004 23:59 - Comentários (0)

Mais uma prova que computação não é uma ciência exata

A Microsoft, a um tempo atrás, implementou a política "Zero Defects" para desenvolvimento de software. Essa política determinava que, enquanto todos os bugs de um determinado software não estivessem corrigidos, nenhuma nova funcionalidade seria adicionada. Mas os softwares da Microsoft (assim como 100% dos softwares existentes) ainda tem bugs. O que acontece então? Leiam isso:

Larry's rules of software engineering, Part 3: "Zero Defects" doesn't result in zero defects

Em 08/10/2004 17:41 - Comentários (0)

Mais ferramentas

Achei sites muito bons com ferramentas:

John Lyon-Smith: Libs C++/Win32, libs .NET. Além disso, tem o "C# Script Executor 2.6.8", que permite usar C# como ferramenta de script.

SmidgeonSoft: Ferramentas a la Sysinternals. Debuggers, Disassemblers, e visualizadores de objetos do NT (threads, arquivos, etc). Merece uma visita.

Em 08/10/2004 18:17 - Comentários (0)

Conhecimento é poder

Eu já escrevi sobre como é extremamente necessário que você tenha informações para tomar decisões. Tenho pensado bastante em open source, e resolvi me informar melhor de uma vez por todas. Eu sou um usuário Linux razoável, tenho um SUSE instalado em casa e até consigo me virar. Bom, mas que graça tem usar um sistema operacional sem programá-lo?

Além do C/C++, que é lingua franca em todos os sistemas operacionais, tendo estudado algumas bibliotecas. Fiz alguns testes com Qt (o toolkit usado pelo KDE) e também com GTK (usado pelo Gnome). Tendo mais ao Qt, por ser nativamente C++ e por achar o KDE mais conciso do que o Gnome.

Além do C++, tenho feito testes com o Mono, a runtime .NET para Linux, do pessoal da Ximian/Novell. Apesar de toda a dificuldades com a implementação do Windows.Forms (eles também desistiram do Wine), o trabalho deles é espantoso. Nos poucos testes que eu fiz, o JIT do Mono me pareceu sensivelmente mais rápido do que o JIT do .NET Framework. Ainda vou fazer alguns testes com sistemas maiores, mas a primeira impressão é muito boa. Na empresa onde eu trabalho, estamos começando a projetar um sistema grande, com a camada de negócios separada da apresentação. Isso vai me dar vários componentes sem dependência de Windows.Forms para testar no Mono. E prometo que publicarei os testes aqui no site.

Falando em Mono, eu troquei uns e-mails com o Miguel de Icaza, o cara que projetou e coordenou a implementação do Mono. O mais legal é que, em um dos e-mails, ele me deu uma "bronca", dizendo que o fato de eu ter nascido no mundo Microsoft não é um motivo para eu me sentir tão perdido no UNIX ("não vou comprar seu argumento, simplesmente porque computadores são divertidos"). Além disso, ele me indicou um livro, o The UNIX Programming Environment, que eu comprei e estou lendo.

Esse livro foi escrito em 1984. Pode parecer desatualizado, não? É, mas não está. E lendo esse livro eu, além de aprender um pouco da filosofia do UNIX, tenho aprendido que as grandes novidades na área de programação são reinvenções de tudo que já existe desde de 1960.

O UNIX trabalha com o conceito de stream, usando e abusando da capacidade de redirecionar o stdout de um programa para o stdin de outro (em português: redireciona a saída de um programa para a entrada de outro). Esse conceito permite que vários programas trabalhem em conjunto, multiplicando o poder das ferramentas. Você pode até trocar aquele funcionário incompetente por um shell script :-).

Mas como um programa entende a saída de outro? Simples, usando o formato texto, com tudo separado com quebra de linha (\n). Assim todo mundo se entende. Isso te lembra alguma coisa? Não mesmo? Pense melhor...

XML. Padrão baseado em SGML (texto) para intercâmbio de informações. Assim todo mundo se entende. Mas, considerando os fatos acima, XML é uma evolução, e não uma revolução. Seria uma revolução se todos os programas, desde o início dos tempos, trabalhassem com formatos fechados e binários, como o Microsoft Word. Mas é evolução, já que isso já existe desde a década de 60.

Mais uma simples evolução: SOA, arquitetura orientada a serviços. RPC baseado em SOAP (XML) trafegando em HTTP. Sem binding em tempo de compilação = IDispatch. Sem guardar estado = programação procedural. Sim, isso mesmo, programação procedural. As funções são "abrigadas" em objetos, que simplesmente fazem o mesmo papel que um namespace C++. Isso vem desde o pooling de componentes do COM+, com objetos stateless (que não guardam estado). E aí, qual a revolução?

Conclusões que eu cheguei:

  • Evolução é uma palavra que só serve para o pessoal de marketing
  • Sim, o Linux é legal... :-)
  • Como ler um livro pode fazer a gente pensar em tantas coisas
  • Como é colossal a quantidade de informações que você pode absorver estudando sobre um assunto
  • Quando você estuda um assunto, você obtêm informações que podem te levar a conclusões sobre outros assuntos
  • Ainda bem que eu não virei advogado, como minha família queria...

Em 11/10/2004 18:07 - Comentários (1)

Saudações ao debutante

Alfred Gary Myers, o cara que mais entende de .NET que eu conheço, resolveu de uma vez por todas escrever um blog. Vão aqui meus votos para que ele realmente leve isso a sério e escreva freqüentemente.

Em 19/10/2004 01:03 - Comentários (2)

Projeto de lei 1947/2003 sobre a regulamentação da profissão de Analista de Sistemas: eu sou contra!

Existe um projeto de lei, proposto por um deputado do Rio de Janeiro (Eduardo Paes), que prevê a regulamentação da Profissão de Analista de Sistemas e a criação de um conselho, como a CREA, para os profissionais de informática.

Até aí, tudo muito bom. Até ler o conteúdo da lei:

Art.2º Poderão exercer a profissão de Analista de Sistema no País:

I - os possuidores de diploma de nível superior em Análise de Sistemas, Ciência da Computação ou Processamento de Dados, expedidos no Brasil por escolas oficiais ou reconhecidas pelo Governo Federal;
II - os diplomados por escolas estrangeiras reconhecidas pelas leis de seu País e que revalidarem seus diplomas de acordo com a legislação em vigor;
III - os que, na data de entrada em vigor desta lei, tenham exercido, comprovadamente durante o período de, no mínimo 5(cinco) anos, a função de Analista de Sistema e que requeiram o respectivo registro aos Conselhos Regionais de Informática.

E tem mais:

Art.31 Constituem infrações disciplinares, além de outras:
I - transgredir preceito de ética profissional;
II - exercer a profissão quando impedido de fazê-lo, ou facilitar, por qualquer meio, o seu exercício aos não inscritos ou impedidos;

Não esqueça que "impedido de fazê-lo" é alguém que não tenha diploma na área de informática. E passar um projeto para uma pessoa não formada será uma infração passível de punição.

O deputado Eduardo Paes é advogado, ex Secretário do Meio Ambiente do Rio, e assumidamente não entende de informática. Não estou simplesmente o desqualificando, mas acho que a visão dele da nossa área é equivocada, ele está tratando nossa área como se fosse Medicina e Engenharia. Inclusive, as pessoas que defendem a criação da lei insistem em fazer comparações com essas áreas, que como sabemos, tem um dinâmica completamente diferente da nossa. Eles chegam até a dizer que tirando os "sem diploma" do mercado o salário aumentará graças a lei da oferta e procura. Egoísmo pouco é bobagem mesmo...

Eu já gastei meu latim (sem sucesso) em uma lista de discussão sobre o assunto, tentando explicar que tirar as pessoas sem diploma do mercado não é o mais correto. Sabemos que existem MUITOS analistas e programadores sem diploma que são MUITO melhores do que os formados. Além disso, me parece um instrumento visivelmente criado por alguém que não tem conhecimento da área, e para profissionais limitados que querem manter seus empregos às custas de um diploma. Querem criar mais um indústria dos "assinadores de projeto", como existe em jornalismo e engenharia. Talvez a regulamentação seja boa, mas o texto dessa lei está completamente equivocado.

Criei uma nova lista de discussão para as pessoas que SÃO CONTRA essa lei. Caso alguém queira ajudar com argumentos, ou ajudar a pensar na forma mais adequada de manifestação para evitar que essa lei seja aprovada, sinta-se convidado.

Caso você não conheça direito a lei, dê uma lida nela e nos seus apensos. Caso você chegue a conclusão que também é contra, entre na lista. Caso ainda não tenha opinião formada ou deseje somente discutir o mérito da questão, use a lista da RioJUG que foi criada para isso.

Parece que o pessoal da Sociedade Brasileira de Computação também é contra a exigência de diploma na área. Fiquei muito feliz ao saber disso.

Para mais detalhes

Texto do projeto de lei
Página com todas as informações dos projetos. Leia os apensos e veja as justificativas, você dará boas risadas

Em 19/10/2004 17:16 - Comentários (23)

Wikipedia tem tudo, inclusive algoritmos

Quem leu o About do site deve saber o quanto eu gosto da Wikipedia. É a enciclopédia livre e aberta, com mais conteúdo do que as enciclopédias pagas. E como bastante gente que trabalha diretamente com programação escreve lá (até eu já escrevi algumas coisas...), existe bastante conteúdo sobre programação.

Mas um das coisas mais legais é que a Wikipedia tem muita informação sobre algoritmos, inclusive com implementações em diversas linguagens. Duvido que a Barsa que sua mãe comprou tenha a implementação de quicksort em Lisp. :-)

Além da definição de um algoritimo, temos a explicação e implementação de quick sort, busca binária, red black tree, entre outros. Fora os links para sites com mais detalhes e como animações Java para você entender melhor o algoritmo. Precisa de mais alguma coisa?

Em 19/10/2004 22:56 - Comentários (1)

Mostre-me teu Menu Iniciar e digo quem tu és

Em 20/10/2004 05:02 - Comentários (1)

Se o programador é bom, não importa a ferramenta

Uma das características de um bom programador é saber escolher a ferramenta certa para a tarefa certa. Apesar de eu ser um ferrenho programador Visual C++, quando eu preciso fazer programinhas de cadastros eu uso VB6 ou .NET.

Mesmo assim, existem pessoas que fazem coisas muito boas, mesmo que a ferramenta não seja a mais adequada. Eu lembro que a uns 8 anos atrás eu copiei da antiga revista Micro Sistemas (alguém lembra dessa?) um driver de mouse para QBASIC, usando DATA e PEEK/POKE. Eu eu comecei a fazer um Adventure, com interface gráfica e tudo, em QBASIC. Cheguei até a fazer uma lib para interface tipo Windows, como botão 3D e um sistema tosco de caixas e coordenadas. Bons tempos aquele, quando eu nem sabia que programação era uma profissão, pra mim era tudo diversão.

O fato é: a grande maioria dos softwares são feitos em C/C++. Apesar de serem linguagens altamente técnicas e muito complexas para iniciantes, a flexibilidade faz com que elas sejam as preferidas. Já falei sobre isso em um post anterior. Mas existem pessoas que que usam linguagens menos comuns - ou até mesmo não adequadas - para contruir programas. Alguns exemplos:

BitTorrent: Software para downloads distribuídos, uma das grandes invenções em P2P. Um conceito maravilhoso, implementado em Python e com a GUI feita em wxWindows, usando um bind para Python.

Azureus: Client para BitTorrent, feito em Java. Não que Java não seja uma ferramenta adequada para isso, mas os programas GUI feitos em Java, na sua grande maioria, são muito feios. Esse programa (junto com o Eclipse e as IDEs da Borland) é muito bonito e funciona muito bem. É meu client BitTorrent preferido.

DHTML Lemmings: O CAMPEÃO!!! Link enviado pelo meu amigo Fábio. Um clone do jogo Lemmings implementado em DHTML/CSS/JavaScript. Joguei só as primeiras fases, mas o jogo é perfeito. Eu custei a acreditar que ele foi feito somente em JavaScript.

Em 20/10/2004 22:24 - Comentários (0)

Testando ASP.NET, rodando em Linux e Mono

O MonoForge, uma iniciativa do provedor italiano Impulso, está aceitando candidatos para um período de beta test para hospedar aplicativos feito em ASP.NET. A grande novidade é que os servidores usarão Linux e Mono (a runtime .NET para Linux/Unix/MacOS da Ximian/Novell), ao invés servidores com Windows.

Serão 10MBs de espaço, e 1 banco mySQL. Sendo assim, você não pode simplesmente copiar seu aplicativo ASP.NET feito para SQL Server e vê-lo rodando, a não ser que você tenha usado alguma camada para abstração de banco de dados. Mas como as classes de acesso a banco de dados suportam as interfaces IDbXXX, você pode dar sorte, e um find/replace para trocar as declarações pode resolver o seu problema - se você não usou nenhum recurso esotérico do SQL Server, é claro.

É uma ótima oportunidade para testar o Mono sem usar um servidor próprio.

Em 25/10/2004 18:04 - Comentários (0)

Um dia minha filha terá orgulho disso

Minha pequena Heloisa nasceu faz um pouco mais de uma semana. Além da minha felicidade de pai de primeira viagem, isso também gerou uma história interessante para contar. Afinal, nada da vida de um escovador de bits pode passar em branco... :-)

Quem conhece um pouco de gravidez, sabe que horas antes (pode ser até 48 horas) do parto, a mulher começa a sentir as contrações e o corpo começa a se preparar para o trabalho de parto. As mães que fazem pré natal são orientadas pelos médicos e enfermeiras a anotar a duração e intervalo das contrações, para saber como anda a evolução do trabalho e ajudar a saber a hora certa de ir para um Hospital (ou para uma Casa de Parto, como foi o meu caso).

Enquanto eu estava no trabalho, minha esposa ligou dizendo que já estava sentindo algumas contrações leves. E também me disse que estava controlando isso, anotando os horário no papel. Poxa, no papel?? Nem uma planilhinha Excel?? Em que mundo vivemos?? :-)

Quando eu cheguei em casa, ouvi o que eu queria: "Ficar olhando no relógio e anotando essas contrações já está enchendo o saco...". Foi aí que eu perguntei se um programinha no iPaq ajudava. Disse que seria só clicar quando começasse e quando terminasse a contração, que ele faria o resto. Então, ela disse sim. :-)

Abri feliz meu Visual Studio, iniciei um projeto para PocketPC em C#. Meia hora, um DataGrid, um DataSet e um Button depois, eu tinha uma versão alfa, que testamos nas próximas contrações. Corrigi alguns bugs e fui colocando mais recursos a medida que ela ia pedindo. Mais uma hora programando e acompanhando as contrações eu tinha um programa para controlar contrações pronto.

        

A parte que está no DataGrid é simples. Os tempos que estão embaixo são as médias. O programa salva o DataSet em um arquivo "\contra.xml", e pergunta no início se você quer carregá-lo para continuar contando as contrações.

Você pode baixar os fontes do programa aqui. Caso queria somente o programa para usar, você pode baixar o programa, instalar o .NET Compact Framework no seu PocketPC e depois copiar o programa pra ele (não precisa instalar). E como ele é feito em .NET, esse programa também roda em qualquer PC que tenha o .NET Framework instalado (fica feio, mas roda).

Acho desnecessário, mas lá vai:
Esse programa é livre e pode ser usado por qualquer um, sem restrições. Sendo assim, NÃO DAREI NENHUM TIPO DE SUPORTE. Também não me responsabilizo caso alguma conta esteja errada, não se esqueça que eu programei isso enquanto minha esposa estava tendo contrações... Também não me responsabilizo se alguém parir em meia hora ou se a mulher passar o tempo todo jogando paciência no iPaq e não controlar as contrações. E claro, ignorarei qualquer pedido para que o programa seja portado para VB.NET :-)

Em 31/10/2004 23:40 - Comentários (12)

WinDbg novo!

O WinDbg 6.4.4.4 beta já está disponível para download. Eu já baixei o meu :-)

Em 01/11/2004 06:15 - Comentários (0)

Novo servidor

Depois de ficar realmente decepcionado com a instabilidade do meu antigo servidor, resolvi fazer uma troca. Espero que esse novo hosting faça jus aos 97% de aprovação e satisfação que eles conseguiram no Find My Hosting. A aprovação do meu antigo era de 80%, talvez isso diga alguma coisa...

Em 02/11/2004 17:28 - Comentários (5)

Programando drivers com bom humor

Toda equipe sempre tem um programador que acaba colocando uma gracinha ou outra em um programa. Eu faço questão de não deixar as equipes que eu participo sem esse tipo de pessoa. Além de ter o costume de colocar uns comentários não muito convencionais no código, eu às vezes aproveito para colocar um ovinho de páscoa nos programas. O que seria da nossa vida sofrida de programador se não tivessemos bom humor?

Olhem só um erro que foi retornado pelo BUILD (programa que gerencia a compilação de drivers) do DDK quando eu, por engano, coloquei um arquivo .h na lista de SOURCES:

BUILD: Adding /Y to COPYCMD so xcopy ops won't hang.
BUILD: Object root set to: ==> objchk_w2K_x86
BUILD: Compile and Link for i386
BUILD: Loading c:\WINDDK\3790\build.dat...
BUILD: Computing Include file dependencies:
BUILD: Examining d:\data\sp\source2\drivers\spfwu directory for files to compile.
    d:\data\sp\source2\drivers\spfwu 
BUILD: d:\data\sp\source2\drivers\spfwu: Interesting sources extension: callbacks.h

Eu também acho que .h é uma extensão bem interessante... :-)

Em 03/11/2004 01:47 - Comentários (3)

Programadores sepultam programas mortos

Terra Informática: Programadores sepultam programas mortos

Slashdot: Programmers Hold Funerals for Old Code

Eu tenho tantos programas para chorar... Os meus pelo menos eu mantenho em estado de hibernação no meu HD, não tenho coragem de matar. Afinal, algum dia eu ainda posso querer modificar o programa em Clipper que eu fiz quando tinha 16 anos, vai saber...

Editado em 05/11/2004: Um link do Slashdot é muuuuuuuuuito melhor do que um link do Terra Informática

Em 04/11/2004 19:18 - Comentários (0)

Atendendo a pedidos: Controle de Contrações para PC, baixável e feito em WTL

Atendendo aos pedidos do meu amigo Wanderley e da minha querida doula Ana Cris, resolvi fazer uma versão do meu Programa Para Controle de Contrações Para Parturientes e Similares que roda em PC e feito em WTL. O layout do programa não é lá essas coisas, mas eu tentei manter o design e o funcionamento parecidos com a versão para PocketPC. E apesar do pedido da Ana Cris para que o programa fosse "instalável", eu resolvi teimar e fazer um que não precisa instalar, é só baixar e rodar. :-)

Chega de ladainha, eu só quero fazer o download...

O programa foi desenvolvido usando o Visual C++ 7.1 e o WTL 7.5. Como eu não usei nada esotérico, acredito que ele deve ser compilável usando versões mais antigas do WTL. Mesmo assim, não sei se isso vai compilar no Visual C++ 6.0.

Como em Win32 não existe DataGrid, eu usei um ListView, que serve muito bem para esse propósito. Ao invés de usar um DataSet para gravar os dados, eu usei um std::list e populei o ListView "na mão".

Foi difícil resistir a tentação de usar o ATL::CAtlList no lugar do std::list, mas eu estou querendo me livrar do vício do ATL e ser um pouquinho mais multiplataforma. Acho que eu nunca escrevi sobre isso ainda, mas eu me recuso a usar a lib do C. Eu gosto mesmo é de CreateFile, ATL::CAtlFile, CreateFileMapping, CoCreateInstance e CoMarshalInterThreadInterfaceInStream :-)

Olhem os trechos principais do código:


typedef struct _CONTRA_INFO
{
   __time64_t StartTime;
   __time64_t EndTime;
} CONTRA_INFO;

typedef std::list CContraList;

...

void CMainDlg::ChangeButtonText()
{
   m_Button.SetWindowText(m_bContraInProgress ?
                          _T("Ufa, acabou...") : 
                          _T("Está doendo, socorro!!"));
}

void CMainDlg::UpdateList()
{
   CContraList::iterator CurrentIterator, PreviousIterator;
   int iItem;
   
   ATL::CTime CurrentTime, PreviousTime;
   ATL::CTimeSpan Duration, Interval;
 
   CurrentIterator = m_ContraList.end();
   CurrentIterator--;
   
   if(m_bContraInProgress)
   {
      //
      // contração acabou de iniciar. Calcular o intervalo entre 
      // a última e colocar o horário inicial no ListView
      //

      //
      // Colocar no ListView
      //
      CurrentTime = CurrentIterator->StartTime;
      iItem = m_lstContra.InsertItem(100000, CurrentTime.Format(_T("%x %X")));

      //
      // se for a primeira contração, não tem intervalo para calcular
      //
      if(CurrentIterator != m_ContraList.begin())
      {
         PreviousIterator = CurrentIterator;
         PreviousIterator--;
         

         ATLASSERT(PreviousIterator->EndTime != 0);
         Interval = CurrentIterator->StartTime - PreviousIterator->EndTime;

         m_TotalInterval += Interval.GetTimeSpan();

         m_lstContra.SetItemText(iItem,2,Interval.Format(_T("%M:%S")));
      }
   }
   else
   {
      //
      // a contração acabou. É só calcular a duração e colocar no último item
      //
      iItem = m_lstContra.GetItemCount() - 1;
      
      ATLASSERT(CurrentIterator->StartTime & CurrentIterator->EndTime);
      Duration = CurrentIterator->EndTime - CurrentIterator->StartTime;

      m_TotalDuration += Duration.GetTimeSpan();

      m_lstContra.SetItemText(iItem,1,Duration.Format(_T("%M:%S")));
   }
}

void CMainDlg::DoStatistics()
{
   ATL::CTimeSpan t;
   int iItemCount = m_ContraList.size();
   
   if(iItemCount < 2)
      return;

   t = m_TotalDuration / (iItemCount - (m_bContraInProgress ? 1 : 0));
   m_stcDuration.SetWindowText(t.Format(_T("%H:%M:%S")));


   //
   // como o primeiro item não tem intervalo, é -1
   //
   t = m_TotalInterval / (iItemCount - 1);
   m_stcInterval.SetWindowText(t.Format(_T("%H:%M:%S")));
}

LRESULT CMainDlg::OnContraClick(WORD,WORD,HWND,BOOL&)
{
   CONTRA_INFO ContraInfo;
   CContraList::iterator i;
   
   if(!m_bContraInProgress)
   {
      //
      // início da contração
      //
      ContraInfo.StartTime = _time64(NULL);
      ContraInfo.EndTime = 0;

      m_bContraInProgress = TRUE;
      m_ContraList.push_back(ContraInfo);
   }
   else
   {
      ATLASSERT(!m_ContraList.empty());
      i = m_ContraList.end();
      i--;
      
      i->EndTime = _time64(NULL);
      m_bContraInProgress = FALSE;
   }

   UpdateList();
   ChangeButtonText();
   DoStatistics();
         
   return TRUE;
}

Algumas coisas podem ser notadas nesse trecho de código:

  • Não acho que esse código seja muito mais complicado do que um código em .NET. Complicado e difícil de compreender é a lib do C, que tem mais de 3 décadas. ATL/WTL facilitam muito as coisas, e o código fica MUITO pequeno e MUITO rápido.
  • Eu não me preocupei com otimizações. O máximo que eu fiz é usar __time64_t ao invés de ATL::CTime na hora de gravar os horários, para reduzir a atividade desenfreada de copy constructors. Até porque, hoje em dia todo mundo usa .NET, que é muuuuuuuuito lento (não me venham com esse papo de "depende"). Então não me venham falar que meu código em C++ poderia ser mais rápido... :-)
  • Programar drivers faz bem! Quem já programou KernelMode vê a clara influência do estilo WinDDK no meu código. Uso de ASSERTS, código claro, comentários claros e destacados, variáveis com nomes claros. Olhe algum sample do DDK e veja o que é programar bonito!
  • Eu usei TCHAR (veja as macros _T()) para que eu pudesse compilar em UNICODE (Windows NT/2000/XP/2003) e em ANSI (Windows 9x/Me). No projeto existem 3 configurações: Debug, Release e Release ANSI. Eu costumo compilar somente UNICODE, mas resolvi de última hora ser solidário com os usuários de Win9x, afinal, eles já têm problemas suficientes.
  • Eu coloquei o ATL:: antes de CTime e CTimeSpan para deixar bem claro que o código não tem nada a ver com MFC.

Como o programa é feito em WTL (a melhor lib do planeta para desenvolver código Win32), nós temos um executável de 84kb sem dependência de nenhuma runtime ou DLL. É só baixar o executável e rodar.

Downloads
Executável UNICODE (Windows NT/2000/XP/2003/Longhorn)
Executável ANSI (Windows 9x/Me). Eu não tenho uma máquina Windows 9x para testar, me avisem caso não funcione.
Fontes do programa. Você vai precisar do WTL para compilar.

Em 07/11/2004 23:41 - Comentários (1)

Realizando o meu desejo: Todos os posts do weblog, em ordem cronológica

Eu leio diversos weblogs diariamente, e existe um recurso que eu sinto falta: poder ler todos os posts de um weblog em ordem cronológica. Em todos os blogs os posts são organizados no esquema de "os mais atuais primeiro", e quando alguém escreve uma série de posts relacionados, você é obrigado a ficar lendo de trás para frente, como fazemos com rastros de e-mail. Ou pior, você tem uma lista com os posts antigos e você tem que clicar um por um. Alguns weblogs tem a funcionalidade de calendário, para que você veja em uma tela só os posts de um determinado mês. Sinceramente, a não ser que seja um weblog daqueles sentimentais ("hoje acordei feliz, o dia está bonito e eu vesti aquela minha meia das Meninas Superpoderosas"), a data em que o post foi escrito pouco importa.

Como eu não posso modificar os weblogs dos outros, então eu mudo o meu. Se você quer ver tudo que eu escrevi até hoje, e em ordem cronológica, agora você pode. Um link para esse recurso também está disponível no arquivo do WebLog.

Em 08/11/2004 02:20 - Comentários (0)

Firefox 1.0!

Hoje, dia 09/11/2004 está oficialmente lançado o Mozilla Firefox 1.0

Eu uso o Firefox desde o 0.1 (quando ele ainda chamava Firebird), ele já era muito bom desde aquela época. E está ficando cada dia melhor.

Caso você ainda sofra com os demorados patches do Internet Explorer, sugiro que você mude para o Firefox. Caso você seja um power-user, mude para o Firefox e depois escolha as extensões (plugins) que mais te agradam. Eu uso Ad-Block (bloqueia banners), Tabbrowser Extensions (melhor controle das tabs), Sage (leitor RSS), e o All-in-one Gesture (comande seu browser somente movendo o mouse). Resumindo: caso você navegue na web, eu sugiro que você use o Firefox!


Get Firefox

Em 09/11/2004 16:13 - Comentários (0)

Novo artigo: Como ser um bom programador

Existem diversos fatores que levam alguém a ser um bom programador. Selecionei os mais importantes e escrevi um artigo, sobre como ser um bom programador. Coloquei nele também algumas dicas importantes sobre como procurar informações na Internet e em lista de discussão. Além de lembrar que essa estória de "não preciso de livros, tem tudo na Internet" é coisas de gente que não sabe o que fala. :-)

Em 16/11/2004 01:15 - Comentários (0)

Achei um blog sobre desenvolvimento de drivers!

Para os interessados em programação de drivers para WinNT, dêem uma olhada no blog de Steve Dispensa (MVP para Windows DDK), one ele escreve algumas dicas sobre desenvolvimento e teste de drivers.

Estou preparando um post sobre como iniciar o desenvolvimento de drivers e onde encontrar informações, aguardem!

Em 18/11/2004 16:13 - Comentários (0)

Como fazer drivers para Windows NT/2000/XP/2003, parte 1

A dúvida mais frequente que eu recebo pelo formulário de contato é: "como eu faço para desenvolver drivers para Windows?". Vou publicar algumas dicas aqui para quem está iniciando.

Pré requisitos

Primeiramente eu espero que você seja um bom programador, programar drivers não é fácil. :-)

Para fazer um driver, você precisa SABER programar drivers (programação kernel mode). Parece óbvio mas não é. Eu recebo todo mês pelo correio a newsletter da OSR, e a do mês passado era uma edição especial com o dobro do tamanho normal, falando sobre teste de drivers. E lá eu li algo muito interessante: em kernel mode, é impossível fazer programação cowboy. Programação cowboy é quando você não entende muito do assunto, pega um código exemplo daquilo que você quer e sai ajustando e remendando até que fique bom, e depois entrega para o cliente. Quando a coisa roda em kernel mode, isso nunca vai ficar bom, a não ser que você saiba mesmo o que está fazendo. Eu conheço uma empresa que está remendando um driver faz mais de dois anos, e até hoje não funciona direito.

Você precisa ser um bom programador C, ponto. Caso você ainda tenha dúvida sobre ponteiros ou estruturas de dados, programação de drivers é um péssimo lugar para treinar isso. O indicado é que você consiga fazer um programa Win32 com certa facilidade antes de se aventurar a programar drivers. Alguns conceitos de passagem de parâmetros IN e OUT usando estruturas e ponteiros void que viram ponteiro para estruturas mágicas também são usados em Win32. Ah, e se você não souber o que é uma lista ligada, veja antes na wikipedia, você vai usar isso muito.

(E antes que alguém pergunte, experiência com C#, VB.NET, VB6 ou Java não vai te ajudar em muita coisa além do treino em lógica de programação)

Onde encontrar informações

Agora que você já passou pelo blá-blá-blá, vamos ao que realmente interessa. Se você quer programar em kernel mode você terá que estudar bastante, e é melhor que comece o quanto antes.

Livros

Microsoft Windows Internals - Esse é o primeiro que você deve ler e reler. Não trata especificamente sobre programação kernel mode, mas descreve o funcionamento do kernel do Windows. E é ESSENCIAL que você saiba isso.

Programming the Windows Driver Model - Esse trata especificamente de programação kernel mode. Windows Driver Model (WDM) é um modelo de programação de drivers (mais voltado para drivers que controlam hardware). Caso seu driver siga as regras do WDM, seu driver pode ser compilado para Windows 2000 ou superior e Windows 98 e superior. Mesmo que seu driver não seja para um hardware (um firewall é um driver e não controla hardware), vale a pena ler esse livro, ele explica muito bem como funciona a sincronização e o I/O em kernel mode.

Existem mais livros, esses dois são os que eu tenho e uso para consulta. Procure na Amazon.

Internet

OSR Online - Empresa dos Estados Unidos que além de dar treinamentos sobre desenvolvimento kernel mode, disponibiliza bastante material sobre isso.

Windows Drivers Developer's Digest (W3D) - Artigos escritos por autoridades do assunto, como Walter Oney e Thomas Divine.

Listas de discussão - Nessa categoria temos as listas da OSR, o newsgroup da Microsoft sobre drivers e a lista da PCAUSA (específica para a parte de rede e afins).

Na continuação dessa série, falarei sobre o DDK e como colocar a mão na massa.

Em 21/11/2004 15:47 - Comentários (14)

Como contratar alguém para uma equipe de testes

Sem uma equipe de testes é MUITO DIFÍCIL entregar um software com qualidade, qualquer que seja a equipe de testes. A equipe pode ser formada por profissionais treinados e experientes em testes ou um estagiário mais esforçado. Apesar de não ser o ideal, o estagiário esforçado é muito melhor do que ninguém ligado ao desenvolvimento testando. Uma coisa muito importante: um estágiário de desenvolvimento serve, mas um estagiário ligado ao usuário não serve. O foco dele é outro e muitas vezes a equipe não tem o acceso necessário a ele. Todos os "testadores" devem ser ligados a equipe de desenvolvimento.

(Já trabalhei em uma empresa onde o responsável pelos testes era o Analista Funcional. Não considero isso uma equipe de testes, pois um Analista Funcional vai fazer os mesmos testes que o usuário vai fazer, testes de regras de negócios. Uma equipe de testes faz testes de stress, casos de usos e usos atípicos)

Eu já trabalhei com equipe de testes e sem equipe de teste. Sem é muito mais difícil, porque o programador tem que perder tempo com testes de validação, tentar deixar campos sem preencher, etc. Um bom estagiário faria isso sem problemas, e é muito melhor do que o programador perder tempo com isso. Isso otimiza o tempo de desenvolvimento e deixa o programador fazendo o que ele tem que fazer: programar.

Uma equipe de testes ajuda os programadores a escreverem um código de melhor qualidade. Se o programador sabe que a maioria dos erros serão pegos pela equipe de testes, ele terá mais atenção ao programar. Não existe nada mais desanimador do que um sistema de controle de bugs (sua empresa tem um, não tem?) com dezenas de bug com o seu nome estampado.

Caso sua empresa não tenha uma equipe de testes ou você esteja montando essa equipe, dê uma lida nesse artigo da Sticky Minds com dicas para contratar "testadores".

Em 26/11/2004 21:12 - Comentários (0)

Lendo a camiseta do ThinkGeek sem programar

O site do ThinkGeek está com uma promoção onde, gastando mais do que US$ 100,00 você ganha uma camiseta:

Essa camiseta tem o logo do SourceForge na frente e um número binário giganteco atrás. É CLARO que esse número quer dizer alguma coisa. Perdi 10 minutos hoje para encontrar o significado, me propondo a não usar uma só linha de código. Olhe o que eu fiz:

  1. Copiei o número binário do site;
  2. Criei um novo arquivo TXT no Visual Studio.NET, e colei o binário lá;
  3. Usando Regular Expressions, tirei as quebras de linha (troquei "n" por "");
  4. Usando Regular Expressions novamente, deixei que cada linha tivesse oito dígitos (troquei "{^........}" por "1n"). Pronto, agora temos números binários de 8 dígitos, que podem ser convertidos para char.


  5. Copiei o resultado e colei no Excel;
  6. Usei a função "BIN2DEC" do Excel para converter os binários em decimais. Caso o seu Excel diga que essa função não existe (colocando #NAME nas células), vá no menu Tools >> Addins e ative o "Analysis ToolPak";
  7. Depois, usei a função "CHAR" para achar os caracteres correspondentes aos números de acordo com a tabela ASCII;


  8. Copiei os caracteres para Visual Studio.NET e substitui "n" para "";
  9. I spent $100 at ThinkGeek and all I got was this lousy limited edition t-shirt.


Caso você tenha outra solução, sinta-se a vontade para mostrá-la nos comentários.

Em 02/12/2004 21:40 - Comentários (6)

Internet sem firewall é suicídio

Um texto da ArsTechinica conta sobre uma experiência feita com computadores conectados à Internet via banda larga, para descobrir quanto tempo eles levariam para ser atacados.

O resultado é inacreditável. O computador com Windows XP Service Pack 1 foi invadido 4 minutos após ter sido conectado. Na maioria dos casos, o computador invadido é transformado em um robô para envio de spam e vírus, e o usuário nem se dá conta disso.

Apesar de complicado para os usuários comuns, isso tem uma solução. Atualize seu Windows, e permita que o Service Pack 2 do Windows XP ative o firewall.

Em 03/12/2004 15:00 - Comentários (0)

Você acha que todos os autores de livros sabem o que falam?

Eu encontrei essa pérola no livro "Aprendendo ASP.NET com C#", de autoria de "Américo Damasceno Jr":

" No Windows temos duas áreas de memória que podemos colocar algo: o stack e o heap

O stack é uma área menos nobre, pois quando entram novos item nela, os mais antigos caem fora (se autodestroem, digamos assim). Coisas que não requerem muita permanência são jogados no stack. Um parâmetro que não é um value-type e seu valor são um caso típico, pois geralmente o processamento do bloco termina rapidamente."

  • Quer dizer que eu posso "colocar" algo em uma área de memória? O que seria esse algo?
  • "pois quando entram novos item nela, os mais antigos caem fora". Ouvi poucas coisas mais estúpidas em toda minha vida
  • "parâmetro que não é um value-type e seu valor são um caso típico". Essa parte eu não consegui entender.
  • Sabe o que é pior? Eu já vi gente no fórum da MSDN brasileira recomendando esse livro.

    Eu sei que mesmo os livros ruins têm mercado, principalmente entre os desenvolvedores iniciantes. Mesmo assim eu me reservo o direito de repugnar um livro escrito às pressas, com péssima qualidade e cheio erros.

    Em 13/12/2004 15:14 - Comentários (1)

    O bom filho à casa torna

    Chega de DataSets, chega de DataAdapters, chega de MessageBox.Show. A partir de segunda-feira (dia 20 de dezembro), voltarei a trabalhar com minha linguagem de programação preferida. Agora, ao invés de ".NET de dia e C++ de noite", eu voltarei ao esquema "C++ de dia e C++ de noite".

    Apesar da tristeza por deixar meus amigos de trabalho, minha alma de escovador de bits fala mais alto. Até onde eu sei terei bastante ATL, multithread e sockets pela frente. Ou seja, do jeito que eu gosto... :-)

    Em 15/12/2004 20:39 - Comentários (1)

    Usando o WinDbg para entender um software multithread

    É necessário que você conheça pelos menos as funções básicas do WinDbg para entender esse post. Escrevi dois artigos sobre WinDbg que podem ajudar (parte 1 e parte 2)

    Como já disse no post anterior, comecei o ano com o pé direito, voltando a trabalhar com Visual C++. E essa mudança de emprego e as festas de fim de ano são as minhas desculpas para o grande intervalo nos posts. E feliz 2005 para todos!

    Mas esse tempo não foi de todo perdido. Encontrei alguns problemas com o RSS do blog e resolvi. Agora o RSS está verificado e deve funcionar com qualquer NewsReader.

    Vamos ao que interessa. A empresa onde eu trabalho produz software de alto desempenho para o mercado financeiro. Os softwares se comunicam de diversas maneiras, sockets, File Mapping, MSMQ, etc. Muitas vezes existem comunicações entre as threads do mesmo processo, e essas comunicações são feitas de diversas maneiras, filas, mensagens inter thread, etc. Lendo o código é possível entender o que cada ThreadFunc faz, mas o mais fácil mesmo é colocar o software para rodar e analizar as comunicações entre as threads. Vamos ver como o WinDbg ajuda nisso.

    Com o executável já rodando e sob o controle do WinDbg, vamos dar um break e analizar como as coisas acontecem. Primeiro vamos listar as threads do processo usando o comando "~":

    0:027> ~
       0  Id: 890.bf8 Suspend: 1 Teb: 7ffde000 Unfrozen
       1  Id: 890.cd8 Suspend: 1 Teb: 7ffdd000 Unfrozen
       2  Id: 890.da0 Suspend: 1 Teb: 7ffdc000 Unfrozen
    ...
      41  Id: 890.95c Suspend: 1 Teb: 7ff91000 Unfrozen
      42  Id: 890.2a4 Suspend: 1 Teb: 7ff90000 Unfrozen
      43  Id: 890.de4 Suspend: 1 Teb: 7ff8d000 Unfrozen
    

    Bom, temos 44 threads. Um processo pode ter diversos thread pools, cada um para uma determinada tarefa. Podemos ter um thread pool para recepção de comunicação TCP/IP, outro pool para acesso ao banco de dados e outro ainda para acessar o MSMQ. Primeiro temos que saber o que cada thread está esperando.

    (Está se perguntando o que é um thread pool? É quando você colocar diversas threads para atender solicitações em paralelo. Cada vez que é recebida uma solicitação pelo processo, a thread que gerencia o pool sinaliza uma das threads para que ela trate essa solicitação. Isso faz com que uma solicitação não trave outra, já que elas estarão rodando em threads diferentes. Uma thread de um pool quase sempre fica esperando uma sinalização (evento, semáforo, etc) usando WaitForMultipleObjects)

    O comando "~", além de listar as threads do processo, também é usado como um modificador de comandos, fazendo com que o comando em uso seja direcionado para uma ou mais threads especificadas. Vamos testar isso com o comando "k". Esse comando faz um dump da pilha da thread atual. Vamos usá-lo:

    0:027> k
    ChildEBP RetAddr  
    02f3ffc8 77f77fe8 ntdll!DbgBreakPoint
    02f3fff4 00000000 ntdll!DbgUiRemoteBreakin+0x36
    

    Como nós forçamos um breakpoint com CTRL+BREAK, nós estamos na thread de debug. Primeiro vamos utilizar o comando "~" para ver algumas informações da thread, e depois veremos como o comando "~" pode modificar o comando "k":

    0:027> ~35
      35  Id: 890.84 Suspend: 1 Teb: 7ff98000 Unfrozen
          Start: kernel32!BaseThreadStartThunk (77e4a99b) 
          Priority: 0  Priority class: 32
    
    0:027> ~35 k
    ChildEBP RetAddr  
    0374fda8 77f4372d SharedUserData!SystemCallStub+0x4
    0374fdac 77e41bfa ntdll!NtWaitForMultipleObjects+0xc
    0374fe54 77e4b0e4 kernel32!WaitForMultipleObjectsEx+0x11a
    0374fe6c 0041d3bc kernel32!WaitForMultipleObjects+0x17
    0374fee8 004300eb XXX!CInterThreadMessageQueue::WaitForMessageEx+0x8c 
    0374ffb8 77e4a990 XXX!CResponseThread::ResponseThreadProc+0x9b
    0374ffec 00000000 kernel32!BaseThreadStart+0x34
    

    Agora temos um dump da pilha da thread 35. Mas como queremos estudar o que faz cada thread, com esse comando teríamos que ver thread por thread. Agora usaremos o suporte a curingas do comando "~".

    0:027> ~* k
    ...
       1  Id: 890.cd8 Suspend: 1 Teb: 7ffdd000 Unfrozen
    ChildEBP RetAddr  
    00bbfea0 77f4372d SharedUserData!SystemCallStub+0x4
    00bbfea4 77f6c86c ntdll!NtWaitForMultipleObjects+0xc
    00bbff48 77f6d7f5 ntdll!EtwpWaitForMultipleObjectsEx+0xf7
    00bbffb8 77e4a990 ntdll!EtwpEventPump+0x27d
    00bbffec 00000000 kernel32!BaseThreadStart+0x34
    
       2  Id: 890.da0 Suspend: 1 Teb: 7ffdc000 Unfrozen
    ChildEBP RetAddr  
    014dfe20 77f4313f SharedUserData!SystemCallStub+0x4
    014dfe24 77c57b85 ntdll!NtReplyWaitReceivePortEx+0xc
    014dff8c 77c60829 RPCRT4!LRPC_ADDRESS::ReceiveLotsaCalls+0x193
    014dff90 77c60771 RPCRT4!RecvLotsaCallsWrapper+0x9
    014dffb0 77c60857 RPCRT4!BaseCachedThreadRoutine+0x9c
    014dffb8 77e4a990 RPCRT4!ThreadStartRoutine+0x17
    014dffec 00000000 kernel32!BaseThreadStart+0x34
    
       3  Id: 890.c88 Suspend: 1 Teb: 7ffdb000 Unfrozen
    ChildEBP RetAddr  
    015dff10 77f4262b SharedUserData!SystemCallStub+0x4
    015dff14 77e418ea ntdll!NtDelayExecution+0xc
    015dff7c 77e416ee kernel32!SleepEx+0x68
    015dff88 77162501 kernel32!Sleep+0xb
    015dff94 771625ea ole32!CROIDTable::WorkerThreadLoop+0x12
    015dff9c 77160000 ole32!CRpcThread::WorkerLoop+0x1e
    015dffac 77162653 ole32!_imp__InstallApplication  (ole32+0x0)
    015dffb8 77e4a990 ole32!CRpcThreadCache::RpcWorkerThreadEntry+0x1f
    015dffec 00000000 kernel32!BaseThreadStart+0x34
    
    ...
    
       5  Id: 890.f38 Suspend: 1 Teb: 7ffd9000 Unfrozen
    ChildEBP RetAddr  
    01a2fc38 77f4372d SharedUserData!SystemCallStub+0x4
    01a2fc3c 77e41bfa ntdll!NtWaitForMultipleObjects+0xc
    01a2fce4 77e4b0e4 kernel32!WaitForMultipleObjectsEx+0x11a
    01a2fcfc 0041d3bc kernel32!WaitForMultipleObjects+0x17
    01a2fd78 004171bd XXX!CInterThreadMessageQueue<>::WaitForMessageEx+0x8c 
    01a2ffb8 77e4a990 XXX!CExecutionThread::ExecutionThreadProc+0x1bd 
    01a2ffec 00000000 kernel32!BaseThreadStart+0x34
    
       6  Id: 890.f08 Suspend: 1 Teb: 7ffd8000 Unfrozen
    ChildEBP RetAddr  
    01b2fc38 77f4372d SharedUserData!SystemCallStub+0x4
    01b2fc3c 77e41bfa ntdll!NtWaitForMultipleObjects+0xc
    01b2fce4 77e4b0e4 kernel32!WaitForMultipleObjectsEx+0x11a
    01b2fcfc 0041d3bc kernel32!WaitForMultipleObjects+0x17
    01b2fd78 004171bd XXX!CInterThreadMessageQueue<>::WaitForMessageEx+0x8c 
    01b2ffb8 77e4a990 XXX!CExecutionThread::ExecutionThreadProc+0x1bd 
    01b2ffec 00000000 kernel32!BaseThreadStart+0x34
    
       7  Id: 890.71c Suspend: 1 Teb: 7ffd7000 Unfrozen
    ChildEBP RetAddr  
    01c2fc38 77f4372d SharedUserData!SystemCallStub+0x4
    01c2fc3c 77e41bfa ntdll!NtWaitForMultipleObjects+0xc
    01c2fce4 77e4b0e4 kernel32!WaitForMultipleObjectsEx+0x11a
    01c2fcfc 0041d3bc kernel32!WaitForMultipleObjects+0x17
    01c2fd78 004171bd XXX!CInterThreadMessageQueue<>::WaitForMessageEx+0x8c 
    01c2ffb8 77e4a990 XXX!CExecutionThread::ExecutionThreadProc+0x1bd 
    01c2ffec 00000000 kernel32!BaseThreadStart+0x34
    
       8  Id: 890.38c Suspend: 1 Teb: 7ffd6000 Unfrozen
    ChildEBP RetAddr  
    01d2fc38 77f4372d SharedUserData!SystemCallStub+0x4
    01d2fc3c 77e41bfa ntdll!NtWaitForMultipleObjects+0xc
    01d2fce4 77e4b0e4 kernel32!WaitForMultipleObjectsEx+0x11a
    01d2fcfc 0041d3bc kernel32!WaitForMultipleObjects+0x17
    01d2fd78 004171bd XXX!CInterThreadMessageQueue<>::WaitForMessageEx+0x8c 
    01d2ffb8 77e4a990 XXX!CExecutionThread::ExecutionThreadProc+0x1bd 
    01d2ffec 00000000 kernel32!BaseThreadStart+0x34
    
    
    ...
    
      29  Id: 890.c4c Suspend: 1 Teb: 7ff9d000 Unfrozen
    ChildEBP RetAddr  
    0324fe64 77f4372d SharedUserData!SystemCallStub+0x4
    0324fe68 77e41bfa ntdll!NtWaitForMultipleObjects+0xc
    0324ff10 77e4b0e4 kernel32!WaitForMultipleObjectsEx+0x11a
    0324ff28 00455d59 kernel32!WaitForMultipleObjects+0x17
    0324ffb8 77e4a990 XXX!ReceiverThreadProc+0x49 
    0324ffec 00000000 kernel32!BaseThreadStart+0x34
    
      30  Id: 890.8ac Suspend: 1 Teb: 7ff9c000 Unfrozen
    ChildEBP RetAddr  
    0334fe80 77f4372d SharedUserData!SystemCallStub+0x4
    0334fe84 77e41bfa ntdll!NtWaitForMultipleObjects+0xc
    0334ff2c 77e4b0e4 kernel32!WaitForMultipleObjectsEx+0x11a
    0334ff44 00455b68 kernel32!WaitForMultipleObjects+0x17
    0334ffb8 77e4a990 XXX!ListenerThreadProc+0x48 
    0334ffec 00000000 kernel32!BaseThreadStart+0x34
    
      31  Id: 890.f44 Suspend: 1 Teb: 7ff9b000 Unfrozen
    ChildEBP RetAddr  
    0344fe78 77f4372d SharedUserData!SystemCallStub+0x4
    0344fe7c 77e41bfa ntdll!NtWaitForMultipleObjects+0xc
    0344ff24 77e4b0e4 kernel32!WaitForMultipleObjectsEx+0x11a
    0344ff3c 00455a2c kernel32!WaitForMultipleObjects+0x17
    0344ffb8 77e4a990 XXX!PublisherThreadProc+0x4c 
    0344ffec 00000000 kernel32!BaseThreadStart+0x34
    
    

    Podemos ver que as threads em vermelho são parte de um pool (note que a pilha das threads é a mesma, chamando as mesmas funções). Agora vamos fazer debug dessas threads do pool, colocando um breakpoint bem no retorno do WaitForXXX. Repare que quando fazemos o dump da pilha, temos o endereço de retorno da função:

       8  Id: 890.38c Suspend: 1 Teb: 7ffd6000 Unfrozen
    ChildEBP RetAddr  
    01d2fc38 77f4372d SharedUserData!SystemCallStub+0x4
    01d2fc3c 77e41bfa ntdll!NtWaitForMultipleObjects+0xc
    01d2fce4 77e4b0e4 kernel32!WaitForMultipleObjectsEx+0x11a
    01d2fcfc 0041d3bc kernel32!WaitForMultipleObjects+0x17
    01d2fd78 004171bd XXX!CInterThreadMessageQueue<>::WaitForMessageEx+0x8c 
    01d2ffb8 77e4a990 XXX!CExecutionThread::ExecutionThreadProc+0x1bd 
    01d2ffec 00000000 kernel32!BaseThreadStart+0x34
    

    Esse é o endereço que será executado logo após o retorno da função. Então, se colocarmos um breakpoint nesse endereço, o programa parará logo que a thread retornar do Wait. Vamos colocar o breakpoint e usar o comando "ln" (que mostra o symbol mais próximo de uma posição de memória) para ver como o RetAddr está realmente na função que queremos:

    0:027> ln 0041d3bc
    (0041d330)   XXX!CInterThreadMessageQueue::WaitForMessageEx+0x8c
    
    0:027> bp 0041d3bc
    
    

    Pronto. Agora quando chegar uma solicitação para uma das threads, o programa parará no breakpoint e poderemos ver o que a thread faz.

    Uma última dica: você pode também colocar o breakpoint somente em uma thread, caso não tenha interesse em fazer debug de todas as threads do pool (até porque se o programa estiver recebendo diversas solicitações em várias threads vai ficar difícil fazer o debug). Para isso use o modificador "~" e especifique o ID da thread antes de colocar o breakpoint:

    0:027> ~8 bp 0041d3bc
    0:027> bl
    ...  0:~008 XXX!CInterThreadMessageQueue<>::WaitForMessageEx+0x8c 
    

    Em 03/01/2005 18:36 - Comentários (1)

    Meu Windows Internals já está em casa!

    Fiz um ótimo negócio em encomendar o Windows Internals na Amazon antes do lançamento. A data prevista para o lançamento era de 5 de janeiro nos USA, o livro chegou na minha casa dia 4. A previsão de entrega era 2 de fevereiro... :-)

    Ainda não li o livro inteiro (...), mas tudo me parece ótimo! O livro já começa com a introdução escrita por David Cutler, o arquiteto do kernel do Windows NT. Logo depois, no capítulo 1, temos uma perspectiva histórica dos componentes do Windows (subsistemas, Win32 API, meia página sobre WinFX e .NET, etc) e a apresentação ao leitor das ferramentas necessárias para escarafunchar o sistema: Debugging Tools for Windows (WinDbg e outros), ferramentas Sysinternals e Windows 2003 Resource Kit.

    Certos programadores da equipe do Windows não só escreveram alguns capítulos, como revisaram todo o livro. Além disso, um dos autores (não lembro se o Mark Russinovich ou o David Solomon) teve acesso aos fontes do kernel do Windows, para ajudar a garantir a consistência e a veracidade das informações. Isso só ajuda a garantir que esse livro é definitivamente a bíblia sagrada da arquitetura NT!

    Só um aviso: esse livro não é especificamente sobre programação. Apesar de experiência com programação ser bem vinda durante a leitura, o livro descreve a arquitetura do sistema, não ensina a programá-lo. Assim, caso você seja um administrador de sistemas NT/2000/XP/2003, esse livro lhe será muito útil. Conhecendo a estrutura do sistema, você terá muito mais informações para lidar com problemas, principalmente telas azuis e conflitos diversos entre softwares (já que você saberá como eles interagem entre si).

    Em 06/01/2005 13:57 - Comentários (2)

    Mais uma falha séria no Internet Explorer, com demonstração

    Eu não uso antivírus (eu sei como o scan em tempo real é (mal) feito e sei o quanto acaba com a performance do computador), não uso Adware (que de tão bom diz que um Windows recém instalado tem spyware) nem qualquer tipo de software de segurança. Quando um computador é contaminado com algum tipo de vírus ou malware, eu removo na unha mesmo. Nada que uma busca no Google e Filemon/Regmon/ProcessExplorer/TcpView não resolvam. Apesar de não ser o mais prudente, minha experiência em arquitetura Windows (estou lendo o "Windows Internals", lembra?), um firewall bem configurado e um Windows atualizado têm me mantido longe das pragas virtuais. Eu uso Windows XP Professional (SP2 + últimas correções).

    Lendo o Slashdot hoje, encontrei um link para uma página da Secunia que demostra uma nova falha encontrada no Internet Explorer. Abri meu Internet Explorer (eu uso o Firefox, é claro) e o apontei para página com o teste. Muito me impressionou quando, clicando no link para testar se o micro era vulnerável, um cmd.exe foi aberto.

    Mas dessa vez a Microsoft foi bem rápida. A correção já está disponível no Windows Update. Apesar de meu Windows estar configurado para fazer atualizações automáticas, quando fiz esse teste eu tinha acabado de ligar o computador, e as atualizações ainda não haviam sido baixadas. Dez minutos depois, o Windows me avisou que haviam novas atualizações. Se eu usasse o Internet Explorer e entrasse em uma página que explorasse essa falha, seria tarde demais....

    Em 12/01/2005 01:43 - Comentários (0)

    Procure no Google, versão 0.1 alpha

    Todo mundo tem pelo menos um amigo que faz perguntas estúpidas antes e procura no Google depois. Muitas perguntas que me fazem por e-mail ou MSN seriam respondidas pelo primeiro link encontrado pelo Google. Você também passa por esse tipo de situação? Cansado de perder a concentração quando você está programando? Seus problemas acabaram!

    Além da "manezisse" de perguntar para os outros antes de procurar no Google, os manés ficam tremendamente ofendidos quando você responde com um "procure no Google". Além de se sentir ofendido, o mané age como se isso fosse óbvio (apesar disso, ele não fez o óbvio, se tivesse feito não faria a pergunta). Talvez o mané não sabia interpretar os resultados do Google...

    Foi para resolver esse problema que nós (eu) do 1bit.com.br criamos o "Procure no Google 0.1 alpha". Para não responder com um "Procure no Google", ou mesmo com um link para o Google (o mané pode se sentir ofendido da mesma forma), foi criado um sistema altamente complexo de redirecionamento, que envia o mané para o Google, mas sem mandar um link do Google para ele (já que isso poderia ofender o mané).

    O funcionamento é simples. Aponte seu navegador para www.1bit.com.br/png/png.php, preencha o formulário com a pergunta do mané e clique em "Gerar link". Depois é só repassar o link gerado para o mané. Quando ele clicar no link que você passou, ele será automaticamente redirecionado por nossos servidores para uma página do Google com as respostas que ele precisa.

    Não é só isso! O nosso super "Procure no Google 0.1 alpha" ainda possibilita que você envie seu amigo (que além de mané não sabe inglês) para uma busca do Google somente com páginas em português!

    Vamos à um exemplo de utilização do nosso fantástico "Procure no Google 0.1 alpha". Digamos que seu amigo lhe envie a mensagem "Onde posso encontrar tutoriais de ASP?". Ao invés de ser indelicado e mandá-lo procurar no Google, você acessa o nosso espetacular "Procure no Google 0.1 alpha" e gera um link para a consulta "Tutorial ASP". Depois é só repassar o link gerado para seu amigo:

    http://www.1bit.com.br/png/png.php?solution=7475746f7269616c20415350266c723d6c616e675f707426686c3d70742d4252

    Pronto! Agora seu amigo mané está feliz, e você pode voltar a programar em paz!

    Em 12/01/2005 20:18 - Comentários (2)

    Forçando a especialização de templates em C++

    Estou refazendo o marshaller de um projeto meu, para que ele use menos macros e fique mais orientado a objetos. Para cada tipo de dado suportado pelo marshaler, deve haver uma classe responsável. E todas as classes devem ser derivadas de um template específico.

    (Para quem não sabe o que é um marshaler, vamos lá: o marshaler é o responsável por transportar os parâmetros de uma chamada remota, de um contexto para outro. Quando você usa DCOM e chama um método que está fora do seu processo (rodando em outro EXE ou até em outro computador da rede), o marshaler do DCOM pega os parâmetros da função que você chamou, coloca isso num buffer e manda para o outro contexto. Lá ele lê esse buffer e chama a função do objeto remoto. Para o programador tudo é transparente, já que o DCOM (com uma ajuda das classes de proxy/stub) faz tudo sozinho).

    No início, o código ficou assim:

    template <typename T>
    class CSpTypeMarshaler
    {
    ...
    };
    
    template <>
    class CSpTypeMarshaler<DWORD>
    {
    public:
       CSpTypeMarshaler()
       {
          ATLTRACE("especialização para DWORD");
       }
    };
    
    
    template <>
    class CSpTypeMarshaler<GUID>
    {
    public:
       CSpTypeMarshaler()
       {
          ATLTRACE("especialização para GUID");
       }
    };
    
    //
    // aqui vou declarar os templates
    //
    CSpTypeMarshaler<DWORD> DwordMarshaler;
    CSpTypeMarshaler<GUID> GuidMarshaler;
    CSpTypeMarshaler<char> CharMarshaler;
    

    Até aqui tudo bem, nada que a especialização de templates não faça. Mas eu tenho um problema: é possível instanciar o template com qualquer tipo, MESMO QUE NÃO EXISTA ESPECIALIZAÇÃO PARA ELE. Como cada tipo deve ser tratado de uma forma especial, deve existir uma especialização para cada tipo.

    Uma possível solução é criar um construtor para o template não especializado. Esse construtor deve gerar um erro de compilação, para que não seja possível instanciar sem especialização. Olhe como ficou agora:

    template <typename T>
    class CSpTypeMarshaler
    {
    public:
       CSpTypeMarshaler()
       {
          //
          // isso NÃO FUNCIONA, gera erro sempre
          //
          // #error "Você precisa especializar o template"
    
          //
          // vamos declarar uma array com limite negativo. 
          // Isso gerará um erro de compilação quando o template
          // for instanciado sem especialização
          //
          char por_favor_especialize_esse_template[-1];
    
       }
    ...
    };
    
    template <>
    class CSpTypeMarshaler<DWORD>
    {
    public:
       CSpTypeMarshaler()
       {
          ATLTRACE("especialização para DWORD");
       }
    };
    
    template <>
    class CSpTypeMarshaler<GUID>
    {
    public:
       CSpTypeMarshaler()
       {
          ATLTRACE("especialização para GUID");
       }
    };
    
    //
    // aqui vou declarar os templates
    //
    CSpTypeMarshaler<DWORD> DwordMarshaler;
    CSpTypeMarshaler<GUID> GuidMarshaler;
    CSpTypeMarshaler<char> CharMarshaler;
    

    Agora, quando eu instanciar o template com o tipo char (que não tem especialização disponível), o compilador acusará o erro:

    ConfigManager_ProxyStub.h(48) : error C2118: negative subscript
    ConfigManager_ProxyStub.h(46) : 
    while compiling class-template member function 
    'CSpTypeMarshaler<T>::CSpTypeMarshaler(void)'
        with
        [
            T=char
        ]
    

    Quando o programador desavisado for verificar o motivo do erro, encontrará uma variável por_favor_especialize_esse_template.Quem usa a biblioteca Boost pode usar o BOOST_STATIC_ASSERT ao invés de usar esse método da array com tamanho negativo.

    Em 18/01/2005 04:23 - Comentários (7)

    Com a nota 9.6, agora sou certificado em C# também

    Fui lá eu, sem pretensão alguma, fazer a prova de certificação em C# (70-316). Dei só uma olhada no livro, e fui contando com a experiência e o curso intensivo de .NET que foi trabalhar com o Alfred. Como eu já não estou mais trabalhando com .NET faz 1 mês, achei prudente fazer a prova antes de esquecer o que é um DataSet (eu juro que estou tentando...) :-)

    Tive uma grande surpresa ao final da prova: além de passar na prova, minha nota foi 960 (1000 é o máximo), 9.6 numa escala de 1 a 10. Confesso que fiquei feliz e impressionado. Mas agora é que vem a melhor parte: Ninguém pode falar que eu só "aponto os defeitos" do .NET porque sou um programador C++ que não consegue enxergar além dessas APIs malucas. Com essa nota, eu posso falar mal (leia-se "apontar os defeitos") do .NET com autoridade. :-)

    Achei a dificuldade da prova mediana. É preciso ter bons conhecimentos em ADO.NET, saber usar de forma correta o DataReader, saber exatamente como funciona o DataAdapter (e qual sua interação com o banco de dados e com o DataSet), saber usar eventos como OnRowUpdated, além de RowErrors e coisas assim. Se você usa ADO.NET como usava o ADO, você não passa na prova. Cai também gerenciamento de Assemblies (GAC, COM Interop, etc), deploy (usando XCOPY ou MSI), WinForms (cai coisas de help e assessibilidade, que a maioria das pessoas não usa), e Debug. No meu caso, a experiência com Visual C++ e com a plataforma Windows em geral ajudou bastante, principalmente em debug e deploy.

    PS: Eu não odeio .NET. Só prefiro muito mais trabalhar com C++. Eu já escrevi um artigo sobre minhas opiniões quanto ao .NET.

    Em 18/01/2005 17:17 - Comentários (11)

    GMail e 7Zip: A evolução do software

    Realmente não há limites para o software. Enquanto os programadores e engenheiros de sistemas continuarem tentando criar algo novo, nós sempre iremos nos surpreender. O mais interessante é que a evolução do software só depende de nós, não depende diretamente dos engenheiros de hardware. Na época que o Doom foi lançado, ninguém acreditava que um computador da época conseguiria rodar um jogo com aquela qualidade. Mas John Carmack foi lá, fez um algoritmo de "3D pero no mucho", aproveitando o máximo da capacidade dos computadores. E o mundo dos jogos nunca mais foi o mesmo.

    Dois softwares me surpreenderam bastante nos últimos tempos: o GMail e o 7Zip. Um webmail e um software de compactação. Algo que existe desde que a web é web. Apesar disso, os dois software tem alguns diferenciais que os fazem se destacar no meio das dezenas de opções que temos.

    Vamos ao primeiro: o GMail tem um conceito simplesmente fantástico, com um interface gráfica simplesmente maravilhosa. Tão maravilhosa que desde que eu comecei a usar o GMail eu abandonei o Outlook, acho o GMail muito mais ágil. E apesar de fantástica, a proposta deles é simples: uma boa interface, bastante espaço, um sistema de busca eficiente. Eles não inventaram nada, somente agruparam boas idéias e bons recursos em um conjunto coeso. E esse conjunto coeso fez os usuários exigirem mais qualidade de todo o mercado (de webmail), e elevou o nível de todos os softwares. Todo mundo está vendo a correria do Yahoo e do Hotmail para aumentar o espaço de armazenamento (para míseros 250 MBs) e colocar mais recursos.

    O segundo: 7Zip. Um software de compactação que consegue compactar até 30% mais do que o WinRAR (que já é melhor do que o ZIP). Além do algoritmo melhor, o programador teve a ousadia (que eu não entendo porque não tiveram até hoje) de acreditar que o usuário não se importaria em esperar um pouco mais pela compactação para ter um resultado final bem melhor. Em um dos testes que eu fiz, compactei uma pasta com 42MBs de DLLs e PDBs. Nas suas respectivas configurações de compactação máxima, o WinRAR produziu um arquivo de 6.8 MB, enquanto o 7Zip produziu um com 4.8 MB. Além disso, o 7Zip é free e opensource, e o WinRAR abre os arquivos produzidos por ele (extensão 7zip).

    Essas boas surpresas só me ajudam a acreditar cada vez mais no ramo de desenvolvimento de software. Você não precisa ter uma idéia genial para criar um software. Se você agrupar boas idéias em um software de qualidade, você estará fadado ao sucesso (a não ser que você dê muito ouvidos ao pessoal de marketing antes de terminar o produto). Idéias maravilhosas e inovadoras são difícieis de aparecer. Mas um software de qualidade e que resolva o problemas das pessoas e das empresas é (só um pouco) mais fácil. É só acreditar e ir em frente. Se as empresas que produzem softwares de má qualidade e as consultorias desonestas conseguem ter sucesso, por que um software de qualidade não conseguiria?

    Em 29/01/2005 17:39 - Comentários (8)

    Ode ao C++

    Resolvi a uns dias atrás escrever um post sobre algo como "Por que C++?", e convidei meu amigo (e consultor particular em padrão C++) Wanderley Caloni Jr para ajudar. Não bastasse aceitar prontamente, ele também enviou-me um poético texto que rebatizei de "Ode ao C++":

    /**
    * @title Porque C++
    * @author Wanderley Caloni Jr
    * @date 31.01.2005
    */
    
    É natural que um programador tenha preferência por uma linguagem.
    Geralmente por motivos pessoais que se refletem nas características da
    linguagem. Eu, por exemplo, tenho vários motivos para amar essa
    linguagem:
    
    Linguagem C. Todas as vantagens da linguagem C estão embutidas em C++.
    E sem aquele papo erudito que deve-se programar em OO para ser C++.
    Por ser multiparadigma, a linguagem também suporta o melhor da
    programação procedural e estruturada.
    
    Popularidade. C++ é o que há. Linguagem unânime e reconhecida no mundo
    todo como de uso geral. Dificilmente você vai encontrar um algoritmo
    que não tenha representação em C++.
    
    Economia e Expressividade. Pode parecer bobagem, mas coisas como
    operador de incremento e valor em todas expressões permite que se faça
    muita coisa com poucas linhas. Isso a torna muito expressiva. Isso, em
    outras palavras, quer dizer que você pode juntar várias expressões
    numa só, e esse conjunto será também uma expressão.
    
    Liberdade. Em C++ você é o culpado de virtualmente qualquer coisa de
    bom e ruim que aconteça no seu programa, pois você tem que seguir
    poucas regras e tem que ser responsável no que faz. C++ não te ajuda a
    seguir um bom modelo de programação com restrições embutidas. Isso a
    torna difícil para iniciantes, mas conforme aumenta a experiência,
    maior o prazer em programar.
    
    Portabilidade. A possibilidade de compilar e rodar o seu código em
    vários ambientes - de compilação e execução - é uma característica
    útil e agradável. No meu caso é só agradável, pois dificilmente faço
    código portável, apesar das boas noções que tenho sobre o assunto. E
    são essas boas noções que me permitem afirmar que C++ suporta muito
    bem essa possibilidade.
    
    Rapidez. Pode não ser importante em muitos casos, mas já é do instinto
    do programador o desejo de eficiência no código. E nada como programar
    numa linguagem extremamente eficiente em tempo de execução para se
    sentir feliz de ver o código rodando.
    

    Após ler esse texto, eu irei - emocionado - me dedicar a escrever um texto com a minha versão e minhas opiniões. E claro, colocarei também aquela "água mercadológica no feijão", para ajudar a esclarecer que a pergunta correta não é "Por que C++?" e sim "Por que algo que não C++?"

    Em 03/02/2005 01:09 - Comentários (1)

    Um bom exemplo de comparação de performance entre linguagens/runtimes

    Vou reproduzir aqui um comentário que vi no Slashdot, em um post que falava de uma discussão sobre a segurança da JVM e do .NET, envolvendo o Don Box:

    You're quite right that Java's speed is excellent these days (for non-GUI code, at least). I've spent a lot of time recently working with a large system that was first implemented in Java (by highly skilled developers) and then ported to C++ (by greenhorns). The C++ port is only 50-100% faster, which isn't worth the price in developer time that's been wasted on memory leaks and other forms of memory corruption that were never a factor in Java. Besides that, supporting multiple platforms with the C++ version is the #definition of pain. However, the C++ version uses only about 1/4 or 1/5 as much memory as the Java version, and starts up far more quickly. If a *desktop* application needs to be deployed on older machines, or if the application is so memory-intensive it taxes the limits of today's server hardware, Java still falls flat.

    Grifei as partes que achei mais importantes. Ah, e antes que você vá procurar em um dicionário, eu já fiz - greenhorn quer dizer "iniciante".

    Vamos aos fatos:

    Uma aplicação feita por programadores experientes em Java, quando portada para C++ por programadores novatos e inexperientes ficou (só) 50-100% mais rápida. Ok, eu prometo que vou parar de ficar repetindo que o C++ é muito mais rápido que ambientes gerenciados. Mas eu juro que só faço isso porque existem pessoas (principalmente aqui no Brasil) que ainda teimam em dizer o contrário. Agora imagine uma aplicação portada por programadores experientes...

    Mesmo com o ganho de performance não valeu a pena, já que os memory leaks e erros de memória corrompida nunca aconteceriam em Java. Concordo plenamente. Os ambientes gerenciados foram criados para não deixar que esses erros aconteçam. Quando a performance não é o mais importante, faça em Java/.NET. Um programador inexperiente Java/.NET vai fazer um aplicativo lento que consome muita memória. Um programador inexperiente C/C++ vai fazer um aplicativo que dá GPF por qualquer coisa.

    A versão C++ consome de 1/4 a 1/5 da memória consumida pelo aplicativo Java e inicia muito mais rápido. Esse é um dos problemas dos ambiente gerenciados, o consumo de memória. Experimente usar a extensão SOS (Sons of Strike, para debug da CLR) do WinDbg, e faça um dump de todos os objetos no heap do Garbage Collector. Você vai entender... A grande vantagem é que, apesar do consumo médio de memória ser alto, ele tem um limite no decorrer da vida do aplicativo, já que o GC recolhe todos os objetos. Já em C/C++, se sua aplicação aloca bastante memória e não desaloca, uma hora ela esgota a memória da máquina. Como sempre, em C/C++, você tem que saber o que faz e fazer direito (mais do que em ambientes gerenciados).

    Esse é o tipo de comparação de performance que eu acho válida. Faça um aplicativo, de no mínimo de médio porte, e compare. Comparar performance de "Hello World"s e outras coisas simples não indicam muita coisa útil. Um exemplo: a alocação de memória em ambiente com GC é mais rápido do que em C/C++, já que envolve somente uma soma de ponteiro. A alocação em C/C++ envolve algoritmos de heap, mais complicados e demorados. E isso não faz os ambientes gerenciados mais rápidos.

    Resumindo: cada ferramenta tem seu propósito (eu já disse isso várias vezes, não é?). Se o aplicativo citado fosse feito por programadores C++ experientes, ele talvez seria 300% mais rápido e consumiria 1/10 de memória. Parece óbvio fazer em C++ não? Mas não é. Talvez a empresa gaste menos pagando programadores Java e comprando mais 3 servidores. Nem sempre performance e baixo consumo de memória é o foco. Se fosse, estaríamos usando DOS até hoje...

    Em 08/02/2005 01:19 - Comentários (5)

    Escovação de bits ao vivo e a cores

    Nada como terminar uma quarta feira com um bom chopp Brahma e discutindo tecnologias, metodologias e outras "ias" mais. Fazendo valer nossa última frase antes de irmos embora ("isso merece um post!"), deixo aqui registrado o encontro "escova bit" que participei hoje: Alfred Myers, Fabio Galuppo, e a "minha pessoa".

    Depois de discutirmos de C++ à PDCs, e de Datasets à gerenciamento de projetos, voltei para casa com a certeza de que encontros para discutir desenvolvimento de software - regados a um bom chopp - fazem muito bem para saúde e para o cérebro. Esses encontros além de divertidos são muito bons para trocar experiências. Pena que hoje em dia é difícil encontrar pessoas com nível técnico, bagagem e boa vontade suficiente para isso...

    Em 10/02/2005 03:48 - Comentários (1)

    Google: a Microsoft da web

    A grande novidade no mundo do software é o Google. Pode não parecer tão óbvio para as pessoas, já que o software deles não roda diretamente nos computadores dos usuários. Quando você faz uma consulta no Google, um cluster de aproximadamente 100.000 PCs (isso mesmo, cem mil) é utilizado para procurar em bilhões de páginas, tudo isso em questão de milesegundos. E com milhões de usuários fazendo isso ao mesmo tempo.

    Apesar de todo esse sucesso com seu carro chefe, o Google está deixando de ser "a empresa do mecanismo de buscas" para ser uma empresa de aplicações web. GMail, Orkut, Google Maps, Google Suggest, Google Desktop Search... E essa lista cresce a cada dia que passa.

    E como eles estão fazendo isso? Conheço milhares de empresas que desenvolvem sistemas para web e continuam só desenvolvendo sistemas para Web... O segredo deles é que eles desenvolveram mais que uma aplicação de buscas, eles desenvolveram uma plataforma para desenvolvimento de software, um software para viabilizar mais softwares. Uma plataforma de computação em cluster especializada em rodar aplicações web. E agora eles estão usando essa plataforma para desenvolver softwares que têm as mesma necessidades do mecanismo de busca: atender milhões de usuários, ter petabytes de espaço para armazenamento e um tempo de resposta mínimo.

    Essa plataforma do Google envolve gerenciamento do cluster, um FileSystem distribuído (o Google File System) que roda nesse cluster, entre outros. E o melhor é que as aplicações que são desenvolvidas nessa plataforma podem aproveitar sem muito esforço qualquer melhoria que nela for feita. Eles podem focar os profissionais mais competentes na melhoria dessa plataforma, e fazer pequenos ajustes nos softwares que rodam sobre ela. Quando alguém da equipe do Google tiver a idéia para um software web, é só desenvolver em cima dessa plataforma e pronto: milhões de usuários podem usar. Isso agiliza MUITO o desenvolvimento e permite que um novo produto seja lançado no mercado em muito menos tempo. Isso deixa a concorrência maluca, porque eles não conseguem acompanhar o ritmo e acabam fazendo alguma besteira no meio do caminho. A Microsoft usou bastante essa estratégia e derrubou líderes de mercado como o WordPerfect. É bom lembrar que a própria Microsoft começou fazendo uma plataforma para desenvolvimento de software: os compiladores de linguagem BASIC.

    A Microsoft construiu uma base sólida para desenvolver seu aplicativos: seus sistemas operacionais. (ok, a base só é sólida a partir do Windows NT, o Windows 9x é pior que gelatina). Tudo que a Microsoft desenvolve é em cima dessa plataforma. Cada dia que passa ela agrega valor a essa plataforma desenvolvendo mais softwares para ela. E cada dia que passa ela melhora essa plataforma para poder construir software melhores sobre ela. O Google segue a mesma linha e tem tanta competência na sua plataforma quanto a Microsoft tem na dela.

    Essa é uma ótima estratégia: gerar receita agregando valor a uma plataforma base muito bem construída. Se seu software exporta uma API, e você mesmo a usa para desenvolver alguma coisa, é um ótimo sinal. Você está usando o seu software para construir software. Por que você acha que as equipe da Microsoft mais respeitadas são as do Windows e a do Visual C++?

    (Procurando sobre o Google File System, encontrei um post interessante sobre esse assunto, em um blog escrito por alguém que tem um pensamento parecido sobre o Google. Vale a leitura. E antes que você fale que o Orkut é uma porcaria, não se esqueça que ele é feito em ASP.NET e não roda nessa plataforma do Google, que é baseada em Linux)

    Em 12/02/2005 02:05 - Comentários (3)

    10 anos de Delphi (programadores WinForms, comemorem!)

    O Delphi - ferramenta da Borland baseada em Pascal que nasceu para desbancar o VB - fez dez anos de vida no dia 14 de fevereiro. A Borland tentou com o Delphi (e talvez tenha conseguido) unir o poder do C++ e a facilidade do VB.

    A Borland colocou no site uma entrevista com Anders Hjelsberg, Gary Whizin, e Zack Urlocker, como parte da comemoração do aniversário. Além da Borland, os programadores WinForms também tem que comemorar, afinal, a VCL do Delphi e seu recursos (como herança visual) foram as grandes inspirações para boa parte do que está contido em System.Windows.Forms. E parabéns ao Delphi!

    Em 17/02/2005 00:14 - Comentários (0)

    Olhe só quem está no orkut...

    http://www.orkut.com/Profile.aspx?uid=7881279797765706622

    E olhe só a lista de amiguinhos dele... Achei isso navegando entre as comunidades de programação e chegando à comunidade Windows Technical: Off Topic, da (nem tanto) famosa lista de discussão homônima.

    Em 18/02/2005 05:04 - Comentários (0)

    Como fazer drivers para Windows NT/2000/XP/2003, parte 2

    Parte 2 = continuação da parte 1 :-)

    Já leu os livros recomendados na parte 1? Eu sei que leu! Então vamos ao que mais precisamos para fazer um driver.

    Driver Development Kit (DDK)

    O DDK é o SDK para fazer drivers, ele contém os headers C e todas as ferramentas necessárias para todas as etapas do desenvolvimento de um driver. Quando eu digo "todas as ferramentas necessárias" é verdade, até o compilador é incluído no DDK. É possível compilar drivers usando o VC++ 6 ou superior, mas é altamente não recomendado. Essa configuração não é suportada pela Microsoft, e o pessoal do newsgroup de drivers da Microsoft geralmente se recusa a responder perguntas relativas a compilação quando o "perguntador" usa essa configuração.

    Para obter o DDK você tem duas opções. A primeira é ter uma assinatura MSDN. Dessa forma é só entrar no "Subscriber Downloads" e baixá-lo. Para quem não tem uma assinatura MSDN, a opção é entrar no site do Windows DDK e pagar US$ 25,00 para que a Microsoft envie o DDK pelo correio.

    O DDK também vem com vários samples para várias arquiteturas de drivers (USB, SCSI, NDIS, etc) e a documentação (que também pode ser encontrada na MSDN online). A maioria dos samples são muito bons e muito bem documentados. Além disso, em boa parte das arquiteturas é recomendável que você comece a fazer seu driver a partir de um sample (ao invés de começar do zero). Drivers WDM, por exemplo, devem gerenciar Plug'n'Play, e só essa parte do código tem umas 2000 linhas...

    Existe somente uma categoria de drivers que não pode ser feita com o DDK: os drivers relacionados com File System (File Systems e filtros). Para esse tipo de driver é necessário comprar o IFS Kit (Installable File System Kit), que custa a bagatela de US$ 899,00 (um amigo meu costumava dizer que era o arquivo .h mais caro que ele conhecia. Até ele perguntar para o pessoal da OSR o preço do kit de File System deles...). Os antívirus com scan em tempo real, por exemplo, são implementados como filtros de File System.

    É bom lembrar que, apesar da palavra driver ser quase sempre usada para denominar um software que controla um hardware (driver da placa de som, por exemplo), no Windows é comum chamar de driver todo software que roda em kernel mode, mesmo que ele não controle hardware. Firewalls e filtros de file system são exemplos de drivers que não controlam hardware. Mesmo assim, no Windows existem alguns drivers que rodam em user mode.

    Editor de código C/C++

    Apesar de ser possível escrever um driver usando o Bloco de Notas, é recomendável que você use um editor apropriado para isso. Eu costumo usar o Visual Studio.NET, usando o "MakeFile Project" para compilar o driver usando o compilador do DDK e não o Visual C++. Você pode usar o editor de sua preferência.

    Em 19/02/2005 00:39 - Comentários (6)

    Por que o meu Visual C++ está fazendo REBUILD toda hora?

    Eu tenho um Solution do Visual C++ 7.1 que tem 12 projetos, entre COM/ATL e WTL, com algumas dependências entre eles. Entre essas dependências existe um header "spmain.h" que é incluído no "stdafx.h" de todos os projetos. Ontem eu adicionei algumas declarações nesse header, já consciente que todos os meus projetos seriam recompilados. Dois minutos depois tenho todos os projetos recompilados, e começo a escrever código em modo automático, até uma hora que eu não me lembro (só lembro que passava da meia noite). Depois desliguei o modo berserker e fui dormir.

    Quando fui mexer no projeto hoje de novo (sim, eu programo as domingos), percebi que o Visual C++ fazia rebuild dos projetos toda vez que eu compilava. Se fosse um projeto pequeno tudo bem, mas 30 mil linhas de C++ em 12 projetos não compila lá muito rápido...

    Procurando informações sobre esse problema, encontrei um artigo do KB da Microsoft para NMAKE e Visual C++ 5.0 que falava alguma coisa sobre problemas com arquivos na rede e com a data da instalação do Visual C++. Como meus arquivos não ficam em rede, só me restava verificar o problema da data.

    Tiro e queda: verificando a data dos arquivos, percebi que o "spmain.h" estava com data do dia 21/02/2005 (detalhe: hoje ainda é dia 20/02). Abri o arquivo, adicionei um CRLF e fechei. Problema resolvido! Se você tiver um problema parecido (eu já passei por isso antes), já sabe como resolver.

    Outro fato interessante: um projeto C# que eu estava mexendo ontem também estava com o mesmo problema, todos os arquivos estavam com data do dia 21/02. Mas como o build do C# é MUITO mais rápido que o build C++, eu acabei nem me dando conta...

    Em 21/02/2005 01:44 - Comentários (6)

    Mais um benchmark interessante

    A Microsoft apresenta no site do Visual C++ um benchmark interessante. A proposta é verificar a performance de um backend para o mercado financeiro (uma bolsa asiática), baseado em Visual C++, MSMQ e SQL Server. Lá eles descrevem de forma acadêmica diversos fatores que foram levados em conta, desde configuração de discos, até servidores SQL federados com chaves em hash para diminuir a concorrência e o gargalo do banco.

    O artigo apresenta boas dicas sobre performance para aplicações críticas e sobre metodologias para medição disso. Performance é algo complicado de medir, porque além de ser um conceito relativo, existem diversos fatores obscuros que podem distorcer um estudo - desde um design de uso de memória que aproveite o fator localidade para facilitar o trabalho do cache do processador, até a configuração do arquivo de paginação do Windows. Como Raymond Chen já explicou, otimização de performance não é algo tão óbvio quanto parece.

    No final do artigo, o autor diz que a equipe reimplementou o aplicativo em C# para fazer uma comparação de performance de processamento de mensagens entre código gerenciado e não gerenciado. Esse é um dos primeiro benchmarks que eu vejo que serve de alguma coisa. Eu já escrevi aqui algumas dezenas de vezes que comparar duas linguagens testando um algortitmo simples em loop não leva a conclusão nenhuma. O que funciona é isso, pegar uma aplicação crítica e implementar em duas linguagens. Primeiro o autor apresenta o seguinte gráfico de comparação (maior é melhor):

    E depois faz umas considerações muito interessantes sobre como essa diferença absurda entre as duas linguagens deve ser interpretada. Ele cita um fato interessante que eu já citei aqui: de que a alocação de memória em ambientes gerenciados é eficiente e tão rápido quanto C++ (o que compensa um pouco a desalocação mais lenta feita pelo GC). Além disso eles explicam que, como eu também já disse, a medida que a tecnologia da VM .NET for evoluindo a implementação em C# se tornará mais rápida e eficiente. Só que, como eu também já disse, eu vivo no presente e não no futuro. :-)

    Em 22/02/2005 13:59 - Comentários (0)

    Aulas de C++: como ensinar?

    Estou estudando uma proposta para dar aulas de C++, e estou pensando em uma didática interessante para as aulas. Tive uma conversa com o Wanderly Caloni sobre isso e quero deixar aqui registrado. Caso alguém tenha uma sugestão ou idéia sobre o assunto, coloque nos comentários desse post. Minha idéias ficaram bem claras nesse chat, o que dispensa maiores explicações. Vamos ao chat:

    Today, 09:42 Rodrigo Strauss:
    como assim aulas teóricas?

    Today, 09:43 Wanderley:
    vc falando e uma lousa

    Today, 09:44 Rodrigo Strauss:
    impossível ensinar programação assim.

    Today, 09:44 Rodrigo Strauss:
    Vai ser eu e um projetor, um aluno por micro

    Today, 09:44 Wanderley:
    mmmmmm...

    Today, 09:44 Wanderley:
    dai facilita MUITO

    Today, 09:44 Wanderley:
    ainda mais q o instrutor vai saber tirar duvidas eheheehheheh

    Today, 09:44 Rodrigo Strauss:
    sim, e vai ser assim

    Today, 09:44 Rodrigo Strauss:
    to pensando em adotar uma estratégia diferente

    Today, 09:45 Rodrigo Strauss:
    li uma coisa no comp.lang.c++.mod e fiquei pensativo

    Today, 09:45 Rodrigo Strauss:
    todo mundo q ensina C++ começa pela parte difícil

    Today, 09:45 Rodrigo Strauss:
    ponteiros, strcpy, etc

    Today, 09:45 Rodrigo Strauss:
    estou pensando em já começar usando std::string

    Today, 09:45 Rodrigo Strauss:
    e passando tudo por valor

    Today, 09:48 Wanderley:
    dai vai ser vb! =P

    Today, 09:51 Rodrigo Strauss:
    e qual o problema? Não é mais lógico ensinar primeiro a parte fácil e depois a difícil?

    Today, 09:59 Wanderley:
    em ambas as formas existem vantagens e desvantagens. no caso de comecar pelo mais facil, a desvantagem eh esconder desde o comeco detalhes essenciais para programar em C

    Today, 10:00 Rodrigo Strauss:
    não é esconder, é só começar pelo mais fácil. E eu vou dar aulas de C++, vou deixar claro pra eles que não é aula de C

    Today, 10:02 Wanderley:
    separar C de C++? mmmmmmmmmm...
    =P

    Today, 10:03 Rodrigo Strauss:
    vou explicar as diferenças e similaridades e dizer que linguagem C é mais específica

    Today, 10:04 Wanderley:
    seilah. penso q sao coisas indivisiveis pq uma eh parte do todo

    Today, 10:04 Rodrigo Strauss:
    sim... mas como vc mesmo já disse, nós não sabemos programar em C. O C tem várias limitações e diferenças q não estamos acostumados

    Today, 10:06 Rodrigo Strauss:
    essas aulas vão ser muito boas pra mim, pq vão me fazer estudar mais coisas e mais termos. Se vc me perguntar o q é polimorfismo eu não sei explicar

    Today, 10:06 Rodrigo Strauss:
    vou ter q comprar o livro do Stroustrup urgente

    Today, 10:06 Wanderley:
    geralmente a pratica acaba sendo mais fixada na mente de quem realmente programa =P

    Today, 10:07 Rodrigo Strauss:
    EU CONSEGUI! Escrevi Stroustrup certo sem consultar!
    Stroustrup, Stroustrup, Stroustrup!

    Today, 10:07 Rodrigo Strauss:
    Bjarne Stroustrup

    Today, 10:07 Rodrigo Strauss:
    Bjarne Stroustrup

    Today, 10:07 Rodrigo Strauss:
    Bjarne Stroustrup

    Today, 10:07 Rodrigo Strauss:

    Today, 10:09 Rodrigo Strauss:
    o seu Stroustrup é em portuga, né?

    Today, 10:16 Wanderley:
    eiuaheiuahieuaeihauhiauehiaueaiehiahuei
    eh, sim
    ta afim?

    Today, 10:16 Rodrigo Strauss:
    mais um clique na amazon e eu compro o meu

    Today, 10:17 Rodrigo Strauss:
    e to pensando em comprar esse tb:

    Today, 10:17 Rodrigo Strauss:
    http://www.amazon.com/exec/obidos/tg/detail/-/0201749629

    Today, 10:21 Wanderley:
    q baum!! =D

    Today, 10:22 Rodrigo Strauss:
    to quase comprando... mas eu tenho gasto uma puta grana com livros ultimamente

    Today, 10:22 Rodrigo Strauss:
    é sempre um bom gasto, mas é um gasto. E o bom é que livros de C++ nunca ficam desatualizados

    Today, 10:27 Wanderley:
    =D

    Today, 10:28 Rodrigo Strauss:
    vc já leu o blog do thiago adams?

    Today, 10:29 Wanderley:
    naum. qual o link?

    Today, 10:33 Rodrigo Strauss:
    http://planeta.terra.com.br/informatica/thiago_adams/codigo.htm

    Today, 10:33 Rodrigo Strauss:
    pena q não tem RSS

    Today, 14:13 Wanderley:
    onde vc viu a msg sobre ensinar c++ comecando pelo mais facil?

    Today, 14:14 Rodrigo Strauss:
    eu li uma mensagem no comp.lang.c++.moderated sobre um cara dizendo q o problema é q em C++ as pessoas começar pelo contrário, pelas coisas mais difíceis

    Today, 14:14 Rodrigo Strauss:
    vc não acha q ponteiro é uma ferramenta muito poderosa pra dar nas primeiras aulas?

    Today, 14:15 Wanderley:
    acho

    Today, 14:15 Wanderley:
    na primeira aula se dá variaveis e construções simples de programação

    Today, 14:15 Rodrigo Strauss:
    não é melhor que o cara que está começando use std::string ao invés de fazer um buffer overflow esperando pra acontecer?

    Today, 14:16 Wanderley:
    nao enquanto ele nao entender o funcionamento e os riscos de um buffer overflow

    Today, 14:17 Rodrigo Strauss:
    sim, mas acho q isso pode ser ensinado depois

    Today, 14:17 Wanderley:
    mas a lista do c++.moderated eh enorme
    vc nao tem o link?

    Today, 14:17 Rodrigo Strauss:
    a maioria usa char* e não sabe o q é um buffer overflow

    Today, 14:18 Rodrigo Strauss:
    eu li ontem num post sobre c#, mas não lembro onde

    Today, 14:18 Rodrigo Strauss:
    esse cara não vai saber o q é um buffer overflow, mas pelo menos ele vai correr menos riscos

    Today, 14:18 Wanderley:
    a maioria faz isso pq nao aprendeu todos os conceitos. soh os necessarios pra sair programando...

    Today, 14:19 Wanderley:
    por isso q eu disse q eh vblizar o aprendizado: evitando riscos escondendo perigos

    Today, 14:20 Rodrigo Strauss:
    eu acho q é jogar o cara no fosso com leões só depois que ele conseguir matar um cachorro.

    Today, 14:21 Wanderley:
    qualquer abordagem eh valida qdo se trata de ensinar C ou C++ (ja li isso em algum livro eheheeh). mas existem vantagens e desvantagens em todas. eh vivendo, programando e aprendendo

    Today, 14:22 Rodrigo Strauss:
    minha experiência tem dito que fazer coisa low-level a toda hora por qualquer coisa prejudica o desenvolvimento. "Humble thyself, reuse".

    Today, 14:23 Rodrigo Strauss:
    se eu usar std::string não vou perder tempo com corrupção de memória. E SE FICAR LENTO, eu otimizo

    Today, 14:24 Wanderley:
    eu entendi. funciona. mas vc nao pode dizer q uma pessoa sabe c++ se ela nao consegue explicar o que eh uma string em C

    Today, 14:39 Rodrigo Strauss:
    claro, isso eu concordo

    Today, 14:39 Rodrigo Strauss:
    meu ponto é só que ela deve começar pelo mais fácil

    Today, 14:47 Wanderley:
    isso eh questao de gosto

    Today, 14:47 Wanderley:
    de objetivo tb

    Today, 14:47 Wanderley:
    ja q eles vao mexer com mfc, eh bom q se acostumem com a ideia de classe string, por ex

    Today, 14:47 Rodrigo Strauss:
    eu acho q é questão de didática e motivação

    Today, 14:47 Rodrigo Strauss:
    eu acho C difícil, imagina pra quem não sabe programar

    Today, 14:48 Rodrigo Strauss:
    é muito mais fácil alguém q nunca programou entender um
    cout << "numero " << 5 << endl;
    do que
    printf("numero %d\r\n",5);

    Today, 14:49 Wanderley:
    o Ritchie ensina muito bem as nuances da linguagem em seu livro. eh claro q a pessoa ja tem q ter um minimo de conceitos

    Today, 14:49 Wanderley:
    muito mais facil entender intuitivamente. mas nao tecnicamente

    Today, 14:50 Rodrigo Strauss:
    as pessoas tem uma visão distorcida de C++, acham q é muito complicado. Se usar STL não é muito complicado.

    Today, 14:51 Wanderley:
    nao se nao der erro

    Today, 14:52 Rodrigo Strauss:
    sim... e algumas coisas no STL tb são phoda. como tempo de vida de um interator

    Today, 14:54 Wanderley:
    mas, claro, se vc usa religiosamente como manda o figurino, tudo funciona

    Today, 14:56 Rodrigo Strauss:
    se vc usar char* como manda o figurino nunca dá buffer overflow

    Today, 14:59 Wanderley:
    aih q entra o grau de dificildade =P

    Today, 15:00 Rodrigo Strauss:
    sim. como é mais fácil usar o std::string sem fazer besteira me parece mais óbvio ensinar isso primeiro

    Today, 15:00 Rodrigo Strauss:
    do mesmo jeito que eu recomendo VB para iniciantes por ser mais fácil

    Today, 15:00 Wanderley:
    pior q nao eh. depende do gosto e/ou objetivo
    eu recomendo basicao

    Today, 15:01 Rodrigo Strauss:
    eu vou pensar bastante nisso. Quero criar uma geração que não use C só por costume (como a gente faz), e use realmente C++ e STL sempre (como eu quero fazer daqui pra frente)

    Today, 15:02 Wanderley:
    ohhhhhhhhhh
    nisso eu apoio. leia o livro do Stroustrup. ele eh um categorico dessa ideia

    Today, 15:04 Rodrigo Strauss:
    sim... no site dele ele diz que um livro de C++ que começa com printf já começa mal...

    Today, 15:05 Wanderley:
    ehehehehhehehehehhe. o legal eh q ele eh categorico, mas nao eh xiita, q fala q C++ deve ser usado soh com orientacao a objetos. eh o criador, neh. dai eh outra coisa

    Today, 15:09 Rodrigo Strauss:
    O Criador...

    Today, 15:09 Wanderley:
    ehehehehhehehehehheh

    Today, 15:15 Rodrigo Strauss:
    comprei o Stroustrup!

    Today, 15:15 Wanderley:
    =D

    Today, 15:15 Rodrigo Strauss:
    Esse chat merece um post.
    Autoriza?

    Today, 15:15 Wanderley:
    agora vc ouvirah a palavra tb. e logo estara pregando a palavra =P

    Today, 15:16 Wanderley:
    se vc acha q merece, vai la

    Today, 15:16 Rodrigo Strauss:
    A palavra do Criador, sem apóstulos, sem intermediários. É como se "God himself" tivesse escrito a Bíblia

    Today, 15:17 Wanderley:
    eiuhaieuhaiuehiauheiauhieua eh vero. sem interpretacoes erroneas. eh o proprio!

    Em 23/02/2005 20:36 - Comentários (3)

    MandrakeSoft compra a Conectiva

    Direto do Slashdot: A européia Mandrakesoft, responsável pelo famoso "Mandrake Linux", comprou a brasileira Conectiva.

    Esse é sem dúvida um evento muito importante para o mercado de software brasileiro. E com tamanho interesse do governo brasileiro pelo Linux, era mesmo de se esperar que alguma empresa estrangeira do setor arrumasse uma boa forma de entrar no nosso mercado.

    Em 24/02/2005 19:41 - Comentários (0)

    Otimização e Pessimização

    Eu li em algum lugar que "otimização precoce é pior do que nenhuma otimização", e a cada dia que passa eu tenho visto que isso é realmente verdade. Qual o motivo de otimizar um programa que você nem terminou? Na verdade você não sabe exatamente o que vai trazer problemas de desempenho. Então, na realidade, você não está fazendo uma otimização, e sim uma pessimização. Existem alguns conceitos básicos de desempenho que devemos seguir, mas não é justificável levarmos duas vezes mais tempo para fazer algo porque queremos bom desempenho. Faça primeiro, do modo mais rápido e correto possível, com o código simples e claro, e deixe para otimizar somente a parte problemática, se existir uma. Não tente resolver um problema que ainda não existe.

    É muito comum programadores C++ terem preocupações excessivas com otimizações precoces e poucas preocupações em fazer um código seguro, que funcione, e feito em tempo hábil. Por isso que temos tantos buffer overflows e coisas assim. Não seria melhor usarmos std::string para tudo e parar de nos importar se ela faz uma (ou duas) cópias da string no retorno? Nós vivemos no mundo das VMs, do .NET e das linguagens script. Um código C++ que copia uma string desnecessariamente ainda será dezenas de vezes mais rápido do que tudo que existe por aí. E você ainda poderá otimizar se quiser (eles não).

    Faça um programa que funcione, de uma maneira simples e com um código claro. Depois que tudo estiver funcionando, otimize o que ficou mais lento. Se você faz uma otimização precoce (usando ponteiros e memcpy irresponsavelmente, por exemplo), existe uma possibilidade de, além de não ficar tão mais rápido, você criar um buffer overflow esperando para acontecer. Se você escrever um código simples e PSICOLOGICAMENTE lento, você pode fazer a otimização depois, e testar especificamente aquele memcpy para se certificar que nada de errado vai acontecer. Com a otimização precoce, o seu memcpy suicida será testado junto com o resto do software, e não receberá a devida atenção. Mas o teste que será feito depois da otimização será específico para esse memcpy, e é muito mais provável que os erros sejam encontrados.

    Hoje a velocidade os processadores é da ordem de gigahertz, podemos nos dar ao luxo de nos preocupar em otimizar as partes interessantes e fazer de forma simples e clara as partes menos interessantes. Mas não esqueça: já que não vai otimizar e está disposto a queimar alguns nanosegundos a mais, pelo menos faça o código de forma clara. Se seu medo é que o próximo programador a mexer no código fale que você faz código lento, coloque um comentário explicando que você tem mais o que fazer do que otimizar uma função que lê do registro e só é chamada uma vez...

    PS: Sim, eu sou um programador C++.

    Em 26/02/2005 05:43 - Comentários (7)

    Testando o ASP.NET 2.0

    Essa é para os interessados em teste de software (ou para os que tem inveja de equipes que tem infraestrutura de testes). Scott Watermasysk, um membro da equipe do ASP.NET, fez um post explicando com um bom nível de detalhamento como são feitos os testes do ASP.NET 2.0. Lá ele descreve o software para gerenciamento de testes que eles desenvolveram (que inveja...) e o software para controle de bugs. O software de testes gerencia os casos e cenários a serem testados e o ambiente de teste, incluindo instalação automática de software para montagem dos ambientes.

    Esse post me fez lembrar uma coisa: quando eu fiz entrevista para trabalhar na Microsoft US (um dia eu conto essa história aqui), eu descobri que a Microsoft é uma das poucas empresas que tem desenvolvedores especializados em testes (eles chamam de Software Design Engineer in Test). São desenvolvedores 100% dedicados a fazer softwares para testar softwares, e para produzir maravilhas como essa que testa o ASP.NET 2.0.

    Além desse post, existem alguns vídeos da Sara Ford no Channel9 Developers Division.

    Em 02/03/2005 01:11 - Comentários (0)

    Cante comigo

    Imagine (with apologies to John Lennon)

    Imagine there's no requirements. It's easy if you try
    Just a bunch of coders, reachin' for the sky
    Imagine all the people, coding for today
    Imagine there's no schedules. It isn't hard to do
    No silly project deadlines, no one supervising you
    Imagine all the people, coding hand in hand

    You may say I'm an extremer but I'm not the only one
    I hope someday you'll join us and make coding lots more fun.

    Imagine oral documentation. I wonder if you can
    No need for UML diagrams. Just words passed, man to man
    Imagine just refactoring, playing in the sand

    You may say I'm an extremer, but I'm not the only one
    I hope someday you'll join us and make coding lots more fun.

    Mais uma paródia de Imagine, retirada do artigo da Wikipedia sobre Agile Software Development. Mande isso para aquele seu gerente dilbertiano que acha que RUP é a oitava maravilha do universo. Você pode dar sorte e ele ter um enfarte. :-)

    Em 03/03/2005 15:01 - Comentários (0)

    Mark Lucovsky vai para o Google

    Pode parecer mais um grande engenheiro de software sendo contratado pelo Google, mas para mim significa o início de uma nova era. Para quem não sabe, Lucovsky é um dos engenheiros originais do Windows NT, que veio da DEC junto com Dave Cutler para desenvolver o novo sistema operacional da Microsoft. Naquela época (1989) o projeto era fazer um kernel modular (e não um micro kernel como alguns dizem) que rodasse em um protótipo de processador da Intel (o N10) e suportasse basicamente a API do Presentation Manager do IBM OS/2. Depois de um tempo, esse novo sistema operacional (já batizado de NT, sem o "Windows" antes) foi portado para plataforma Intel e Alpha. Além disso, a API do Presentation Manager foi substituída pela API Win32, que foi criada para aproveitar o boom do Windows 3.0.

    Muitos dos componentes do kernel do Windows NT/2000/XP/2003 foram feitos por Lucovsky. Na verdade toda vez que você executa um programa, você está rodando um código feito por ele (ele foi responsável pela parte do kernel que gerencia processos e threads). Ele foi responsável também pelo gerenciamento do controle de versão desses projetos gigantescos (como ele explica nessa apresentação). Mais algum dúvida sobre a importância dele para Microsoft?

    Vamos agora à explicação do porquê eu acho que estamos no início (sim, no início) de uma nova era. Lucovsky escreveu um post mês passado sobre como a Microsoft não é mais a melhor em fazer, entregar e distribuir software. E a visão dele é muito interessante: quando alguém na Amazon (ou no Google) faz uma modificação no software deles, milhões de pessoas têm acesso a essa modificação na mesma hora. Quando a Microsoft faz uma modificação, esse modificação precisa de um download, muitas vezes da instalação de uma runtime (.NET), precisa que o usuário aceite um contrato, faça ativação, etc.

    Lembra quando eu falei sobre a "Plataforma Google"? O Google mostra cada vez mais que está investindo nessa plataforma e ela é realmente o core da empresa. O mecanismo de buscas é, na realidade, o software mais importante que roda sobre essa plataforma.

    As contratações do Google são todas na direção do que a empresa já faz, e não na criação de algo revolucionariamente novo. Contratando especialistas em sistemas operacionais (como Lucovsky e Pike) não significa que eles irão lançar um sistema operacional. Contratando especialistas em browsers (como Goodger, do Firefox) não significa que eles NECESSARIAMENTE lançarão um browser (Só mesmo o bobinho e inocente John "idéias não-tão-geniais" Dvorak para pensar assim). Essas são as plataformas que eles usam (o cluster Linux e Ajax), quanto mais eles entenderem delas mais sucesso eles terão. Se uma fábrica de automóveis contrata especialistas em aço, não significa que ela necessariamente vai entrar no ramo de siderurgia...

    Em 04/03/2005 16:16 - Comentários (4)

    Lista de libs C++, GCC-XML e divagações sobre computação distribuída

    Encontrei duas coisas interessantes sobre C++ hoje. A primeira foi uma página com uma lista de libs C++ disponíveis. Esse lista contém links para dezenas de libs, desde as mais conhecidas (como a ACE) até libs para sequenciamento biomolecular (!?). Vale a pena perder um tempinho navegando na lista.

    A segunda é o GCC-XML, uma versão (talvez "versão" não seja o termo correto) do GCC que ao invés de compilar o programa, gera um XML com a estrutura do programa. Estou desenvolvendo um framework para computação distribuída nas horas vagas, e nas últimas semanas "criei" um formato de IDL em XML. Basicamente você cria uma arquivo XML com as definições das interfaces e seus métodos, e um programinha (feito em C#) gera o código C++ para proxy/stub. Eu podia usar DCOM, mas minha infra-estrutura precisa ser flexível e extensível (e multiplataforma se possível). Isso envolve comunicação de diversas formas (TCP/IP, MSMQ, MQSeries, FileMapping, ISAPI, etc) e não ter problemas com firewall. Pode parecer que eu estou refazendo a roda (refazendo o DCOM mais precisamente), mas acho que esse framework se parecerá mais com o Indigo.

    Voltando à IDL em XML: eu cheguei a pensar em usar YACC/LEX para gerar um parser para MS-IDL, mas achei que seria muito trabalhoso. No final das contas, cheguei à conclusão que um formato XML seria bem mais interessante, pois me permitiria colocar informações extras na IDL/XML (como comentários e links para documentação) sem alterar a estrutura do programa gerador - que nada mais faz do que carregar um XmlDocument e usar XPath para encontrar as interfaces e métodos. Além disso, seria mais fácil manter compatibilidade com os geradores antigos, já que usando XPath eu automaticamente ignoro qualquer propriedade ou entidade que eu não conheça.

    Usando o GCC-XML eu posso até gerar o XML diretamente de uma classe C++, e "passá-lo" por um XSLT para gerar um XML no formato do meu gerador. Com isso eu poderia colocar os metadados das classes (inclusive sobre variáveis privadas) dentro do programa, e ter informações precisas em run-time, exatamente como em managed code. Seria possível até fazer um marshal-by-value automático, como acontece com as classes [Serializable] em .NET. Isso funcionaria como um TypeLibrary COM vitaminado, com informações extensas sobre as classes e dados que elas encapsulam.

    Eu poderia também fazer um Attribute Provider para o Visual C++ 7.1. Eu também poderia fazer um compilador e criar uma runtime. Ah, e eu poderia ... mmmm ... esquece... Eu sou um programador solitário, e essas divagações - apesar de interessantes - levam bastante tempo para implementar...

    Em 07/03/2005 22:53 - Comentários (3)

    Artigo sobre WinDbg parte 3: a missão

    Já está no ar a terceira parte da minha série de artigos sobre o WinDbg. Nessa parte eu explico o que são os symbols, para que servem, e porque eles facilitam tanto as nossas vidas.

    Em 14/03/2005 05:23 - Comentários (0)

    Algumas coisas deveriam funcionar, mas não funcionam

    O trecho a seguir foi retirado do documento "MSMQ Best Practices":

    Asynchronous notifications using WithEvents in Visual Basic can be a powerful feature. The idea of running code only in response to an event is quite attractive. However, note the following:

    • Events can get lost, and you should periodically reenable notification.
    • Multiple clients will be notified in the event of a single message. This problem is common; the application ceases to respond to user input. To fix this, ensure that all subsequent receives have timeouts set for the MQReceiveMessage function.

    Note como o documento é bem específico. Eu traduziria esse trecho assim: "Os eventos podem se perder de acordo com a fase lunar, é prudente reabilitar as notificações em um intervalo que você se sinta confortável, de acordo com a sua intuição".

    Em 15/03/2005 14:48 - Comentários (1)

    Alguém ainda procura convites para o GMail?

    Achei um site que tem +-500 mil convites do Gmail para doação. Eu testei, realmente funciona e você recebe o convite na hora. Apesar de estar quase abrindo para o povão, uma conta do GMail agora cai bem, nem que seja para usá-la como um disco virtual de 1 GB.

    Em 16/03/2005 04:11 - Comentários (4)

    Não tem jeito, a Microsoft não respeita os programadores VB

    O suporte "standard" da Microsoft para o VB6 está chegando ao fim. Considerando a quase total incompatibilidade entre o VB6 e o VB.NET, a comunidade VB está se mobilizando (de forma tardia) para tentar reverter a situação, inclusive tentando que a Microsoft suporte o VB6 (que eles chamam de "Classic Visual Basic") e faça atualizações para ele. Foi criada uma petição, que pode ser assinada no site classicvb.org.

    Até agora, a petição foi assinada por mais de 2300 desenvolvedores, incluindo 224 MVPs. O mais engraçado que é não valeu nada, a Microsoft não fez nada. Se 1 MVP tem tanta importância como ele dizem, porque 200 deles não valem nada? Não estou dizendo que a Microsoft deveria simplesmente criar o "Classic Visual Basic", mas acredito que eles mereciam mais atenção. A única reação da Microsoft foi uma entrevista que o Somasegar (cujo cargo é "Corporate Vice President - Developer Division") deu ao CNET. Achei as considerações do Somasegar ridículas e as desculpas completamente esfarrapadas:

    • A Microsoft não pretende atualizar ou lançar uma nova versão do "migration wizard". Isso realmente é uma questão de (falta de) respeito.
    • Fazer com que o "Classic Visual Basic" rode na IDE do VS.NET é "tecnicamente não plausível". Espero que isso queira dizer um "nós não queremos fazer", porque se isso significa dificuldade técnica, a Microsoft já não é mais a mesma.
    • O Visual Basic 8 (VS 2005) será muito melhor em RAD (Rapid Application Development). Será que ele realmente entendeu do que se trata ou está mesmo fugindo do assunto? RAD por RAD, os programadores VB6 podem muito bem mudar para o Delphi, já que ele tem um RAD ótimo. Do que adianta ficar desenhando formulários multicoloridos em tempo recorde se a linguagem é incompatível? Estamos falando em compatibilidade com os milhões de sistemas desenvolvidos em VB6 até hoje.
    • No Visual Studio 2005, recursos conhecidos do VB6 serão trazidos de volta, como o "Edit and Continue". Mais uma consideração ridícula. Tiraram esse recurso do VB e agora estão jogando confete por colocar de volta. É como roubar algo de alguém e devolver como um presente, 4 anos depois.

    Mesmo que muitas pessoas considerem o VB6 "feio" e limitado, ele era (não sei se ainda é) a ferramenta de programação Microsoft mais usada. Existem milhões de linhas de código em VB pelo mundo, e elas ficaram estagnadas para sempre. Tudo isso porque a Microsoft está dando importância ao "puritanismo técnico" típico de programadores C++ ao invés de ouvir os clientes. "Eu devo dar o peixe ou ensinar a pescar? Achamos melhor direcionar nosso esforços para trazer os clientes para o 'novo mundo'", disse o Somasegar. Na boa, esse é um bom método para tratar crianças, não milhões de programadores. Acho que está na hora do pessoal do VB aprender com o pessoal do FoxPro como fazer para ganhar o respeito da Microsoft sem ser programador C, C++ ou C#...

    Não estou dizendo que a Microsoft deveria simplesmente fazer o que esses 230 MVPs querem (ou deveria?). Só acho que ela deveria dar mais atenção à reivindicação, e tentar arrumar a situação. Eles não são usuários domésticos, são programadores que mantêm sistemas de bancos, instituições financeiras e empresas gigantescas. Será que a Microsoft pode se dar ao luxo de tratá-los como moleques que não sabem o que querem? Ah, apesar de dar a entrevista, o Somasegar não escreveu uma palavra sobre isso no blog dele.

    [Atualização 17/03/2005: O Somasegar resolveu se mexer, e escreveu um post sobre isso. Repetindo que a grande resposta da MS ao assunto será criar um "VB Upgrade Center" na MSDN...]

    Em 17/03/2005 04:50 - Comentários (10)

    C++ com sanidade: usando ATL em aplicações

    Muita gente costuma dizer que em C++ programa-se muito para pouco resultado. Todo mundo assume que esse "resultado" roda bem mais rápido, mas já é outra coisa... Eu acredito que é possível manter a sanidade e a vida social programando em C++, só é preciso ter um pouco mais de conhecimento e um pouco mais de cuidado. Pretendo escrever bastante sobre isso se eu tiver tempo.

    A lenda sobre o fato de programar em C++ ser trabalhoso, vem principalmente do fato de que as pessoas usam C++ como um C melhorado. Conheço vários programadores C++ que não usam STL e que ainda ficam manipulando char* a toda hora, em pleno ano de 2005 (eu fazia isso até o ano passado). Apesar do C++ ser um superset da linguagem C, ele tem dezenas de melhorias que permitem escrever um código mais organizado, simples e conciso.

    Eu tenho estudado muito sobre qualidade de código, incluindo sobre como escrever um código claro e com menos bugs. Quanto mais eu estudo, mais eu vejo como isso é possível, e como C++ é uma ferramenta maravilhosa para esse fim. C++ segue o conceito usado para aproximar uma linguagem do nível ideal: fazer as coisas simples serem simples, e as coisas complexas serem possíveis. Muitas linguagens de alto nível que existem fazem o simples ser simples, mas fazem o complexo ser complicado demais e cheio de gambiarras e interops.

    A API Win32 não é lá muito sã. Para criar uma janela em Win32 puro (como Petzold), é necessário uma centena de linhas de código. E tudo que você ganha é uma janela branca e muito feia. Mas não podemos esquecer que a API Win32 ainda herda da API do Windows 1.0, que foi feita para linguagem C, usando os conceitos de programação da época. O mais engraçado é que ainda usam esse conceito hoje em dia... Alguém já tentou enviar uma mensagem para o MSMQ usando a API para C? É coisa para quem não tem mais o que fazer. Apesar de gostar bastante de Win32, eu assumo que é um pouco por masoquismo e por orgulho de ter conseguido aprender isso... :-)

    Escolhi ATL para começar esse assunto porque, além de ser uma biblioteca que eu gosto muito, ela ajuda a trazer um pouco de sanidade à programação Win32. E ao contrário da MFC, não é um framework que você é obrigado a seguir. MFC é bom e tem seu espaço, mas vou explorar a ATL porque ela permite o uso "avulso" e por ser menos usada em Win32.

    ATL (Active Templte Library) é basicamente uma biblioteca de templates usada para facilitar o desenvolvimento de componentes COM. Além de cumprir de modo soberbo esse papel, ela também tem diversas classes (muitas delas desconhecidas) para facilitar a programação Win32, tornando nossa vida muito mais fácil. A ATL, ao contrário do MFC, não cria dependência de nenhuma DLL. É só incluir os headers e sair usando. Se você fizer um disassembly de um executável RELEASE que usa ATL, verá que ela é um "thin layer", e que muito da ATL "desaparece" (vira inline) depois da otimização.

    Hoje vamos ver um exemplo de uso da ATL para leitura de arquivos e para buffers:

    
    #define UNICODE
    #define _UNICODE
    
    #include <atlbase.h>
    #include <atlstr.h>
    #include <atlfile.h>
    #include <atlmem.h>
    
    int wmain(int argc, WCHAR* argv[])
    {
       HRESULT hr;
       ATL::CAtlFile hFile;
       ATL::CString strFileName;
       ATL::CHeapPtr<char> pBuffer;
       unsigned __int64 iSize;
       
       strFileName = L"c:\\boot.ini";
    
       //
       // abrindo o arquivo. Bem parecido com o ::CreateFile, mas com parâmetros defaults
       //
       hr = hFile.Create(strFileName, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING);
    
       if(FAILED(hr))
       {
          MessageBox(NULL, ATL::CString("Erro ao abrir ") + strFileName, L"Erro", MB_ICONERROR);
          return hr;
       }
    
       hFile.GetSize(iSize);
    
       //
       // usando o CHeapPtr como buffer. Vamos alocar, e ele desaloca no destrutor
       //
       pBuffer.Allocate(iSize + 1);
    
       //
       // vamos ler o conteúdo do arquivo, usando o CHeapBuffer
       // Como ele sobrecarrega os operadores de conversão, podemos usá-lo
       // como se fosse mesmo um ponteiro para um buffer
       //
       hFile.Read(pBuffer, iSize);
    
       //
       // Vamos colocar um \0 no final para fechar a string
       //
       pBuffer[iSize] = '\0';
       
       //
       // vamos usar MessageBoxA (ANSI) pq nosso programa é UNICODE
       // Não se esqueça que os arquivos texto geralmente são ANSI
       //
       MessageBoxA(NULL, pBuffer, "Conteúdo do arquivo", MB_OK);
    
    
       //
       // Não precisamos desalocar o buffer
       // não precisamos fechar o handle do arquivo
       //
       
       return 0;
    }
    

    Note que o código usa UNICODE, já que não temos necessidade de suportar Windows 9x nesse exemplo. Note que esse código é mais claro do que o código que usa Win32 diretamente, e com mais facilidades. Não precisamos fechar o HANDLE para o arquivo e não precisamos desalocar o buffer usado. Eu costumo usar o "ATL::" para ser notório quais objetos são da ATL. Isso evita confusão com o objetos parecidos que existem na MFC.

    Em 24/03/2005 03:50 - Comentários (0)

    Eu acho que sei C++

    Eu sempre achei que conhecia bem C++, afinal, trabalho com isso faz vários anos. Já fiz drivers (sim, usando C++), programas em WTL, uso bastante ATL, etc. Sempre me considerei "fluente" em C++. Meu interesse em C++ tem crescido mais e mais ultimamente, e tenho buscado ampliar meus horizontes. Com esse objetivo, comprei dois livros do Herb Sutter ( Exceptional C++ e More Exceptional C++, muito bons), o livro do Stroustrup, tenho usado muito STL, estudado Boost e generic programming. Foi então que eu descobri que o C++ moderno está um pouco longe do que eu aprendi a 7 anos atrás, e apesar de fluente em C++, eu ainda tenho muita coisa para aprender.

    Se você é programador C++, faça um teste. Dê uma lida no comp.lang.c++.moderated e veja se você consegue entender as discussões. Dê uma olhada no blog do Thiago Adams e veja se você compreende tudo que ele escreve. Se você entende tudo, você realmente entende de C++. Eu até entendo tudo isso, mas programar "C++ moderno" só está virando parte do meu cotidiano agora. Mesmo assim, eu não tenho experiência nem conhecimento para implementar muitas das libs que fazem parte do Boost. Foi tão mais fácil conhecer tudo de VB6... :-)

    (Para quem não sabe o que é Boost: é uma espécie de extensão extra-oficial para a Standard Template Library do C++. É formada por libs que podem ser propostas por qualquer um, é só passar pelo crivo da lista de discussões do Boost. Dez libs do Boost serão incluídas no próximo padrão C++.)

    Outra coisa que eu acabei percebendo é que, apesar de o C# e o Java serem baseados em C++, eles estão muito distantes do C++ moderno. É só dar uma olhada em alguma lib do Boost e isso que eu estou falando ficará muito claro. É possível que elas se aproximem um pouco mais com a implementação de generics, mas só o tempo vai dizer. Essas linguagens são baseadas no C++ de 10 anos atrás.

    Eu vou começar a dar aulas de C++ in company semana que vem. Uma das coisas que eu pretendo fazer com as minhas aulas é formar alunos que não tenham o vício que eu (nós) sempre tive(mos): usar o C++ como um C melhorado. Como eu já escrevi em post anteriores, não é preciso fazer programação low level só porque se sabe como fazer. É como usar um canhão para matar moscas. Da mesma forma que eu recorro ao C# para tarefas que ele é melhor do que o C++ (XML, por exemplo), devemos saber reusar as coisas que já estão feitas e deixar de lado o "eu não sei direito como isso funciona, não fui eu que fiz". Quantas vezes eu não fiz um thin wrapper para alguma coisa em Win32, só para descobrir depois que já tinha uma classe igual na ATL...

    Isso me fez lembrar de uma coisa muito interessante: a STL ajuda muito a resolver o problema do "não fui eu que fiz". Como os containers são intercambiáveis entre si, é possível usar usar um std::map hoje, e depois trocá-lo por um hash map do Google, ou até mesmo implementar um. O padrão de interface/implementação disponível em Java/COM/CORBA/.NET ajuda nesse sentido, mas tem o problema de ser amarrado ao tipo do dado (ou boxing/unboxing desenfreado). Como a STL é toda baseada em templates e generic programming, esse problema quase some.

    Bom, chega de divagações de sábado a noite...

    Em 27/03/2005 05:37 - Comentários (0)

    C++ com sanidade: fazendo a IDE do Visual Studio.NET trabalhar para você

    Uma das coisas mais irritantes que acontecem durante o DEBUG de uma aplicação ATL, é o fato do VS.NET insistir em fazer debug das sobrecargas de operadores dos "SmartTypes" do ATL. Quando você usa "Step Into" (F11) para fazer debug de uma função, você é obrigado a passar por todas as sobrecargas de operadores antes de chegar na função. Como um pedaço de código fonte vale mais do que (e*pi)^3 palavras, vamos ao que interessa:

    HRESULT DoTheFlufflers(BSTR str, VARIANT v, IUnknown* pUnk)
    {
       MessageBox(NULL, str, L"BSTR", MB_OK);
    
       if(v.vt == VT_BSTR)
          MessageBox(NULL, v.bstrVal, L"VARIANT", MB_OK);
       else
       {
          MessageBox(NULL, 
                     L"Será que você poderia, por obséqio, colocar uma string nessa Variant?", 
                     L"VARIANT", 
                     MB_OK);
          return E_UNEXPECTED;
       }
    
       //
       // dã!
       //
       pUnk->AddRef();
       
       pUnk->Release();
    
       return S_OK;
    }
    
    int main()
    {
       HRESULT hr;
       CComBSTR str;
       CComVariant v;
       CComPtr pUnk;
    
       str = L"Eu sou uma string legal, podemos ser amigos?";
       v = 0xDEADBEEF;
    
       hr = CoCreateInstance(CLSID_FLUFFLERS, NULL, CLSCTX_ALL, IID_IUnknown, (void**)&pUnk);
    
       if(FAILED(hr))
       {
          OutputDebugString(L"Eu desisto...");
          //
          // pena que não funciona em UserMode...
          //
          // KeBugCheckEx(0xFFFFFFFF,0,0,0,0);
          return hr;
       }
    
       DoTheFlufflers(str, v, pUnk);  // <<-- Muito chato fazer Step Into nessa função...
    
       return 0;
    
    }
    

    Quando você usar o F11 sobre a instrução "DoTheFlufflers(str, v, pUnk);", o VS.NET vai entrar primeiro em "ComPtrBase::operator T*()", depois em "CComBSTR::operator BSTR()", e só depois em "DoTheFlufflers". Se essa função fosse de um objeto COM (algo como "pUnk->DoTheFlufferization(str, v, pUnk)"), você ainda teria chamada de "ComPtrBase::operator ->" antes das outras. Isso realmente é muito chato.

    Mas há uma solução para esse problema no VS.NET: Existe a chave [HKEY_CURRENT_USER\\Software\\Microsoft\\VisualStudio7.1\\NativeDE\\StepOver], onde você pode colocar nomes de funções que não terão StepInto. É só criar valores string com um id numérico e com valor "um_regular_expression=NoStepInto". Para ignorar todos os métodos de CComPtr, use algo como ".+CComPtr.+=NoStepInto". A minha configuração por enquanto é essa:

    Windows Registry Editor Version 5.00
    
    [HKEY_CURRENT_USER\\Software\\Microsoft\\VisualStudio7.1\\NativeDE\\StepOver]
    "100"=".+CCom.*Ptr.+=NoStepInto"
    "99"=".+CComBSTR.+=NoStepInfo"
    "98"=".+CComVariant.+=NoStepInfo"
    "97"=".+CAtlMap.*=NoStepInfo"
    "96"=".+CAutoPtr.+=NoStepInto"
    

    Os valores são avaliados em ordem inversa. Essa configuração pode ser modificada sem reiniciar o VS.NET, parece que ela é recarregada a cada sessão de debug. Para maiores informações sobre esse assunto, dê uma olhada nessa mensagem de alguém da equipe do C#, que mandou um CTRL+C, CTRL+V da única documentação que existe sobre esse recurso: o código fonte da IDE do Visual Studio.NET.

    Em 31/03/2005 04:56 - Comentários (4)

    Nova mudança de emprego

    Depois de pensar e ponderar bastante, resolvi aceitar a proposta de emprego que recebi no começo do mês: uma oportunidade para trabalhar como Arquiteto de Sistemas, em um grande banco brasileiro (não posso revelar o nome ainda). Meu trabalho será integrar e melhorar a arquitetura dos diversos sistemas COBOL existentes na empresa, além de "mentoring" para os novos desenvolvedores. A intenção do banco é montar uma equipe com uma nova geração de desenvolvedores e arquitetos COBOL, todos antenados nas novas tecnologias e sabendo usar como ninguém os novos paradigmas e best practices na área de desenvolvimento de sistemas.

    Além da renumeração muito boa, a oportunidade me interessou principalmente pelo grau de desafio: trazer novas tecnologias (como generic programming, DataSets e WebServices) para o mundo COBOL, revitalizando o mainframe como parte essencial na lógica de negócios da empresa. Isso incluirá a construção de alguns drivers para mainframes (escritos em COBOL), coisa inédita no Brasil e que ajudará bastante a enriquecer o meu curriculum. Outra coisa que interessou muito é que eu terei total autonomia para fazer pesquisas e testes nessa área, já que eles acreditam que isso pode gerar muitos dividendos para banco, tanto financeiros como na parte de eficiência no atendimento e nas operações.

    Pretendo contar minhas experiências nessa nova empreitada aqui no blog, o que talvez signifique uma redução na quantidade de posts com os assuntos mais comuns (C++ e mercado de software). Mas acredito que os novos assuntos agregarão muito mais informação a todos que lêem esse blog, já que provarão que qualquer tecnologia existente pode se adaptar aos paradigmas modernos.

    Em 01/04/2005 19:15 - Comentários (2)

    Mais libs C++, agora vindas do Google e da Adobe

    O Google e a Adobe resolveram disponibilizar algumas de suas libs C++ sob licenças open source. As duas libs são compatíveis com STL, o que faz com elas possam ser facilmente integradas com a maioria das aplicações C++.

    O Google disponibilizou projetos em diversas áreas, incluindo medição de performance e debug. Mas o que realmente me interessou foram os hash maps que eles fizeram. Eles são especializações (no sentido de funcionamento, não de polimorfismo) do std::map, e têm as mesmas assinaturas. Os mapas foram criados para terem vantagens em campos específicos quando comparados com o std::map. O sparse_hash_map, por exemplo, é um pouco mais lento do que o std::map, mas consome 1/3 de memória. O dense_hash_map consome 10% mais memória do que o std::map, mas é entre 5 e 10 vezes mais rápido! E como as funções são as mesmas do std::map, é fácil trocá-lo pelos mapas especializados de acordo com a necessidade, um typedef deve resolver. Dê uma olhada na página que compara a performance dos mapas, é muito interessante.

    Já a Adobe disponibilizou uma biblioteca C++ razoávelmente grande. Podemos encontrar desde um mini framework para relacionamento entre entidades (descrição muito vaga, para mais detalhes veja a página do projeto) até alguns containers STL muito úteis ou interessantes (como um container forest). Tem também um template que encapsula variáveis de diversos tipos, o adobe::value_t - que é milhares de vezes mais seguro que um void pointer e "mais C++" do que um VARIANT. Com ele é possível ter um std::list que contém ao mesmo tempo strings, inteiros e tipos definidos pelo usuário.

    Em 04/04/2005 21:51 - Comentários (0)

    C++ com sanidade: usando ATL em aplicações, parte 2

    A ATL - como eu já disse antes - cobre diversas áreas da programação Win32, não somente COM. Continuando com a minha série "C++ com sanidade", vou mostrar aqui o acesso ao registro usando ATL. Esse pequeno programa mostra um MessageBox com os HotFixes que estão instalados, lendo essas informações do registro.

    #define UNICODE
    #define _UNICODE
    
    #include <atlbase.h>
    #include <atlstr.h>
    
    int main()
    {
       LONG l;
    
       ATL::CRegKey regKey;
       ATL::CString strKey, strBuffer;
       DWORD dwLen;
       static const wchar_t* wzRegPath = 
             L"SOFTWARE\Microsoft\Windows NT\CurrentVersion\HotFix";
    
       l = regKey.Open(HKEY_LOCAL_MACHINE, wzRegPath, KEY_READ);
       
       if(l != ERROR_SUCCESS)
          return HRESULT_FROM_WIN32(l);
    
    
       strBuffer = L"HotFixes instalados:\r\n\r\n";
    
       for(DWORD dwCurrentKey = 0 ; ; ++dwCurrentKey)
    
       {
          ATL::CRegKey regSubKey;
          ATL::CString strFixDescription, str;
          
          dwLen = MAX_PATH;
          l = regKey.EnumKey(dwCurrentKey, strKey.GetBuffer(dwLen), &dwLen);
    
          strKey.ReleaseBuffer();
    
          if(l != ERROR_SUCCESS)
             break;
    
          //
          // agora que pegamos o nome da chave, vamos pegar o valor "Fix Description"
          //
          str.Format(L"%s%s", wzRegPath, strKey.GetString());
    
          l = regSubKey.Open(HKEY_LOCAL_MACHINE, 
                             str,
                             KEY_READ);
    
          //
          // se o Windows falou que existe, tem que existir. A não ser que o usuário
          // seja muito rápido e consiga apagar a chave no meio da enumeração :-)
          //
          ATLASSERT(l == ERROR_SUCCESS);
          if(l != ERROR_SUCCESS)
             continue;
    
          //
          // primeiro vamos saber qual o tamanho da string
          //
          dwLen = 0;
          l = regSubKey.QueryStringValue(L"Fix Description", NULL, &dwLen);
    
          //
          // se não tem "fix description", vamos ignorar
          //
          if(l != ERROR_SUCCESS && l != ERROR_MORE_DATA)
             continue;
    
          l = regSubKey.QueryStringValue(L"Fix Description", 
                                         strFixDescription.GetBuffer(dwLen), 
                                         &dwLen);
          strFixDescription.ReleaseBuffer();
    
          if(l != ERROR_SUCCESS)
             continue; // xi... vamos ignorar e passar para o próximo
    
          strBuffer.AppendFormat(L"%s - "%s"\r\n", 
                                 strKey, 
                                 strFixDescription.GetString());      
       }
    
       MessageBox(NULL, strBuffer, L"Windows HotFixes", MB_ICONINFORMATION);
    }
    

    Para compilar esse código no Visual C++, use um projeto "Win32 Console Project", e não um "ATL Project". Você pode usar as classes da ATL em qualquer programa, é só adicionar os headers (no caso do ATL::CRegKey, só o atlbase.h é suficiente).

    Não se esqueça que minha intenção não é fazer um tutorial sobre as classes da ATL, e sim, mostrar que essas classes existem e dar exemplos práticos de como elas podem facilitar a sua vida, ajudando-o a escrever um código mais claro e menos sujeito à erros. Para mais informações, RTFM

    Em 06/04/2005 03:57 - Comentários (1)

    Tinha um bug no caminho, no caminho tinha um bug

    O código C++ abaixo contém diversos bugs, e se comporta de forma estranha durante a execução (faça o teste em DEBUG). Quais são os bugs e por que eles acontecem?

    //
    // coloque esses include's no stdafx.h, se preferir
    //
    #include <iostream>
    #include <vector>
    
    //
    // não consigo fugir do meu vício de programação Windows...
    //
    #ifndef DWORD
    #define DWORD unsigned int
    #endif
    
    //
    // vamos usar uma classe para facilitar a mudança
    //
    class CTest1
    {
    public:
    	DWORD dwValue;
    	std::string strValue;
    };
    
    class CTest2
    {
    private:
    	CTest1* m_pTest1;
    public:
    	CTest2(DWORD dw, const std::string& str)
    	{
    		m_pTest1 = new CTest1();
    
    		m_pTest1->dwValue = dw;
    		m_pTest1->strValue = str;
    
    	}
    	~CTest2()
    	{
    		delete m_pTest1;
    	}
    
    	CTest1* GetTest1() const
    	{
    		return m_pTest1;
    	}
    
    };
    
    int main()
    {
    	std::vector vecTest2;
    
    	//
    	// vamos colocar um CTest2 no vetor, passando
    	// os valores para o construtor
    	//
    	vecTest2.push_back(CTest2(100, "1bit"));
    
    	//
    	// agora vamos pegá-lo e ver os valores
    	//
            std::cout << "dwValue = "  << vecTest2[0].GetTest1()->dwValue  << std::endl;
            std::cout << "strValue = " << vecTest2[0].GetTest1()->strValue << std::endl;
    
    	return 0;
    }
    

    No próximo post eu explico o bug. E em posts posteriores vou sugerir algumas formas de resolver o problema (sendo uma delas usando ATL, é claro!).

    Em 06/04/2005 15:17 - Comentários (0)

    Eu não acredito...

    pra acreditar? Será que nunca mais precisaremos responder (e perguntar) nos fóruns e listas de discussão? :-)

    Em 08/04/2005 04:00 - Comentários (1)

    Por que o bug?

    Nós tínhamos um bug, que acontecia quando inseríamos (push_back) um objeto de uma determinada classe dentro de um container STL. Quando líamos o valor da variável, ela não correspondia ao valor do objeto inserido no container, e a runtime do C++ gerava um assert dizendo que estávamos chamando delete para um objeto mais de uma vez.

    O problema nesse caso, foi causado por algo que o C++ não costuma fazer: um código gerado pelo compilador, algo que não foi você que fez. Nesse caso, o copy constructor. O copy constructor é um construtor especial, que é chamado quando um objeto é copiado. Isso acontece quando você atribui um objeto a outro, retorna um objeto de uma função, ou passa um objeto para uma função como valor. Um trecho de código vale mais que (pi^10) palavras:

    class X
    {
    public:
      int i;
    };
    
    X func(X obj)
    {
      X localx;
    
      // mais uma cópia
      localx = obj;
    
      localx.i = obj.i;
    
      // oh, estamos copiando novamente!
      return localx;
    }
    
    int main()
    {
      X x1, x2;
     
      x2.i = 10; 
    
      // copiando...
      x1 = func(x2);
    
      return 0;
    }
    

    Na função "func" do exemplo acima, existem 3 operações de cópia: uma quando passamos x2 como parâmetro, outra quando atribuimos o parâmetro obj a localx, e outra na hora de retornar localx. Nessas situações, o copy constructor é chamado para copiar o objeto em questão. Como no nosso exemplo não temos um copy constructor definido, o compilador gera um automaticamente. Olhe como fica a nossa classe X com um copy constructor, equivalente ao que é gerado pelo compilador:

    class X
    {
    public:
      //
      // se definirmos um copy constructor, o compilador não gerará mais
      // o construtor default. Então vamos fazê-lo
      //
      X()
      {}
    
      //
      // copy constructor, que tem a sintaxe [tipo(const tipo& param)]
      // esse copy constructor é equivalente ao gerado pelo compilador
      //
      X(const x& v)
      {
        i = v.i;
      }
      int i;
    };
    

    Para nossa classe X, o copy constructor não gera problemas. Agora, vamos ver o copy constructor equivalente ao gerado pelo compilador para nossa classe com bug:

    class CTest2
    {
    private:
      CTest1* m_pTest1;
    public:
       ...  
    
      //
      // copy constructor equivalente ao gerado pelo compilador
      //
      CTest2(const CTest2& v)
      {
        m_pTest1 = v.m_pTest1;
      }
    
      ...
    
      ~CTest2()
      {
        delete m_pTest1;
      }
    };
    

    Note que m_pTest1 é a única variável membro de CTest2. Então a única coisa que é feita é copiar o valor dessa variável (que é um ponteiro). Note que - isso é importante - o construtor não é rodado no caso de cópia de objeto. Sendo assim, o objeto cópia não terá um ponteiro alocado com new, mas sim, a cópia do ponteiro do objeto do qual ele foi copiado. Assim, tentaremos chamar delete para o mesmo ponteiro, mas nas duas instâncias de CTest2 - o que gera o assert que falei.

    Se você está se perguntando onde é feita a cópia no código do exemplo do bug, repare que eu criei um objeto temporário diretamente ao invés de criar um objeto:
      //
      // criamos um objeto temporário do tipo CTest2, chamando
      // o construtor para inicializá-lo
      //
      vecTest2.push_back(CTest2(100, "1bit"));
    

    O construtor do nosso objeto temporário é executado logo antes da chamada da função (push_back), e o destrutor é chamado logo após o retorno da função. A função push_back espera uma referência para o objeto (const CTest2&), então não é feita a cópia durante a passagem de parâmetros. Mas o objeto é copiado ao ser inserido no vector<>, o que faz com que o copy constructor gerado seja chamado, e copie o valor do ponteiro.

    Uma das possíveis soluções é criarmos um copy constructor para nossa classe com bug, fazendo com que um novo objeto CTest1 seja criado durante a cópia. Assim, cada classe pode chamar delete para o seu ponteiro. Nossa classe ficaria assim:

    class CTest2
    {
    private:
      CTest1* m_pTest1;
    public:
       ...  
    
      CTest2(const CTest2& v)
      {
        m_pTest1 = new CTest1();
    
    
        //
        // por falar em copy constructor, essa instrução chamará o copy constructor
        // da classe CTest1, copiando todos os membros
        //
        *m_pTest1 = v.m_pTest1;
      }
    
      ...
    
      ~CTest2()
      {
        delete m_pTest1;
      }
    };
    

    Nossa solução é eficaz nesse caso. Mas e se precisássemos que as duas cópias usassem o mesmo ponteiro? Aguarde os próximos posts.

    Em 09/04/2005 17:43 - Comentários (4)

    Mais explicações sobre copy constructors

    Aquele post sobre copy constructors gerou para mim uma dúvida existencial: por que o compilador os gera automaticamente? Não é lá muito comum que os compiladores C++ gerem código.

    Depois procurar no FAQ do Stroustrup e não encontrar, resolvi perguntar aos gurus do comp.lang.c++.moderated. Se eles não soubessem, nem o próprio Stroustrup saberia, e eu continuaria até o fim dos meus dias com o peso da dúvida sobre os ombros. As respostas foram muito interessantes.

    Em 11/04/2005 21:35 - Comentários (3)

    Alguém pode me explicar o que é isso nas estatísticas do meu site?
     Operating SystemsHitsPercent
    Windows880994 %
    Unknown3473.7 %
    Linux1721.8 %
    CPM220.2 %
    Macintosh210.2 %

    CP/M???? Alguém anda usando CP-500 para acessar meu site?? :-)

    Em 12/04/2005 00:49 - Comentários (8)

    Essa estatística é mais do que compreensível
     BrowsersGrabberHitsPercent
    MS Internet ExplorerNo701374.6 %
    FirefoxNo137214.6 %
    Unknown?5385.7 %
    MozillaNo1861.9 %
    NetscapeNo1341.4 %
    OperaNo1111.1 %
    KonquerorNo210.2 %
    SafariNo210.2 %

    Eu achei que essa grande quantidade de acessos usando Firefox era isolada, mas essa porcentagem vem crescendo a cada mês. Eu sei que meu site é direcionado para um público mais técnico, mas mesmo assim fico feliz. Sou usuário do Firefox desde a versão 0.1, quando ele ainda se chamava Phoenix.

    Mesmo os microsoftianos mais fanáticos que se recusam a largar seu browser fraquinho devem agradecer muito à equipe do Firefox. Foram eles que conseguiram fazer a Microsoft se mexer e ressuscitar a equipe do IE (mesmo que a nova equipe tenha uns caras de pau).

    Em 13/04/2005 03:45 - Comentários (0)

    Por que um programador explica teoria musical melhor do que um músico?

    Eu já estudei teoria musical (com algum esforço eu leio uma partitura) e toco violão, guitarra e bateria. Já li várias coisas sobre musicalidade e teoria musical, mas nada tão claro e bem explicado quanto a explicação do Eric Lippert.

    (Eric Lippert é um programador da Microsoft que já apareceu em vários vídeos do Channel9 e trabalhou na equipe do VBScript)Por que um programador explica teoria musical melhor do que um músico? Ou será que eu entendo melhor por ser um programador, e pelo fato dele explicar música como um programador?

    Em 13/04/2005 21:52 - Comentários (0)

    Adobe compra a Macromedia

    Adobe compra a Macromedia por US$ 3,4 bilhões.

    Traduzindo: A empresa líder no ramo de software para mídia tradicional (Acrobat [Reader], PhotoShop, Illustrator, etc) compra a empresa líder no ramo de software para mídia online (Flash, Shockwave, DreamWeaver, Fireworks). Resta saber o que será feito com os softwares que "fazem as mesmas coisas", como GoLive e Fireworks, Illustrator e FreeHand.

    Em 18/04/2005 18:55 - Comentários (3)

    Trabalhando como PJ ou como CLT

    Eu trabalho atualmente como PJ (pessoa jurídica, emitindo nota fiscal), mas já trabalhei como CLT. A maioria das pessoas costuma discutir as vantagens e desvantagens das duas modalidades, muitas vezes sem saber a real implicação de cada uma. Vou mostrar alguns fatos sobre as duas modalidades, mas nada que esgote todos os detalhes do assunto.

    PJ

    • Custo para empresa: somente o custo da nota fiscal e mais algum benefício que ela resolver dar. Eu conheço uma empresa que contrata como PJ, mas oferece férias remuneradas (você emite uma nota mas não trabalha) e 13º (você emite uma nota com o dobro do valor em dezembro). Eu tenho férias remuneradas, mas não tenho 13º, e isso muda de empresa para empresa. A maioria das consultorias (cuidado com elas) não oferece nada.
    • Custos para o "funcionário": O custo é de 13,33% por nota fiscal emitida (IRRF (4,80%), COFINS (3%), PIS (0,65%), CSLL (2,88%), ISS para empresa de software em São Paulo (2%)) mais o custo do contador (entre R$ 100,00 e R$ 260,00 por mês), para uma empresa LTDA. Alguns impostos são recolhidos mensalmente (como o ISS) e outros trimestralmente. Uma parte dos impostos trimestrais são retidos pela empresa pagadora de acordo com o valor da nota, senão você mesmo paga. Resumindo: é uma bagunça generalizada. Não vou começar um discurso criticando esse modelo tributário estúpido, remendado e sem sentido porque eu prometi para mim mesmo que não falaria de política no blog. Arrume um bom contador (de preferência por indicação) que resolva isso para você.
    • Salário: Geralmente maior do que quando se é CLT. Uma parte do que a empresa (note que eu disse uma parte) economiza com encargos é repassado ao salário. Varia entre 20% e 100% maior do que CLT.
    • Benefícios: Quase nada. Ao invés de ter o governo como um pai, dizendo o que você deve fazer com o que você ganha, você recebe tudo em dinheiro. Fica por sua conta fazer uma previdência privada, pagar plano de saúde, pagar sua alimentação, etc. Fazer uma previdência privada e ter dinheiro guardado são duas coisas muito importantes para quem trabalha como PJ. Se você não sabe administrar seu dinheiro, talvez seja melhor ser CLT e deixar o governo cuidar dele. Afinal, o governo é especialista em cuidar do nosso dinheiro (e pegar uma boa parte dele).
    • Garantias: Você tem as garantias que o contrato de prestação de serviço te dá. Não tem garantias da CLT como seguro desemprego, multas rescisórias, indenizações, aviso prévio etc. Geralmente é algo muito mais simples do que a mão pesada da CLT: "eu trabalho, você me paga, e só". Você pode ter desde nenhuma garantia, até 3 meses de notificação para cancelar o contrato. Se no contrato não diz nada sobre tempo de notificação para rescisão, a empresa pode te demitir hoje, dizendo que amanhã você não precisa mais aparecer. Mas, da mesma forma, você pode arrumar um emprego melhor e dizer que você pode começar amanhã (na prática costuma-se dar uma ou duas semanas de notificação). É recomendado pedir ajuda de um advogado para interpretar e ajustar o contrato.
    • Liberdades: Assim como as garantias, depende do contrato, mas geralmente são maiores do que o regime CLT - tanto para o "funcionário" quanto para o contratante. Você também tem a liberdade de ter a previdência e o plano de saúde que você quer. No caso da previdência, além das previdências privadas serem bem mais baratas, você pode pagar para ter um benefício maior do que o limite de R$ 2400,00 do INSS. Nesse regime a empresa tem facilidade para contratar e facilidade para demitir. Esse é um dos motivos pelos quais os profissionais desatualizados (e sindicalistas) não gostam do regime PJ, é mais fácil ser demitido por incompetência ou incapacidade. Mas também é muito mais fácil sair da empresa, pois não é preciso se preocupar com todas as burocracias da CLT (aviso prévio, homologação, exame médico, etc). Em um mercado dinâmico como o nosso eu acho isso importante. Conheço programadores que fecham projetos de 3 a 6 meses, a burocracia disso pela CLT seria um inferno. Como um PJ ganha por hora trabalhada, não existe o desconto do descanso remunerado. Quando você falta, deixa de receber somente aquelas horas, que podem ser compensadas outro dia.

    CLT

    • Custo para empresa: Entre 130% e 230% do valor do seu salário. Isso mesmo, até 230% do seu salário! Por isso que as empresas relutam em contratar, porque o custo é excessivamente alto e a burocracia também. Como a burocracia é alta, a empresa também tem um alto custo administrativo.
    • Custos para o funcionário: Eu fiz uma simplificação da fórmula para calcular o salário líquido para quem ganhar mais que o limite do IR (2.326,00 e paga 27,5% de IR). A fórmula é (0,725 * salário bruto) + 238,00, e ela dá uma diferença de até R$ 10,00 para mais ou para menos. Mas é ótima para dar uma idéia do valor do salário e suficientemente simples para ser decorada. Pelo modo normal você tem que calcular INSS, limite de INSS, IR, etc. Note que isso não conta o IR que for restituído na declaração e que, além os descontos previstos nessa fórmula, a empresa pode descontar vale-transporte, vale-refeição e o plano de saúde.
    • Salário: Geralmente menor do que quando se é PJ, já que a empresa gasta bastante dinheiro com impostos e benefícios. Além do salário em dinheiro, esses benefícios devem ser levados em conta na hora de calcular o salário.
    • Benefícios: Geralmente muitos, a empresa passa a ser praticamente um pai. Você é obrigado a pagar previdência (o que seria bom se o custo/benefício da previdência do governo não fosse ruim), e muitas vezes recebe vale transporte, vale refeição, plano de saúde, etc. Apesar de ser aparentemente muito bom para o funcionário, isso aumenta bastante o seu custo e não permite o remanejamento desse dinheiro. Por exemplo, um funcionário que vai almoçar em casa não pode receber vale refeição em dinheiro, e um funcionário que já tem plano de saúde porque está como dependente do pai também não pode receber o valor do plano de saúde da empresa em dinheiro.
    • Garantias: Um mês de aviso prévio antes de ser demitido, a empresa é obrigada a pagar 15 dias do seu salário em caso de acidente (o resto é por conta da previdência). Você tem direito ao FGTS (que pode ser usado para compra de um imóvel), seguro desemprego, licença saúde e licença maternidade. Esses seguros são úteis para quem consegue pagar suas contas com o limite do benefício do INSS (R$ 2400,00). A garantia de emprego por ser CLT é uma ilusão, a empresa pode demitir da mesma forma. Como a burocracia e os custos para demissão são altos, a empresa evita contratar porque ela sabe que quando precisar demitir precisará passar por tudo isso.
    • Liberdades: Você tem o direito de trabalhar :-).

    Note que não existe um regime melhor do que o outro, tudo depende do caso e da situação. Existem empresas que contratam como CLT e você recebe 17 salários por ano. O salário de um PJ é quase sempre maior do que um CLT, e você tem mais liberdade. É necessário fazer as contas e ver o que vale mais a pena.

    Eu prefiro trabalhar como PJ porque acho importante ter liberdade, e prefiro pagar meu próprio plano de saúde e pagar minha própria previdência. Esse regime está longe do ideal, mas ainda acho o menos pior. Mesmo assim, se eu recebesse uma proposta CLT que fosse muito boa eu aceitaria. Tudo depende das condições e do mercado. Essa é a minha opinião pessoal, e não vale para todas a pessoas. Lembre-se que eu falei que alguém que trabalha como PJ deve ser razoavelmente organizado, e pagar sua própria previdência e plano de saúde. A CLT tem garantias muito importantes (exames médicos e seguros para o caso de doença), mas a tamanha burocracia dificulta muito as contratações. Chegamos a um ponto em que, apesar do imposto para PJ ser alto, ele ainda é menor do que o da CLT. Esse é o retrato do absurdo: é mais barato ter uma empresa do que ser um funcionário.

    Não se esqueça que eu sou só um programador. Confirme esses dados com seu advogado ou contador antes de tomar alguma decisão.

    Em 19/04/2005 20:53 - Comentários (74)

    Resolvendo o bug usando um smart pointer feito em casa

    Lembra do bug? Solucionei o problema criando um copy constructor (construtor chamado quando um objeto é inicializado com o valor de outro) para a nossa classe CTest2, de forma que em caso de cópia um novo objeto fosse criado. Isso resolveu o nosso problema para essa classe específica.

    Minha segunda solução é criar um smart pointer (template que se comporta exatamente com o ponteiro original) que faça a cópia. Assim, não é mais necessário implementar o copy constructor só porque a classe tem um ponteiro como membro. Se o objeto apontado por esse ponteiro deve ter o tempo de vida igual ao do objeto que o contém (o que é o nosso caso), isso funciona como um luva.

    Nosso smart pointer além de gerenciar cópia, ainda desaloca o objeto automaticamente. Sim, a mesma coisa que muitas runtimes fazem ao custo do seu programa ficar muito mais lento... Bom, vamos ao código, porque ele sempre vale mais do que 6378 palavras:

    #include <iostream>
    #include <vector>
    #include <string>
    
    #ifndef DWORD
    #define DWORD unsigned int
    #endif
    
    //
    // nosso template para copiar o objeto apontado
    //
    template <typename T>
    class CopyPointer
    {
    public:
       T* p;
    
       CopyPointer()
       {
          p = NULL;
       }
    
       ~CopyPointer()
       {
          Free();
       }
    
       void Alloc()
       {
          Free();
          p = new T();
       }
    
       void Free()
       {
          if(p)
          {
             delete p;
             p = NULL;
          }
       }
    
       void Attach(T* pT)
       {
          if(pT == p)
             return;
    
          Free();
          p = pT;
       }
    
       T* Detach()
       {
          T* tempp = p;
          p = NULL;
    
          return tempp;
       }
    
       //
       // copy constructor. Quando for copiado dessa forma,
       // vamos copiar o objeto ao invés de copiar o ponteiro
       //
       CopyPointer(const CopyPointer<T>& c)
       {
          //
          // isso vai chamar o copy constructor de T
          //
          p = new T(*c.p);
       }
    
       //
       // isso permite que haja cópia a partir de um ponteiro raw, não
       // só pelo nosso smart pointer
       //
       CopyPointer(const T* pT)
       {
          p = new T(*pT);
       }
    
       CopyPointer& operator=(T* pT)
       {
          if(p == pT)
             return *this;
    
          if(p)
             *p = *pT;
          else
             p = new T(*pT);
    
          return *this;
       }
    
       CopyPointer& operator=(const CopyPointer<T>& pT)
       {
          if(p == pT.p)
             return *this;
    
          if(p)
             *p = *pT.p;
          else
             p = new T(*pT.p);
    
          return *this;
       }
    
       operator T*() const
       {
          return p;
       }
    
       T* operator->() const
       {
          return p;
       }
    
       bool operator!() const
       {
          return (p == NULL);
       }
    
       bool operator<(T* pT) const
       {
          return p < pT;
       }
    
       bool operator==(T* pT) const
       {
          return p == pT;
       }
    };
    
    
    class CTest1
    {
    public:
       CTest1() : dwValue(0)
       {}
    
       DWORD dwValue;
       std::string strValue;
    };
    
    
    
    class CTest2
    
    {
    private:
       CopyPointer<CTest1> m_pTest1;
    public:
    
       CTest2()
       {
          m_pTest1.Alloc();
       }
       
       //
       // Não preciso mais de um destrutor para desalocar o ponteiro.
       // E nem precisei usar um linguagem mais limitada ou uma runtime lenta
       //
    
       CTest2(DWORD dw, const std::string& str)
       {
          m_pTest1.Alloc();
    
          m_pTest1->dwValue = dw;
          m_pTest1->strValue = str;
       }
    
       CTest1* GetTest1() const
       {
          return m_pTest1;
       }
    
    };
    
    int main(int argc, char* argv[])
    {
       std::vector<CTest2> vecTest2;
       
       CTest2 t1, t2;
    
       t1.GetTest1()->dwValue = 50;
       t1.GetTest1()->strValue = "baba";
    
       //
       // chamando o copy constructor
       //
       CTest2 t3 = t1;
    
       //
       // chamando operator=
       //
       t2 = t3;
    
       
       //
       // vamos colocar um CTest2 no vetor. Isso faz uma cópia
       //
       vecTest2.push_back(CTest2(100, "1bit"));
    
       //
       // agora vamos pegá-lo e ver o seu valor
       //
       std::cout << "dwValue = "  << vecTest2[0].GetTest1()->dwValue  << std::endl;
       std::cout << "strValue = " << vecTest2[0].GetTest1()->strValue << std::endl;
    
    
       return 0;
    }
    

    Só não tenho certeza do "const correctness" do meu smart pointer. Alguém se habilita?

    Em 02/05/2005 00:54 - Comentários (1)

    Trabalhamos no ambiente correto?

    Minha profissão é "Desenvolvedor de Software", "Analista Programador" - ou programador mesmo, no coloquial. Sim, no coloquial, porque minha profissão envolve muito mais do que programar um computador. Meu trabalho envolve fazer reuniões para definição de projetos, reuniões para resolver problemas em servidores, procurar soluções para os mais diversos problemas e estudar sobre tecnologias que podem ser usadas para resolver esses diversos problemas. Além disso, envolve transformar todos os meus estudos e conhecimentos em linguagens e tecnologias em uma solução. Dessas várias atribuições que eu falei, só as reuniões envolvem várias pessoas interagindo e dialogando. Todas envolvem pensar e resolver problemas. Ou seja: a grande maioria do meu trabalho envolve raciocínio, e conseqüentemente, concentração.

    Em todas as bibliotecas (de livros, não de software) que eu entrei até hoje, era proibido conversar e fazer barulho. Tudo isso porque os freqüentadores estavam lá para ler, estudar ou resolver problemas. Não é exatamente isso que eu faço no dia-a-dia? E por que a biblioteca é silenciosa e todos os lugares que eu trabalhei até hoje eram mais barulhentos do que um galinheiro? TODOS os lugares que eu trabalhei eram barulhentos, alguns mais, outros menos. Já trabalhei em empresas de software, provedores de Internet, bancos, bolsas de valores. Além disso já fiz entrevista em várias empresas e pude ver como era o ambiente. A coisa mais evoluída que eu vi foi separar os desenvolvedores do pessoal de suporte telefônico. Mesmo assim, todos os desenvolvedores ficavam amontoados na mesma sala. Em algumas empresas, a mesa de reuniões era NA MESMA SALA do desenvolvimento. Se houvesse uma reunião do seu lado, paciência. Minha única solução sempre foi a que a maioria usa: fones de ouvido e música.

    Será que nossa mesa minúscula (muitas vezes compartilhada) com um computador é o melhor lugar para fazer o que nós fazemos? Por que em Universidades conceituadas (não sei como é nas UNICACAs) cada professor tem sua sala? Qual a diferença de necessidade de concentração entre um professor da USP e um desenvolvedor tentando fazer debug de um algoritmo de load balance? As empresas geralmente organizam as mesas dos desenvolvedores da mesma forma que organizam as mesas dos auxiliares administrativos. E o gerente que solta a pérola "Nosso software deve ser portado para Linux nem que isso leve 15 dias" tem uma sala exclusiva. Os gerentes - que geralmente são os que menos trabalham - são os que geralmente têm sala exclusivas. Será que isso está certo?

    Quem assiste o Channel9 da Microsoft ou lê sobre as condições de trabalho lá deve ter visto que eles dão UMA SALA PARA CADA DESENVOLVEDOR. Será que esse é um dos motivos pelos quais eles são a Microsoft e as outras empresas de software não passam de outras empresas de software? Tudo que eu queria era uma sala só para mim, com um sofá de 3 lugares para poder deitar e pensar (ou até dormir um pouco...). É muito difícil pensar na reengenharia de um cluster sentado em uma cadeira desconfortável e com uma mesa minúscula. Uma lousa e um pouco de silêncio seriam muito bem vindos.

    Bom, enquanto eu não abro minha empresa de software, continuo com meu fone companheiro...

    Em 05/05/2005 03:01 - Comentários (13)

    WinDbg Live Programming: Fazendo um log das chamadas à MessageBox

    Nesse exemplo, nós faremos uma espécie de log de todas as chamadas para MessageBox que um processo fizer. Toda vez que um MessageBox for chamado, a string passada será passada também para OutputDebugString. Dessa forma, é possível usar o DebugView da Sysinternals para salvar isso em um arquivo (ou somente ver no WinDbg mesmo).

    Para fazer isso, vamos criar uma "função" em tempo real (em assembly), e redirecionar o fluxo do programa para ela toda vez que o MessageBoxExW for chamado. No Windows NT (NT/2000/XP/2003), a função MessageBox nada mais faz do que chamar MessageBoxEx, que é quem realmente faz o trabalho. Além disso, quando chamamos MessageBoxA (quando você compila seu programa em ANSI, MessageBox é um define para MessageBoxA), ela só "traduz" sua string para UNICODE e repasa para versão W. Sendo assim, fazendo nosso hook em MessageBoxExW vamos pegar todas as variantes.

    Chega de teoria e vamos à escovação de bits:

    A primeira coisa que precisamos fazer é alocar memória para a nossa "função". Isso pode ser feito usando o comando ".dvalloc" do WinDbg, que aloca memória no espaço de endereçamento do processo. Vamos alocar 1kb de memória:

    0:000> .dvalloc 1000
    Allocated 1000 bytes starting at 00230000
    

    Agora temos a nossa memória. Vamos salvar o endereço dela no pseudo-registrador $t0 do WinDbg. O WinDbg possui 20 pseudo-registradores ($t0 até $t19) para usos como esse. Para isso, usaremos o comando "r", que altera o valor de um registrador:

    0:000> r $t0 = 0x00230000
    

    Como as strings enviadas para o MessageBox não necessariamente são terminadas com CRLF, vamos colocar um na nossa memória para podermos dar uma quebra de linha na string que enviaremos ao OutputDebugString. Fazemos isso usando o comando "ezu" para gravar uma string UNICODE na nossa memória (cujo endereço está em $t0). Depois de fazer isso, vamos usar o comando "db" para ver a memória, pasando L0xF no final para dizer que só queremos ver 0xF bytes:

    0:000> ezu $t0 "\r\n"
    
    0:000> db $t0 L0xF
    00230000  0d 00 0a 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
    

    Agora que já temos nosso "ENTER" na memória, vamos à parte que interessa: escrever o código. Vamos escrever o código logo depois da string que acabamos de gravar. Como a string é UNICODE, seu tamanho em bytes é 6 (dois caracteres UNICODE mais o 00 00 no final). Vamos gravar o início do nosso código no pseudo registrador $t1 e escrevê-lo para chamar o OutputDebugString, passando o ponteiro da string que veio em EAX como parâmetro. Aqui vamos usar o comando "A" para começar a escrever o código assembly e depois usaremos o comando "U" para verificar se o disassembly ficou como queríamos:

    0:000> r $t1 = $t0 + 0x6
    
    0:000> a $t1
    00230006 push @eax
    push @eax
    00230007 call kernel32!OutputDebugStringW
    call kernel32!OutputDebugStringW
    0023000c push 0x00230000
    push 0x00230000
    00230011 call kernel32!OutputDebugStringW
    
    call kernel32!OutputDebugStringW
    00230016 int 3
    int 3
    00230017 
    
    0:000> u $t1
    00230006 50               push    eax
    00230007 e8aaf54f79       call    KERNEL32!OutputDebugStringW (7972f5b6)
    0023000c 6800002300       push    0x230000
    00230011 e8a0f54f79       call    KERNEL32!OutputDebugStringW (7972f5b6)
    00230016 cc               int     3
    00230017 0000             add     [eax],al
    00230019 0000             add     [eax],al
    0023001b 0000             add     [eax],al
    

    Duas coisas devem ser notadas no trecho acima. A primeira é que vamos chamar o OutputDebugString duas vezes, uma para string recebida em EAX e outra para o nosso CRLF. O ideal seria concatenar isso em um novo buffer, mas para uma aplicação single threaded isso funciona sem problemas. A segunda coisa é que coloquei um "int 3" no final do código. Isso é um breakpoint, e caso o processador passe do meu código (eu vou redirecionar isso antes com um BP), ele vai parar no breakpoint forçado e eu posso resolver o problema. O código depois do "int 3" deve ser ignorado, é o disassembly de "00 00 00 00".

    Nosso próximo passo é fazer o WinDbg redicionar o fluxo do programa para a nossa função toda vez que o MessageBoxExW for chamado. Para isso vamos usar o suporte que o BP (comando para breakpoint) nos dá para executar comandos toda vez que um breakpoint for atingido. Vou mostrar o comando primeiro e explicar depois:

    0:000> bp user32!MessageBoxExW "r $t2 = @eip ; r eax = poi(@esp+8) ; r eip = $t1 ; g"
    

    Os comandos na string depois do BP serão executados assim que o breakpoint for atingido. Note que são vários comandos separados por ";". Vamos à explicação:

    • r $t2 = @eip: Vamos salvar o ponteiro de instrução do processador (EIP) em $t2. Tem um "@" antes para dizer ao WinDbg para interpretar isso como um registrador, e não tentar procurar symbols para isso;
    • r eax = poi(@esp+8): Esse é fácil :-) Vamos colocar o apontado do segundo parâmetro da pilha no registrador EAX. Quando chegamos numa função, o primero parâmetro está em ESP+0x4, o segundo em ESP+0x8, etc. Dessa forma estaremos colocando em EAX o ponteiro para a string que foi passada no segundo parâmetro da MessageBoxEx;
    • r eip = $t1: Aqui vamos colocar o endereço da nossa "função" em EIP. Isso faz com que o processador comece a executar as instruções que estão na nossa "função".
    • g: Hey, Ho, Let's Go!

    Resumindo: Quando chegarmos em MessageBoxExW, vamos colocar a string em EAX, salvar a instrução atual em $t2 e mandar o processador para a nossa "função".

    Agora que já fizemos o redirecionamento para nossa "função", precisamos fazer com que o processador volte para a MessageBoxExW depois de executar nosso código, fazendo com que o programa siga seu fluxo normalmente. Para isso, vamos usar a mesma técnica que usamos no primeiro breakpoint, modificar o EIP:

    0:000> bp $t0 + 0x16 "r eip = $t2 ; g"
    

    Se você chamar um "U $t0 + 0x16", vai ver que esse é o endereço do nosso "int 3", que vem logo depois do nosso código. Aqui nós estamos colocando um breakpoint que será atigindo logo depois da segunda chamada à OutputDebugString. A única coisa que temos que fazer nesse momento é restaurar o EIP usando o conteúdo antigo dele que salvamos em $t2 e deixar o programa seguir seu curso. Com isso teremos o nosso log.

    Pronto, isso deve funcionar. Abra no WinDbg qualquer programa que chame um MessageBox, siga os passos acima, e veja as mensagens mostradas no prompt do WinDbg. Teste também a visualização com o DebugView.

    Isso tudo funcionou porque OutputDebugString é __stdcall (ou seja, a função chamada que restaura a pilha). Se a função fosse __cdecl (quem chamou restaura a pilha), o nosso assembly ficaria um pouco maior. A única API Win32 que eu sei que é __cdecl é a wsprintf, porque ela recebe parâmetros dinamicamente.

    Segue o log completo dos comandos usados para isso:

    0:000> .dvalloc 1000
    Allocated 1000 bytes starting at 00230000
    
    0:000> r $t0 = 0x00230000
    
    0:000> ezu $t0 "rn"
    
    0:000> db $t0 L0xF
    00230000  0d 00 0a 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
    
    0:000> r $t1 = $t0 + 0x6
    
    0:000> a $t1
    00230006 push @eax
    push @eax
    00230007 call kernel32!OutputDebugStringW
    call kernel32!OutputDebugStringW
    0023000c push 0x00230000
    push 0x00230000
    00230011 call kernel32!OutputDebugStringW
    call kernel32!OutputDebugStringW
    00230016 int 3
    int 3
    00230017 
    
    0:000> u $t1
    00230006 50               push    eax
    00230007 e8aaf54f79       call    KERNEL32!OutputDebugStringW (7972f5b6)
    0023000c 6800002300       push    0x230000
    00230011 e8a0f54f79       call    KERNEL32!OutputDebugStringW (7972f5b6)
    00230016 cc               int     3
    00230017 0000             add     [eax],al
    00230019 0000             add     [eax],al
    0023001b 0000             add     [eax],al
    
    0:000> db 0x230000
    00230000  0d 00 0a 00 00 00 50 e8-aa f5 4f 79 68 00 00 23  ......P...Oyh..#
    00230010  00 e8 a0 f5 4f 79 00 00-00 00 00 00 00 00 00 00  ....Oy..........
    00230020  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
    00230030  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
    00230040  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
    00230050  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
    00230060  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
    00230070  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
    
    0:000> bp user32!MessageBoxExW "r $t2 = @eip ; r eax = poi(@esp+8) ; r eip = $t1 ; g"
    
    0:000> bp $t0 + 0x16 "r eip = $t2 ; g"
    

    E aqui vai o fonte do programa simples que eu fiz para testar:

    #define WIN32_LEAN_AND_MEAN
    #define UNICODE
    #define _UNICODE
    
    #include <windows.h>
    
    
    int APIENTRY WinMain(HINSTANCE hInstance,
                         HINSTANCE hPrevInstance,
                         LPSTR     lpCmdLine,
                         int       nCmdShow)
    {
     	wchar_t wzBuffer[128];
    	DWORD dwInterval = 1000, dwCount = 10;
    
    
    	for(DWORD a = 0 ; a < dwCount ; a++)
    	{
    		wsprintf(wzBuffer, L"mensagem %d", a);
    
    		MessageBox(NULL, wzBuffer, L"LOG", MB_OK);
    		Sleep(dwInterval);
    
    	}
    
    	return 0;
    }
    

    Para maiores informações veja o help do WinDbg (RTFM). Boa escovação de bits!

    Em 06/05/2005 16:42 - Comentários (2)

    Série sobre programação de Drivers no CodeProject

    Um programador de drivers com boa alma escreveu uma série de artigos sobre o assunto no Code Project. Aos interessados:

    Driver Development Part 1: Introduction to Drivers
    Driver Development Part 2: Introduction to Implementing IOCTLs
    Driver Development Part 3: Introduction to driver contexts
    Driver Development Part 4: Introduction to device stacks
    Driver Development Part 5: Introduction to the Transport Device Interface

    Destaque para a parte 5, que explica como fazer um cliente TDI. TDI (Transport Device Interface) é a interface usada por drivers para fazer comunicação via rede (equivalente ao WinSock, só que em kernel mode). Eu já fiz um filtro de TDI, e descobri da pior forma que TDI é complicado e tedioso. Não bastasse isso, a explicação da MSDN deixa bem a desejar...

    Em 10/05/2005 17:46 - Comentários (1)

    Os códigos que eu tenho que aguentar

    Em todas as empresas que eu já trabalhei, tive que dar manutenção em diversos sistemas, muitas vezes feitos por pessoas que nem trabalham mais na empresa. Eu já passei infinitas horas tentando entender o que foi feito, quase sempre em um código confuso e com nenhum comentário. Agora que eu estou trabalhando com otimização de performance em componentes já existentes, minha situação piorou. Eu preciso antes entender COMPLETAMENTE o que o componente faz, ao invés de entender somente a parte que eu preciso modificar.

    Meu amigo Gilberto já passou por mais empresas do que eu, porque ele aceitava [se sujeitava a] pegar projetos com 4 meses de duração. Depois de algumas discussões sobre os diversos estilos de código que encontramos durante nossa longa jornada (isso deveria ter uma música dos anos 60 como trilha sonora...), ele me apareceu no dia seguinte com essa pérola que vos mostro, entitulada "Os códigos que eu tenho que agüentar".

    Nos exemplos abaixo, programadores com diversos perfis (prático, metódico, enigmático, etc) se propõem a criar uma função que retorne o resultado de (2 + 2). Cada estilo de programador faz de uma forma diferente, como vemos abaixo:

    //
    // Prático
    //
    int soma()
    {
           return 2 + 2;
    }
    
    //
    // Enigimatico
    //
    int soma()
    {
           int a = 1;
           a = a << 1;
           a = (++a)++;
           return a;
    }
    
    //
    // Metódico
    //
    int soma3()
    {
           int a;
           int b;
           int c;
           a = 2;
           b = 2;
           c = a + b;
           return c;
    }
    
    //
    // Aprendeu orientação a objetos ontem
    //
    class cVal
    {
    private:
           int valorInterno;
    public:
           int GetValue()
           {
                   return valorInterno;
           };
           void SetValue(int i)
           {
                   valorInterno = i;
           };
           int Soma(cVal CV)
           {
                   return this->GetValue() + CV.GetValue();
           };
    };
    
    int soma()
    {
    
           cVal a,b;
           a.SetValue(2);
           b.SetValue(2);
           return a.Soma(b);
    }
    
    //
    
    // Macaco velho
    //
    int soma()
    {
           return 4;
    }
    
    //
    // Experiente
    //
    static const int Soma2_2 = 4; //2 + 2 = 4
    
    
    //
    // STL maníaco
    //
    #include <vector>
    #include <functional>
    #include <numeric>
    
    
    template<typename T>
    int supersoma(T p1, T p2)
    {
     typedef T _MyType;
     return std::plus<_MyType>()(p1,p2);
    }
    
    template<typename T>
    int supersoma2(T p1, T p2)
    {
     std::vector<T> v;
    
     v.reserve(2);
     v.push_back(p1);
     v.push_back(p2);
     return std::accumulate(v.begin(), v.end(), 0, std::plus<T>());
    }
    
    template<typename T, bool b>
    int supersoma3(T p1, T p2)
    {
    	return b ? supersoma<T>(p1,p2) : supersoma2<T>(p1,p2);
    }
    
    template<typename T, bool b>
    class SuperSoma4
    {
     public:
      int operator()(T p1, T p2)
      {
        return supersoma3<T,b>(p1,p2);
      }
    
    };
    
    static const bool qual_somal = false;
    
    int soma()
    {
     // Stroustrup e Alexandrescu sejam louvados
     return SuperSoma4<int, false>()(2,2);
    }

    Eu dei minha contribuição, com o exemplo "STL Maníaco". Se você conhece algum perfil que eu não mostrei, sinta-se à vontade para colocar um exemplo nos comentários.

    Em 11/05/2005 19:43 - Comentários (10)

    Alguém viu as especificações do XBOX360?

    É de babar, e de pensar quando eu vou ter um micro com esse poder no meu desktop (ou quantas semanas o pessoal do Linux vai demorar para portar e hackear). Olhem só alguns detalhes da máquina:

    Xbox 360 System Performance Specifications

    Custom IBM PowerPC-based CPU
    • Three symmetrical cores running at 3.2 GHz each
    • Two hardware threads per core; six hardware threads total
    • VMX-128 vector unit per core; three total
    • 128 VMX-128 registers per hardware thread
    • 1 MB L2 cache
    Memory
    • 512 MB of 700 MHz GDDR3 RAM
    • Unified memory architecture

    Algo me diz que todos os projetos de computação distribuída (inclusive o meu se eu tiver tempo) serão portados para o XBOX360, já que os videogames são mais baratos do que os computadores, e nesse caso específico mais poderoso que um PC comum. Isso também prova que os computadores poderiam ser mais baratos, já que um computador com essas especifícações custa muito mais que um videogame (eu duvido que esse XBOX vá custar mais do que US$ 399,99).

    Além disso, teremos um videogame multicore e com suporte a 6 threads simultâneas, o que fará que os desenvolvedores de jogos comecem a explorar o uso de múltiplas threads, o que não é feito hoje. Assim, como os jogos para XBOX são feitos em DirectX (C++ e COM) e "quase compatíveis" com um PC, os jogos feitos para aproveitar o poder do XBOX também aproveitarão o poder dos PCs com múltiplos processadores e com multicores/hyperthreading, o que acaba por melhorar a qualidade dos jogos para PC também. Atualmente ter dois processadores não melhora o desempenho da maioria dos jogos, já que eles são single threaded.

    Para mais detalhes, veja a especificação completa liberada pela Microsoft. O último videogame que eu tive foi um SuperNES, a muuuuito tempo atrás. Acho que esse eu vou ser obrigado a comprar...

    Em 13/05/2005 13:08 - Comentários (8)

    Problemas no carro? Instale o último Service Pack

    Nos Estados Unidos, a nova moda são os carros híbridos gasolina-eletricidade. Além de não ser necessário ligá-los na tomada (as baterias são carregadas com a energia cinética dos freios e pelo próprio motor a gasolina), eles economizam bastante gasolina e são potentes, já que o motor elétrico é ligado para "dar uma força" quando o carro exige mais potência.

    Mas esse mês os proprietários do modelo Toyota Prius tiveram um estranho problema: o motor era desligado quando o carro estava em alta velocidade. Em condições normais de uso, os motores são ligados e desligados dependendo da situação: em baixas velocidades, só o motor elétrico é usado, e quando o carro está parado o motor a gasolina chega a ser desligado. Quando a velocidade aumenta, o motor a gasolina assume a responsabilidade. E tudo isso é controlado por um software, que a Toyota também licenciou para as outras montadoras (li isso na Wired do mês passado). O problema do desligamento do motor em alta velocidade é devido a um bug nesse software, o que fez a fabricante convocar os seus donos para atualizarem o software, em uma das autorizadas. Ou seja: levar o carro à uma oficina para o mecânico instalar um patch, um service pack.

    Não bastasse os carros mais modernos estarem usando um computador de bordo (sujeitos a vírus), temos mais essa. E aquela piada de programador que fala que para o carro funcionar é só desligar e dar partida novamente está virando realidade...

    Em 17/05/2005 04:27 - Comentários (1)

    Não sou o único na contra mão do MsMarketing

    Pérola que eu li em um newsgroup um dia desses: "Não sei como, nos dias de hoje, ainda existem pessoas que escrevem uma nova aplicação Windows usando Win32 ou MFC ao invés de usar Windows Forms". Como eu sei que existe uma imensa massa de programadores que definitivamente não sabem o que falam, eu resolvi não discutir. Já ouvi tantas pessoas que acreditam que qualquer coisa pode ser feita em .NET, que hoje em dia eu prefiro não discutir e continuar com o meu Visual C++.

    O primeiro problema disso é a definição de conceito e o entendimento da arquitetura das coisas. Falar que não usar Windows Forms não faz sentido é completamente sem sentido, já que o Windows Forms não passa de um wrapper sobre a Win32 API. Além disso, a MFC não passa de outro wrapper para a Win32 API, só que com mais de 10 anos de mercado e MUITO mais maduro do que o Windows Forms (que ainda tem vários bugs). Eu sempre achei o Windows Forms aquém das minhas expectativas para desenvolvimento de aplicativos Win32, mas acredito que a escolha da ferramenta sempre deve ser feita com calma e pesando todos os fatores, como experiência, tempo para desenvolvimento e alcance do aplicativo. É inviável fazer um aplicativo para download em .NET, já que só a runtime dá uns 25 MBs. (Não me venha com esse papo de que isso está melhorando, que mais pessoas tem o Framework instalado, etc. A grande maioria da base instalada não tem Framework e nem sabe o que é isso).

    Semana passada encontrei mais algumas pessoas que compartilham da minha opinião. A primeira é a equipe que desenvolveu o newsreader Sauce. Ele foi totalmente desenvolvido em .NET, como muitos dos leitores RSS que existem hoje. Depois de não conseguir fazer o aplicativo ficar mais rápido e consumir menos memória, eles decidiram abandonar o .NET e refazer em "old unmanaged code". A nova versão ainda está em beta, mas é bem mais rápida do que a versão .NET e consome metade da memória. Fiz algumas medições entre a versão .NET e nativa (feita em C++ Builder, Delphi ou Kylix), e como dados concretos valem mais do que c palavras, vamos a eles:

    Tempo de inicialização
    .NETNativo
    366
    252
    121
    165
    Consumo de memória (Private Bytes)
    .NETNativo
    45.492 KB24.144 KB

    O argumento que todos os .NETers sacam assim que são "atacados" é que a aplicação usada não foi bem escrita, que não segue os best practices de alocação de memória, não usa corretamente IDisposable, bla, bla, bla. Se é tão difícil assim fazer uma aplicação com boa performance e consumo de memória razoável em .NET, é melhor continuar usando C++. Pelo menos em C++ a regra de alocação/desalocação de memória é clara, e não depende de saber quem precisa ou não de Dispose(). Além disso, tem o argumento barato de que as máquinas vão ficar mais rápidas, que a memória vai ficar barata, que o Bush vai recobrar a sanidade algum dia e que em um futuro não muito distante todos os micros terão Framework instalado. Eu desenvolvo aplicativos hoje, para clientes de hoje, que têm máquinas de hoje. Eu acho que o Windows Forms vai ficar muito melhor no futuro (Avalon), mas não posso viver lidando com coisas em fase experimental e SOs que ainda estão em alpha. Quem já tem experiência no mercado sabe que a migração para o Longhorn - ou a instalação da runtime do Avalon em todas as máquinas - deve demorar alguns anos.

    Outra pessoa que compartilha da minha opinião chama-se Mark Russinovich. Para quem vive em outro mundo e não o conhece, ele além de escrever os programas do site SysInternals é o escritor do livro Windows Internals, a bíblia sagrada da arquitetura Windows. Ele escreveu um post sobre isso, e como esperado, foi impiedosamente atacado pelo cardume de Piranhas.NET. Devido ao grande número de comentários negativos, ele explicou novamente o que já tinha explicado e as Piranhas.NET não tinham lido. Compartilho da mesma opinião: .NET é bom para componentes e maravilhoso para Web (ASP.NET é muito bom). Mas Windows Forms aumenta muito o consumo de CPU e memória sem trazer grandes vantagens (traz até alguns problemas). E wrapper por wrapper eu prefiro meu WTL. :-)

    Em 23/05/2005 19:31 - Comentários (25)

    Tem gente que leva isso a sério demais...

    Em 26/05/2005 00:38 - Comentários (4)

    O outro lado da moeda

    Um usuário Linux fez uma avaliação do Windows XP Home, e chegou a conclusão que ele está quase bom para o uso em desktops. Apesar de ter sido colocado na sessão de humor do site, todas as críticas e considerações que ele faz têm fundamento, e mostra que em alguns pontos o Linux com KDE ou Gnome é bem melhor do que o Windows. Eu achei interessante pelo fato da comparação estar considerando como base o Linux, e não o Windows. E o melhor de tudo: o "avaliador" soube elogiar os pontos fortes do Windows, sem simplesmente falar mal como faz a "banda podre" do Linux.

    Em 28/05/2005 20:43 - Comentários (10)

    Explicando a sopa de letrinhas da programação C/C++ para Windows: Win32

    Outros posts dessa mesma série:   ATL     COM     MFC

    Win32 API: API (Application Programming Interface) das versões do Windows que são 32 bits (95, 98, 98SE, Millenium, NT, 2000, XP, 2003, Longhorn, etc). Nada mais é do que as funções que o sistema operacional exporta para serem usadas pelas aplicações. Cada sistema operacional tem a sua API, e a runtime do C e C++ é implementada em cada plataforma usando essa API. Por exemplo, o malloc da linguagem C acaba chamando HeapAlloc ou VirtualAlloc da API. Para mais detalhes, veja a documentação na MSDN. Código fonte sempre vale mais do que h palavras:

    #define UNICODE
    #define _UNICODE
    #define WIN32
    #define _WINDOWS
    #define WIN32_LEAN_AND_MEAN
    
    #include <windows.h>
    
    //
    // programa Win32 tem como entry point (função inicial) a função WinMain
    //
    int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,int nCmdShow)
    {
      WCHAR* wzBuffer;
      HANDLE hHeap;
      
      hHeap = GetProcessHeap();
    
      //
      // usando a função HeapAlloc da API para alocar memória
      //
      wzBuffer = (WCHAR*) HeapAlloc(hHeap, HEAP_ZERO_MEMORY, 256);
    
      //
      // wsprintf também é da API
      //
      wsprintf(wzBuffer, L"TickCount: %d", GetTickCount());
    
      MessageBox(NULL, wzBuffer, L"Mensagem", MB_OK);
    
      //
      // se eu usasse ATL eu não precisava fazer isso...
      //
      HeapFree(hHeap, NULL, wzBuffer);
    
      return 0;
    }
    

    Para mais informações sobre Win32, veja o meu post "FAQ: Programação Win32 em C/C++"

    Se você não tiver o Visual C++, você pode fazer download do Microsoft Visual C++ Toolkit Compiler e do Microsoft Platform SDK. A compilação em linha de comando fica assim:

    C:Temp>cl win.cpp /link user32.lib
    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86
    Copyright (C) Microsoft Corp 1984-1998. All rights reserved.
    
    win.cpp
    Microsoft (R) Incremental Linker Version 6.00.8447
    Copyright (C) Microsoft Corp 1992-1998. All rights reserved.
    
    /out:win.exe
    user32.lib
    win.obj
    

    Depois ainda teremos COM, ATL, WTL e MFC. Alguém tem mais alguma sugestão?

    Em 31/05/2005 15:10 - Comentários (10)

    Mega-super-ultra liquidação de livros importados na Pearson

    Interessado em comprar um livro de programação importado e pagar uma mixaria? Esse fim de semana (4 e 5 de junho de 2005) vai haver uma liquidação de livros importados na Pearson (ex Makron), com livros que já estão encalhados faz algum tempo. E o bom é que nas duas listas dos encalhados (aqui e aqui) estão, entre outros, os clássicos The Design and Evolution of C++, Essential COM e o Effective STL. Não vou entrar no mérito do motivo que faz livros bons como esses estarem encalhados em uma editora, o importante é ter a oportunidade de comprar essas pérolas a preço de banana. Essa liquidação começa sábado, dia 4 de junho, as 10 da manhã, e eu pretendo estar na porta da editora 9:30.

    Estou escrevendo esse post propositalmente no início da madrugada de sábado, para que haja menos probabilidade de eu ter que sair no tapa com alguém por um único exemplar do "Design and Evolution of C++". Eu sou pregador da disseminação e multiplicação do conhecimento, mas quando eu acho clássicos como esses por 20 reais, eu não resisto e ligo o "evit bit" :-) Além disso, seria interessante saber se alguém lê meu blog aos fins de semana, ou se o acesso fora do horário comercial é feito somente por pessoas que conhecem minha fama internacional como especialista em placenta baixa...

    [Atualização (05/06): Foi uma porcaria. Eu marquei uns 20 livros da lista, só consegui comprar o "Essencial COM". O pessoal da editora aceitou encomendas de pessoas de outros estados e venderam muitos livros ANTES de começar o feirão. Eu já fiz minha reclamação... Ah, e eu descobri que existem pessoas que lêem o blog (leia-se "abrem o RSS reader") nos fins de semana. :-)]

    Em 04/06/2005 05:00 - Comentários (4)

    C++ com sanidade: resolvendo o bug usando Boost

    Seguindo a série de possíveis correções para o bug da cópia de um objeto que contém um ponteiro, sugiro agora a correção que eu acho mais fácil e apropriada: usar o shared_ptr do Boost. Essa proposta segue a filosofia C++: fazer tudo com o máximo de simplicidade e reusando as bibliotecas existentes ao máximo.

    Para quem não conhece, o Boost é um conjunto de bibliotecas (a maioria de templates) para C++, que é vista pelos usuários como um extensão da biblioteca padrão (namespace std). Essas bibliotecas vão de smart pointers à regular expression, de algoritmos para manipulação de strings à templates metaprogramming. Algumas das bibliotecas do Boost (acho que 10) serão adicionadas ao próximo padrão C++, nomeado provisóriamente como C++0x (0x será o ano de publicação, o último padrão é o C++98).

    Como todos já sabem, um trecho de código sempre vale mais do que 100.000.000.000 palavras:

    #include <iostream>
    #include <boostshared_ptr.hpp>
    
    //
    // costumes Win32...
    //
    #ifndef DWORD
    #define DWORD unsigned int
    #endif
    
    
    class CTest1
    {
    public:
       CTest1() : dwValue(0)
       {}
    
       ~CTest1()
       {
          // só para termos certeza do que está acontecendo...
          std::cout << "CTest1::~CTest1() | " << (void*)this << std::endl;
       }
    
       DWORD dwValue;
       std::string strValue;
    };
    
    
    
    class CTest2
    {
    private:
       //
       // transformando o ponteiro em um shared_ptr resolve o bug
       //
       boost::shared_ptr<CTest1> m_pTest1;
    public:
    
       CTest2() 
          : m_pTest1(new CTest1)
       {}
    
    
       //
       // Não preciso um destrutor para desalocar o ponteiro.
       // E nem precisei usar um linguagem mais limitada ou uma runtime lenta
       //
    
       CTest2(DWORD dw, const std::string& str)
          : m_pTest1(new CTest1)
       {
          m_pTest1->dwValue = dw;
          m_pTest1->strValue = str;
       }
    
       ~CTest2()
       {
          std::cout << "CTest2::~CTest2() | " << (void*)this << std::endl;
       }
    
       //
       // essa versão é a mais recomendada
       //
       const boost::shared_ptr<CTest1>& GetTest1()
       {
          return m_pTest1;
       }
    
       //
       // vamos renomear. Eu poderia passar o ponteiro diretamente,
       // mas prefiro seguir as regras de ownership do shared_ptr
       // Isso chama-se "safe programming", mais do que possível em C++
       //
       CTest1* GetTest1_old() const
       {
          return m_pTest1.get();
       }
    
    };
    
    int main(int argc, char* argv[])
    {
       std::vector<boost::shared_ptr<CTest2> > vecTest2;
    
       boost::shared_ptr<CTest2> t1(new CTest2), t2(new CTest2(100,"putz grila"));
    
       //
       // agora nossas variáveis são ponteiros
       //
       t1->GetTest1()->dwValue = 50;
       t1->GetTest1()->strValue = "baba";
    
       //
       // vamos criar um objeto e fazer uma cópia 
       // do CONTEÚDO de um dos ponteiros
       //
       CTest2 tv3 = *t1;
    
       //
       // chamando operator=, copiando o valor
       //
       *t2 = tv3;
    
       //
       // vamos atribuir um ponteiro ao outro.
       // Note que como usamos um shared_ptr, o conteúdo do ponteiro t1 será
       // automaticamente liberado, e não haverá leak
       // "olhe mamãe, sem o garbage collector"
       //
       t1 = t2;
       
       //
       // vamos colocar um t1 no vetor. Isso coloca o ponteiro,
       // e não faz cópia do objeto
       //
       vecTest2.push_back(t1);
    
       //
       // vamos colocar t2, que é o mesmo ponteiro de t1
       //
       vecTest2.push_back(t2);
    
       BOOST_ASSERT(t1 == t2);
       BOOST_ASSERT(t1.get() == t2.get());
       BOOST_ASSERT(vecTest2[0] == vecTest2[1]);
    
       //
       // agora vamos pegá-lo e ver o seu valor
       //
       std::cout << "dwValue = "  << vecTest2[0]->GetTest1()->dwValue  << std::endl;
       std::cout << "strValue = " << vecTest2[0]->GetTest1()->strValue << std::endl;
    
       return 0;
    }
    

    Outros post da série:
    Tinha um bug no caminho, no caminho tinha um bug
    Por que o bug?
    Resolvendo o bug usando um smart pointer feito em casa

    Em 06/06/2005 13:11 - Comentários (0)

    Troca-troca de arquitetura

    Primeiro a Microsoft troca a arquitetura do XBOX para o IBM PowerPC. Agora, durante uma apresentação no Apple's Worldwide Developer Conference, Steve Jobs anuncia que a próxima geração de Macs usará plataforma Intel. Sim, a Apple vai usar processadores Intel. Quando eu vi os boatos semana passada, achei que poderiam ser infundados, mas agora está confirmado. Além dessa grande mudança, Steve Jobs ainda anunciou que um software da Apple (chamado Rosetta) vai permitir que os programas compilados para PowerPC rodem no Intel sem maiores problemas. Como sempre, toda revolução vem acompanhado de algum software...

    Como esperado, não será possível usar oficialmente o MacOS X como qualquer PC "made in Santa Efigênia", pois isso seria um tiro na cultura Apple (Oficialmente não será possível, mas quantas semanas vai levar pra alguém resolver isso?). Eu li que algum lugar que a diferença da Apple para as outras empresas é que ela não vende hardware nem software, ela vende computadores. Ela vende algo que você tira da caixa e liga na tomada, como você faz com um eletrodoméstico. E isso faz realmente sentido, e ela prova que esse enfoque funciona. É mais fácil para os leigos de plantão colocarem um iMac para funcionar do que programar um vídeo cassete. Quando passar alguns anos eles lançam um micro com um design novo e todo mundo troca, igual fazemos com eletrodomésticos. A estratégia deles é muito interessante.

    Além desse fato, o pessoal da Debian anunciou que, depois de 3 anos de desenvolvimento, a versão Stable do Sarge (3.1) está finalmente pronta e disponível para download. Só falta agora a 3DRealms anunciar o lançamento do Duke Nukem Forever...

    Em 06/06/2005 22:08 - Comentários (8)

    Windows vendido a US$ 1

    A Microsoft acabou de fechar um acordo com o governo da Indonésia para que ele pague US$ 1,00 por cópia de Windows pirata instalado. Isso parece uma multa baixa, mas na realidade essas cópias passarão a ser legalizadas (não sujeitas à fiscalização de pirataria), o que transforma o acordo em uma venda de licenças de Windows a US$ 1,00. A única condição é que o governo começe a comprar software legalizado daqui pra frente. O governo brasileiro precisa descobrir o que fazer para conseguir esse preço também...

    Em 10/06/2005 13:16 - Comentários (0)

    Eu não visto a camisa da empresa

    Acho que todo mundo já deve ter escutado (ou até usado) a expressão "vestir a camisa da empresa". Isso geralmente é o que o seu gerente diz quando ele quer que você faça hora extra de graça no fim de semana. É uma maneira usada para te pressionar, como que dizendo que, já que você não se preocupa com a empresa, ela também não vai se preocupar com você. Mas tem um detalhe importante que ele não percebe: a sua empresa já não se preocupa com você.

    Eu comecei a trabalhar na área relativamente cedo. Com 15 anos eu era monitor da finada Data Control, e com 16 anos eu estava trabalhando de diagramador para um jornal de uma cidade pequena. Depois disso, já com 17 anos, eu comecei a fazer softwares e arrumar computadores. Hoje eu sei que isso foi muito bom para mim, pois eu já comecei a ser enganado desde cedo. Além de acreditar nas promessas que me faziam e nessa estória de "vestir a camisa da empresa", eu trabalhei muito de graça para pessoas que se aproveitavam da minha total falta de experiência com negociações. Hoje eu vejo que isso não tem nada a ver com a idade, porque eu conheço vários programadores com quase 30 anos que têm o mesmo problema. E vejo também profissionais de outras áreas também passarem pelo mesmo problema, inclusive o de trabalhar de graça quando esse não era o combinado.

    Uma das coisas que eu sempre digo é que salário não é, e nunca foi sinônimo de competência. O sujeito que ganha mais é aquele que melhor sabe negociar seu salário e que cede menos às pressões para que seu salário seja abaixado. Existem inúmeras pessoas que reclamam que "eu programo melhor do que ele e ele ganha mais do que eu". Infelizmente é exatamente assim que as coisas funcionam, porque as empresas só valorizam as pessoas que tem um bom conhecimento técnico quando elas mesmas começam a se valorizar, e não se rebaixam mais a trabalhar por salário baixo. Para muitas empresas não existe um salário para cada competência, existe o mínimo possível que a empresa consegue te pagar. Quem já fez entrevista ou trabalhou em várias consultorias (as grandes inclusive) sabe bem o que estou falando.

    Eu não visto a camisa de empresa nenhuma pelo simples fato de que nenhuma empresa até hoje vestiu a minha camisa. E olha que eu não peço muito, só que me deixem trabalhar em paz. É tão simples deixar um programador feliz... Dê-lhe uma máquina boa, pague-o em dia conforme o combinado, e o deixe trabalhar. Espero que algum dia as empresas percebam (com algumas grandes já perceberam) a quantidade de talentos e de produtividade que eles jogam fora por causa da forma que eles tratam os programadores (e funcionários em geral). Em duas empresas que eu trabalhei acontecia sempre a mesma coisa: de dois em dois anos pelo menos metade dos programadores pediam demissão. Profissionais muito bons, que saiam da empresa porque não tinham a mínima condição de trabalho. Será que os donos eram tão burros a ponto de não perceber a quantidade de dinheiro e potencial que eles estavam perdendo?

    A primeira coisa que eu pergunto para os funcionários de uma empresa quando eu entro: "Vão me deixar trabalhar? Ou eu vou ter que ficar lidando com briguinhas políticas ridículas e babaquices afins?". Sou, como eu disse, igual à grande maioria dos programadores, a única coisa que eu peço é poder trabalhar em paz (Como eu estou conseguindo fazer no meu emprego atual. Por isso que estou trabalhando aqui até hoje, mesmo sabendo que a APINFO está cheia de oportunidades.).

    Esse jeito de tratar profissionais tem um nome, cunhado pelo meu sábio amigo/sócio Gilberto: "Sindrome do super-administrador". Muitos administradores de empresa se acham mais competentes a medida que eles conseguem extrair o máximo de trabalho dos funcionários pagando o MSP ("menor salário possível"). Eu conheço várias empresas que, se pudessem, descontariam dos funcionários a CPMF retida no pagamento dos salários. Se você acha que eu estou exagerando, você provavelmente não tem muita experiência na área ou é muito sortudo. A boa notícia é que a medida que você troca de empresas e se depara com super-administradores, você aprende a lidar com eles e se esquivar dos truques mais comuns.

    PS: Antes que alguém pergunte, eu não sou comunista, sou um capitalista convicto e presto serviço para bolsa de valores com muito orgulho. Eu só acho que, mediante a tudo que eu escrevi, dá para entender porque as empresas de software brasileiras são pequenas e muitas vezes inexpressivas em nichos de mercado que tem potencial de crescimento de 20% ao ano...

    PS2: Alguém pode me dizer o que quer dizer esse "PS" que usamos nas observações?

    Em 13/06/2005 14:01 - Comentários (52)

    Acessando os newsgroups brasileiros da Microsoft de uma forma que funciona

    O antigo fórum da MSDN brasileira foi incorporado à infraestrutura de newsgroups da Microsoft Corp, e agora usa a mesma interface web, além de suportar o protocolo NNTP. Isso é muito bom, já que a responsabilidade de manter isso rodando deixa de ser da Microsoft brasileira, que é muito ocupada tentando empurrar .NET para as empresas a todo custo, e tem dificuldades até para manter um site no ar. O problema é que, além de muitas pessoas trabalharem em lugares onde o NNTP é bloqueado, a interface web do news da Microsoft é incompatível com Firefox (as palavras acentuadas ficam trocadas).

    A solução é usar o Google Groups. De lá você pode ler e responder os posts, receber as mensagens ou o resumo por e-mail (como em listas de dicussão), além de todas as facilidades de busca que o Google provê.

    Em 17/06/2005 21:20 - Comentários (6)

    Os contos que as empresas contam

    Eu já trabalhei em várias empresas e consultorias, e fiz entrevistas em tantas outras. Em algumas empresas que eu trabalhei me prometeram coisas que não cumpriram. Além disso, muitas das empresas onde fiz entrevista tentaram me fazer as mesmas propostas, que eu tinha certeza que não seriam cumpridas. Achei interessante mostrar aqui alguns desses "truques" usados pelas empresas para levar vantagem na negociação, e torço para que isso ajude alguns programadores/consultores a não passarem pelas mesmas situações que eu e alguns amigos passamos.

    Como um trecho de código vale mais do que... ops, sem código hoje. Vamos então aos truques mais conhecidos:

    O velho truque dos três meses: Esse é um dos truques mais velhos e conhecidos. Você pede um salário de R$ 3500,00 por mês. Aí, já quando tudo parece estar combinado e fechado, a pessoa que te contrata diz: "Então vamos fazer assim, eu te pago R$ 3000,00 nos primeiros três meses, enquanto a gente se conhece, e depois eu começo a te pagar os R$ 3500,00". E você, apesar de contrariado, aceita, já que quer mesmo esse emprego. Só que depois do terceiro mês, ao conferir seus rendimentos, você vê que o valor combinado (que eles chamam de aumento) não foi depositado, você recebeu só os mesmos R$ 3000,00. Você passa uma semana tentando encontrar alguém para reclamar, e quando acha, essa pessoa diz que foi um erro do pessoal da contabilidade e que isso não vai se repetir no próximo mês. Mas nesse mês as coisas ficarão desse jeito.

    Por ser detalhada, essa história pode parecer uma experiência pessoal minha, mas não é. Conheço algumas pessoas que tiveram exatamente o mesmo problema em empresas diferentes. Não caia no velho truque dos três meses, não aceite propostas para entrar por um salário mais baixo e aumentar depois. A empresa que propõe isso mostra não ser séria e você corre o grande risco de não receber a quantia que pediu. Seja duro nas negociações, não esqueça que a empresa precisa do seu trabalho. Você não está pedindo um emprego, a empresa está pedindo para você trabalhar para ela por causa das suas qualificações.

    "Oportunidade de cargo de liderança": Essa é a preferida das meninas de RH de consultoria. Esse é o argumento mais usado quando elas querem te colocar em uma barca furada, como trabalhar em um lugar que fica a 100 km da sua casa. Quando elas já esgotaram todos os argumentos, elas falam que "eu sei que é longe, mas e se existir uma possibilidade de você ser colocado em um cargo de liderança? Isso será muito bom para o seu curriculum". E quando você pergunta de quanto é essa possibilidade, ela te diz que a possibilidade existe, mas não pode garantir nada. Talvez isso seja verdade, e você vire o líder da área de meta-estatística tecnológica, que é a única área da empresa que só tem um funcionário - você! Olha que bom, você será líder de você mesmo, pelo menos você terá 100% de aprovação dos seus subordinados... :-)

    Esse truque nunca colou comigo, porque eu prefiro programar do que pegar um cargo de liderança em uma empresa que eu não conheço. E nenhuma empresa que presta coloca um programador sem experiência em liderança para liderar uma equipe. Mas eu já vi esse truque funcionar com várias pessoas que queriam experimentar o gostinho de mandar nos outros ao invés de programar alguma coisa. E já vi vários cargos serem inventados para que muitas pessoas fossem líderes ou gerentes de alguma coisa.

    Oportunidade de crescer junto com a empresa: Esse também é um argumento bastante usado para te pagar um salário baixo. O contratante te diz que a empresa está em franco crescimento, mas que não pode te pagar um salário bom agora. Mas ele diz que, se você aceitar esse salário ridículo, você terá a oportunidade única de acompanhar o crescimento da empresa e crescer junto com ela. Mas quando você pergunta de prazos para aumentos, ele repete que você "estará crescendo junto com a empresa". E ainda fala que o crescimento da empresa será proporcional ao seu esforço - que, é claro, nunca será suficiente. Pense bem: você apostaria no crescimento de uma empresa que você nem conhece? Se você acha que vale a pena, aceite a proposta, mas saiba bem os riscos que você está correndo. Eu não costumo aceitar esse tipo de coisa porque eu acho que, se a empresa quer crescer, ela deve investir. E um dos investimentos necessários é contratar profissionais bons e experientes, coisa que não custa barato. Não consigo acreditar no crescimento de uma empresa que faz de tudo para pagar MSP ("menor salário possível").

    Passei alguns dias tentando me lembrar de outros truques, mas não me lembrei de nenhum outro que fosse tão importante quanto esses (talvez o de contratar como estagiário, mas não acho que tenha tenta relevância para quem já está a algum tempo no mercado). Isso prova como o arsenal de truques das empresas é limitado, e como as pessoas caem sempre nas mesmas histórias. Já sabe: nada de três meses de experiência (você já tem vários), nada de cargo de liderança (você é um programador, lembra?) e nada de crescer junto com uma empresa que não quer investir em você. Valorize-se e fuja dos super-administradores.

    Em 21/06/2005 03:57 - Comentários (44)

    Google Maps: agora eu vejo minha casa!

    O Google Maps agora também tem fotos de satélite do Brasil! Apesar de não mostrar direções e endereços, ainda assim é divertido procurar pontos conhecidos no mapa do satélite. Você pode entrar em maps.google.com e digitar "São Paulo", que você vai cair em um ponto entre o Rio Tietê e o Centro da cidade. Depois é só arrastar o mapa com o mouse e ir ajustando o zoom na barra que aparece no canto superior esquerdo do mapa.

    Alguns pontos interessantes:

    Em 23/06/2005 19:11 - Comentários (47)

    Resolvendo o bug usando CComPtr

    Eu resolvi o bug de diversas maneiras até agora. Eu mudei o código para contorná-lo, criei um smart pointer feito em casa e usei o boost::shared_ptr (melhor solução até agora). Agora eu proponho mais do que uma solução, proponho um re-arquitetura: transformar todos os objetos em objetos COM. Isso resolve nosso problema, porque em COM todos os objetos são acessados através de ponteiros para interfaces, e as regras para gerenciamento de tempo de vida desses ponteiros são bem claras. Esses objetos, apesar de serem "objetos COM", não estão disponíveis para a runtime do COM como de costume, são todos objetos privados. Futuramente, poderemos facilmente exportar esses componentes em uma DLL, para que eles sejam acessíveis através de outras ferramentas que suportam COM, como VB, Delphi ou mesmo .NET.

    Nesse exemplo, eu segui as regras básicas do COM: todas as funcionalidades são acessíveis através de interfaces, todos os objetos herdam e implementam IUnknown e o tempo de vida de um objeto é gerenciado por chamadas a IUnknown->AddRef() e IUnknown->Release(). Para implementar IUnknown e gerenciar o tempo de vida dos objetos eu optei por usar ATL, por tornar a implementação bem mais simples.

    Vamos ao código, ele vale muito:

    #define UNICODE
    #define _UNICODE
    
    // "descomente" isso se usar um projeto do VC com "precompiled header"
    // #include "stdafx.h"
    
    
    //
    // só precisamos disso para usar ATL
    //
    #include <atlbase.h>
    
    ATL::CComModule _Module;
    
    #include <atlcom.h>
    
    
    //
    // isso evita erros na conversão de ponteiros COM
    // eu expliquei isso em http://www.1bit.com.br/content.1bit/weblog/cpp_comma_op
    //
    #define IC(pp) (static_cast<IUnknown*>(*pp),(void**)pp)
    
    //
    // Essa será nossa interface. Antes que você pergunte,
    // SIM, C++ TEM SUPORTE A INTERFACES
    // é só definir todos os métodos como virtual e colocar um "=0"
    // no final para dizer que não é implementado
    //
    struct __declspec(uuid("D0C2F56E-704E-45ce-B6F8-7E9D0F7F8723"))
    ITest1 : public IUnknown  // toda interface COM herda de IUnknown
    {
    	virtual HRESULT get_dw(DWORD* pdw) =0;
    	virtual HRESULT set_dw(DWORD dw) =0;
    	virtual HRESULT set_bstrValue(BSTR str) =0;
    	virtual HRESULT get_bstrValue(BSTR* pbstr) =0;
    };
    
    
    class CTest1 : 
    	public CComObjectRootEx<CComSingleThreadModel>, // implementação ATL de IUnknown
    	public ITest1
    {
    private:
    	DWORD m_dwValue;
    	//
    	// nossa string agora será um BSTR. Vamos usar CComBSTR para não
    	// nos preocuparmos com gerenciamento de memória
    	//
        CComBSTR m_bstrValue;
    	
    public:
    	
    	CTest1() : m_dwValue(0)
    	{}
    	
    	//
    	// Para fazer um objeto COM o ATL só precisa que você herde de
    	// CComObjectRootEx (ou similares) e coloque um mapa de interfaces
    	//
    	BEGIN_COM_MAP(CTest1)
    		COM_INTERFACE_ENTRY(ITest1)
    	END_COM_MAP()
    	
    	
    	//
    	//  implementação de ITest1
    	//
    	HRESULT get_dw(DWORD* pdw)
    	{
    		if(!pdw)
    			return E_POINTER;
    		
    		*pdw = m_dwValue;
    		
    		return S_OK;
    	}
    	
    	HRESULT set_dw(DWORD dw)
    	{
    		m_dwValue = dw;
    		return S_OK;
    	}
    	
    	HRESULT set_bstrValue(BSTR str)
    	{
    		m_bstrValue = str;
    		
    		return S_OK;
    	}
    	
    	HRESULT get_bstrValue(BSTR* pbstr)
    	{
    		if(!pbstr)
    			return E_POINTER;
    		
    		*pbstr = m_bstrValue.Copy();
    		
    		return S_OK;
    	}
    
    	//
    	// vamos facilitar nossa vida
    	//
    	typedef ATL::CComCreator<CComObject<CTest1> > Creator;
    };
    
    
    //
    // Interface ITest2
    //
    struct __declspec(uuid("7BA53C86-5B50-4b69-ACC4-652E60FE2FC9"))
    ITest2 : public IUnknown  
    {
    	virtual HRESULT Init(DWORD dw, BSTR str) =0;
    	virtual HRESULT GetTest1(ITest1** ppTest1) =0;
    };
    
    
    class CTest2 : 	
    	public CComObjectRootEx<CComSingleThreadModel>, // implementação ATL de IUnknown
    	public ITest2
    {
    private:
    	//
    	// vamos usar o CComPtr para não nos preocuparmos com
    	// gerenciamento de tempo de vida do objeto COM
    	//
    	CComPtr<ITest1> m_pTest1;
    public:
    
    	BEGIN_COM_MAP(CTest2)
    		COM_INTERFACE_ENTRY(ITest2)
    	END_COM_MAP()
    
       //
       // Não preciso um destrutor para desalocar o ponteiro.
       // E nem precisei usar um linguagem mais limitada ou uma runtime lenta
       //
       
       //
       // implementação de ITest2
       //
       
       HRESULT Init(DWORD dw, BSTR str)
       {
    	   HRESULT hr;
    
    	   hr = CTest1::Creator::CreateInstance(NULL, __uuidof(ITest1), IC(&m_pTest1));
    	   
    	   if(FAILED(hr))
    		   return hr;
    
    	   m_pTest1->set_dw(dw);
    	   m_pTest1->set_bstrValue(str);
    
    	   return S_OK;
       }
    
       HRESULT GetTest1(ITest1** ppTest1)
       {
    	   *ppTest1 = NULL;
    
    	   if(m_pTest1.p == NULL)
    		   return E_UNEXPECTED;
    
    	   return m_pTest1.CopyTo(ppTest1);
       }
    
       typedef ATL::CComCreator<CComObject<CTest2> > Creator;
    };
    
    int main(int argc, char* argv[])
    {
    	CComPtr<ITest1> pTest1;
    	CComPtr<ITest2> pTest2;
    	CComBSTR bstr;
    	DWORD dw;
    
    	CTest1::Creator::CreateInstance(NULL, __uuidof(ITest1), IC(&pTest1));
    	CTest2::Creator::CreateInstance(NULL, __uuidof(ITest2), IC(&pTest2));
    
    	bstr = L"Oi mamãe, eu sou uma string";
    	dw = 10;
    
    	pTest1->set_bstrValue(bstr);
    	pTest1->set_dw(dw);
    
    	pTest2->Init(dw, bstr);
    
    	//
    	// se você não fizer isso antes de reusar a variável para um parâmetro
    	// OUT, o CComPtr vai disparar um ASSERT, pq isso criaria um leak.
    	// Quando você atribui NULL à um CComPtr é o equivalente do VB 
    	// a "Set p = Nothing" (libera o objeto)
    	//
    	pTest1 = NULL;
    
    	pTest2->GetTest1(&pTest1);
    
    	//
    	// o mesmo problema com o CComBSTR. Precisamos liberá-lo antes de reusar
    	// para um parâmetro OUT. Ok, ainda tem que fazer algum gerenciamento de 
    	// memória manualmente.
    	//
    	bstr.Empty();
    
    	pTest1->get_bstrValue(&bstr);
    	pTest1->get_dw(&dw);
    	
    	return 0;
    }

    Para compilar esse projeto, não crie um projeto ATL no Visual C++, senão você terá uma DLL. Crie um projeto "Win32 Console Application", ou compile isso em linha de comando com o Visual C++ Toolkit.

    Em 27/06/2005 15:00 - Comentários (3)

    Onde está seu Garbage Collector agora?

    Você achava que usando .NET nunca mais teria problemas com leaks? Veja isso:

    GC incorrectly handles large freed objects

    All Windows Forms that leak two GDI handles

    NumericUpDown and DomainUpDown leak

    Massive GDI-Leak in ListView in Windows-Forms (1.1SP1)

    Em 30/06/2005 18:30 - Comentários (9)

    FAQ: Começando a programar em C++

    Todas as semanas eu recebo a mesma pergunta pelo formulário de contato: "Quero começar a programar em C++ mas não sei como". Eu não fico aborrecido com isso, pelo contrário, fico bastante feliz. É muito bom saber que uma quantidade considerável de pessoas quer evoluir e passar para o lado poderoso da força. :-)

    Eu pretendo montar, com vários posts, algo que possa ser agrupado em um FAQ para ajudar essas pessoas a se converterem. Quero que esse movimento se transforme numa conversão em massa dos programadores para C++, mostrando a luz às pessoas que estão limitadas dentro de uma vida onde não se pode gerenciar sua própria memória. E assim, mudar meu país para melhor!

    (Se você costuma ler o que eu escrevo já sabe que não pode me levar muito a sério. Mas, como diz o Alfred, o mais fácil é fazer como a Globo: explique tudo detalhadamente e não deixe nada nas entrelinhas, alguém pode não entender. Então, lá vai: Isso foi uma brincadeira.)

    Quero me converter para o lado poderoso da força, mas essa história de C e C++ me confunde. O que é o que?
    A linguagem C foi criada no início dos anos 70 para ser usada na programação do UNIX. Cansados de fazer tudo em Assembly, os programadores resolveram criar uma linguagem que fosse estruturada e que permitisse programação low-level ao mesmo tempo. Assim nasceu a linguagem C.

    /*
     Isso está em C
    */
    
    #include <stdio.h>
    
    int main()
    {
      int a = 15;
      int b = 20;
      char buffer[128]; /* uma string é um array (ou vetor) de bytes terminado por ASCII zero */
    
      strcpy(buffer, "A variável [a] é ");
    
      if(a > b)
        strcat(buffer, "maior");
      else
        strcat(buffer, "igual ou menor ");
    
      strcat(buffer, " que a variável [b]\r\n");
     
      printf(buffer);
    
      return 0;
    }
    

    O C++ é uma evolução da linguagem C, e foi criada por Bjarne Stroustrup. Nessa evolução foi adicionado à linguagem C o conceito de orientação à objetos, que virou moda naquela época (começo dos anos 80) e é muito usado hoje em dia. Além disso, o próprio C++ foi evoluindo no decorrer da década de 80 e 90, com a adição de recursos como templates e a STL (que veremos depois). O C++ tem tudo que a linguagem C tem (99,8% de compatibilidade) e mais toda a evolução. Todo compilador C++ que eu conheço compila código C sem problemas.

    //
    // Isso está em C++
    //
    
    #include <iostream>
    
    using namespace std;
    
    int main()
    {
      int a = 15;
      int b = 20;
      string buffer; // string é um objeto
    
      buffer = "A variável [a] é ";
    
      if(a > b)
        buffer += "maior";
      else
        buffer += "igual ou menor ";
    
      buffer += " que a variável [b]\r\n";
     
      cout << buffer;
    
      return 0;
    }

    O exemplo feito em C, é compilado sem problemas por qualquer compilador C++, já que ele é também um código C++ válido.

    Preciso aprender C antes de aprender C++?
    Não, não e não. Eu recomendo que você aprenda C++ direto, sem passar pelo C, já que hoje em dia o C++ é mais usado. Se algum dia você precisar fazer algo em C, é só estudar as limitações do C em relação ao C++. As estruturas básicas de controle (if, while, switch...) são as mesmas, o que muda é que a linguagem C não suporta todos os recursos do C++, com suporte à programação orientada a objetos, bibliotecas, templates, etc. Mesmo assim, em C++ você pode usar as bibliotecas do C sem problemas.

    Você pode me recomendar algum livro para aprender C++?
    O único livro para iniciantes que eu conheço e achei bom é o Beginning Visual C++ Programming. Se você é iniciante, qualquer material pode te ajudar, desde um livro de C até um tutorial de Internet. Depois que você já estiver com alguma prática em C++, leia o The C++ Programming Language do Stroustrup, que é o guia definitivo sobre o assunto, e que eu recomendo até para os programdores C++ experientes. Eu prentendo recomendar mais livros a medida que o FAQ for avançando.

    Todo mundo diz que C++ é muito complicado. Isso é verdade?
    Você é um homem ou um rato? C++ requer mais estudo por ser uma linguagem completa e poderosa, mas não é nada que um ser humano normal não consiga aprender. Tem gente que acha que usar o Microsoft Word é complicado. Esqueça o que os outros dizem e estude aquilo que você tem vontade.

    A moda agora é .NET e Java, será que vale a pena estudar C++?
    Vale. Como as pessoas estão indo para as linguagens mais fáceis, os profissionais de C++ são mais valorizados. Afinal, quando se precisa de algo 10 vezes mais rápido, alguém precisa fazer. Não se esqueça que praticamente todos os softwares comerciais que existem são feitos em C ou C++ (Windows, Office, SQL Server, Oracle, Photoshop, CorelDRAW, Linux, Visual Studio, o próprio .NET e todas as VMs Java, etc, etc, etc).

    Eu já ouvi falar que o Java e o C# são versões melhoradas do C++. Isso é verdade?
    Se você acha que uma versão mais limitada de alguma coisa é uma melhoria... O Java e o C# são baseadas na linguagem C++, tirando muitos recursos que, apesar de poderosos, causavam confusão ou dificuldade. Foi feita uma simplificação e um nivelamento por baixo (ok, pelo meio) para atender às necessidades mais comuns. Por serem mais simples, essas duas linguagens são indicadas para aplicativos tecnicamente mais simples, como os que manipulam bancos de dados, controlam regras de negócios e fazem entradas de dados. As duas runtimes (JVM e .NET) têm algumas vantagens sobre o C++, como gerenciamento automático de memória, independência relativa de arquitetura e mais facilidade para leitura dos metadados (enumerar as classes de um DLL, por exemplo). Mas têm a desvantagem de consumirem bem mais memória, serem mais lentas (isso pode melhorar com o tempo) e terem limitações técnicas que as impedem de desenvolver certos tipos de software (como device drivers).

    Onde posso achar mais informações sobre C++?

    Wikipedia - Linguagem C
    Wikipedia - C++
    Bjarne Stroustrup's C++ FAQ
    Bjarne Stroustrup's C++ Style and Technique FAQ
    Google: "C++ tutorial" (antes que alguém pergunte...)

    Em 02/07/2005 17:43 - Comentários (138)

    A evolução da informática em fotos e screenshots

    Um site muito bom para quem quer conhecer a história da informática é o GUIdebook. Capaz de arrancar suspiros saudosistas de qualquer um que trabalha na área a mais de 5 anos, ele conta a história da informática usando fotos e screenshot de aplicativos e sistemas operacionais.

    É interessante ver como as interfaces gráficas e a informática demorou para se popularizar no Brasil (talvez pela reserva de mercado...). Uma das partes que eu achei muito interessante é a que tem diversas fotos do Apple Lisa, que tinha uma interface gráfica já bem parecida com os PCs atuais. Até o Office é muito parecido:

    O detalhe mais importante é que, prestando atenção no splash do Lisa, você vê que ele foi lançado em 1983. Além disso, consultando a Wikipedia, vemos que ele rodava com um Motorola 68000 (o mesmo do Mega Drive) de 5 MHz e com 1 MB RAM. É engraçado pensar que a funcionalidade das interfaces gráficas não evoluiu tanto assim (só estão bem mais bonitas), e fazendo um comparativo com os requisitos mínimos para o Longhorn, podemos ver o que realmente evoluiu...

    Um dos recursos mais interessantes desse site é a linha do tempo dos sistemas operacionais, vale realmente perder um tempo explorando a evolução do Windows e das outras interfaces.

    Esse assunto me fez lembrar de alguns programas antigos que eu não encontrei lá. Alguém lembra do VGA Copy?

    Ah, alguém lembra como se chamava aquele programa que tocava arquivos MOD diretamente no speaker do PC, tinha um modo com três grandes luzes coloridas e que a tela fazia um scroll estranho?

    Em 05/07/2005 13:45 - Comentários (6)

    FAQ: Programação Win32 em C/C++

    Continuando o meu projeto de projeto de FAQ C++, vou falar hoje sobre Win32.

    O que é Win32 e para que serve?
    Já expliquei isso em outro post.

    Ouvi falar que Win32 é complicado, isso é verdade?
    Isso tem lá seu fundo de verdade, alguns assuntos são extremamente complicados, os mais usuais nem tanto. De qualquer forma, essa complicação tem motivo: como a Win32 API é o acesso mais baixo nível que se pode ter ao sistema (considerando user-mode), ela deve possibilitar que você a use para fazer tudo que é possível no Windows. Por isso, muitas vezes, você chama uma função de 8 parâmetros mais só passa 2. Esses outros 6 parâmetros são usados em situações não muito comuns, mas que devem ser cobertas pela API. Além disso, como essa API é C (e não C++), não é possível fazer overload das funções com versões mais simples para os usos mais simples, como é feito muitas vezes nas classes do .NET Framework e em bibliotecas C++.

    Vou exemplificar isso usando a famosa função CreateProcess como exemplo. Essa função pede 10 parâmetros, mas em 99% dos casos você só usa o LPTSTR lpCommandLine. Além dos parâmetros que você passa como NULL, existem alguns que você é obrigado a preencher, como o LPSTARTUPINFO lpStartupInfo, e o LPPROCESS_INFORMATION lpProcessInformation. Veja um exemplo adaptado da MSDN:

    #include <windows.h>
    #include <stdio.h>
    
    void main()
    {
        STARTUPINFO si;
        PROCESS_INFORMATION p;
    
        //
        // dica: muitas estruturas no Windows têm uma variável membro chamada
        // cb ou cbSize. Esse membro deve ser preenchido com o sizeof()
        // da estrutura ANTES de chamar a função. Se você não fizer isso a função 
        // retornará um erro. Esse é um erro comum de iniciantes (eu apanhei muito...)
        //
        ZeroMemory(&si, sizeof(si));
        si.cb = sizeof(si);
    
        ZeroMemory(&p, sizeof(p));
    
        //
        // Abrindo o Bloco de Notas
        //
        if( !CreateProcess( NULL,   // Sem módulo 
            "C:WINDOWS\\notepad.exe", // linha de comando para o executável 
            NULL,
            NULL,
            FALSE,            // O processo criado não herdará os handles
            0,
            NULL,
            NULL,
            &si,              // Ponteiro para STARTUPINFO
            &p )             // Ponteiro para PROCESS_INFORMATION 
        ) 
        {
            printf( "Erro em CreateProcess: 0x%X", GetLastError() );
            return;
        }
    
        //
        // O CreateProcess preencheu as estruturas e colocou handles nelas
        // como não vamos usar os handles, precisamos fechá-los...
        //
        CloseHandle(p.hProcess);
        CloseHandle(p.hThread);
    }
    

    O que eu preciso saber antes de estudar Win32?
    Você precisa ter prática em programação C/C++. A API Win32 é exportada como funções C, mas é usada a partir do C++ sem problemas.

    Quais livros de Win32 você recomenda?
    Primeiramente: eu indico que você leia os livros que vou indicar :-) Eu acho muito mais difícil estudar Win32 com tutoriais e lendo a MSDN. Não acho que exista muito matérial para iniciantes na Internet, e, como eu já disse, vale muito mais a pena comprar um livro e ler do que ficar procurando na Internet.

    O primeiro livro que você deve ler é o Programming Windows do Charles Petzold, considerado a Bíblia definitiva sobre o assunto. Ele já está na quinta edição, e é essencial para que você entenda como a GDI do Windows funciona. Ele é hardcore do meio pra frente (eu não tive paciência para ler a parte de toolbar), se você não tiver paciência leia até o capítulo 11 (página 566). Isso já será suficiente para entender como as coisas funcionam no Windows, e será muito útil MESMO QUE VOCÊ USE Windows Forms (afinal, WinForms nada mais é do que uma camada .NET sobre a GDI).

    Nossa, Win32 é complicado mesmo... 100 linhas para fazer uma janelinha branca e feia? Existe algo mais fácil?
    Existe. Hoje em dia, quase ninguém mais usa API diretamente ("à la Petzold" como é conhecido). A GDI é complexa pelo mesmo motivo que eu já expliquei, ela é muito flexível e é um dos motivos do sucesso do Windows. Poder e flexibilidade sempre têm um custo, que geralmente é a complexibilidade.

    Para resolver isso, foram criadas, por diversas empresas, bibliotecas que encapsulam a Win32 API para facilitar o seu uso (o .NET Framework é uma delas). A biblioteca provida pelo Microsoft Visual C++ é a MFC (Microsoft Foundation Classes), que encapsula uma boa parte da Win32 API em objetos C++. Além disso, a IDE do Visual C++ possui diversos wizards que geram classes e código e facilitam muito a programação. Usar dialogs (forms) em MFC é quase tão fácil quanto VB6. Existem outras bibliotecas, como a VCL/CLX do Delphi e do C++ Builder e o WTL da Microsoft (que é a que eu mais gosto).

    Leia algo sobre MFC depois de ler o Petzold. Pode ser qualquer livro, entendendo como funciona o Win32 e a GDI MFC não será um problema.

    O livro do Petzold cobre extensivamente a GDI (gráficos). E o resto?
    Depois de ler o Petzold e algo sobre MFC, leia o Advanced Windows do Jeffrey Richter, outro clássico. Pronto. Isso já é suficiente para você ser um programador Win32. Só 3 livros :-)

    Meus amigos dizem que a API Win32 está ultrapassada e não vale a pena estudar isso. É verdade?
    Não, não é. Você anda convivendo demais com Piranhas.NET. Explique para os seus amigos que TODOS os aplicativos para Windows hoje são feitos em Win32, já que ela é o Windows. No Windows Longhorn (previsto para 2006/2007) ALGUMAS NOVAS APIs serão disponibilidas como classes .NET. O Windows vai ficar cada vez mais .NET, mas isso deve levar no mínimo uns 5 anos. Não se esqueça que o .NET foi lançado em 2001 e a maioria dos aplicativos comerciais ainda são feitos em C e C++, usando a API Win32 (que por enquanto é a API do Windows)

    Meus amigos disseram que existe mais material de estudos e livros falando de .NET do que de Win32. É verdade?
    Não é verdade que existem mais livros sobre .NET. A API Win32 existe desde de 1993, e uma procura por livros na Amazon retornará vários livros. Procure "MFC" na Amazon, depois procure "Windows Forms" e veja a diferença. Além disso, no Code Project tem muito mais coisa útil em MFC do que em .NET.

    Você me deixou em dúvida. Se o .NET é tão ruim assim, então eu devo abandoná-lo e fazer tudo em C++?
    Primeiro: Eu nunca disse que o .NET é ruim. Na verdade eu acho ele muito bom para resolver os problemas que ele se propõe. Mas, tem seus problemas como qualquer API/Framework/Arquitetura. O grande problema é que no Brasil as pessoas acreditam no pessoal de marketing da Microsoft sem pensar se isso resolve o problema, e acham que .NET resolve todos os problemas. Nada resolve todos os problemas.

    Como eu já disse, hoje tudo é C++ e Win32 e vai levar anos (talvez mais de uma década) para que o .NET seja realmente a plataforma de programação do Windows. Você acha mesmo que a Microsoft, Adobe/Macromedia e outros vão refazer todos os aplicativos em C#? Além disso, C++ ainda é a coisa mais portável que existe (em termos de código fonte), e esse é o motivo dele ter sido usado pela Mozilla Foundation (Firefox, Thunderbird, etc), pela Adobe (Photoshop), etc. E se isso conta, em Linux quase tudo é C e C++, o kernel (C), Gnome (C), KDE, (C++), etc. O fator multiplataforma do .NET ainda é incerto, e depende de implementações de terceiros (como o Mono).

    Eu já escrevi um artigo sobre perspectivas para o NET a quase 1 ano atrás, e acho que ela continua a mesma. As duas arquiteturas são importantes, não troque o .NET pelo Win32, aprenda os dois e use o que for melhor para cada caso. Só não fique sentado esperando o .NET virar a plataforma oficial do Windows, você vai perder vários anos de mercado e da sua vida.

    Em 11/07/2005 14:37 - Comentários (41)

    Estudo prova que 1/3 dos estudos médicos estavam errados

    CNN: "Research: Third of study results don't hold up"

    Journal of American Medical Association: "Contradicted and Initially Stronger Effects in Highly Cited Clinical Research"

    O único jeito é continuar estudando...

    Em 14/07/2005 12:40 - Comentários (0)

    1º aniversário do 1bit.com.br !

    Nem parece que faz tanto tempo assim, mas a exatamente um ano atrás eu escrevi o primeiro post do blog. Um ano escrevendo sobre C++, Win32 e falando mal do .NET :-)

    Antes de começar esse site, eu tinha me convencido que já estava na hora de compartilhar tudo que eu aprendi com as pilhas de livros e com as madrugadas que passei programando. O projeto original era fazer um site junto com o Alfred Gary Myers, com artigos dos dois. Aí eu comecei a fazer o layout do site e escrever alguns artigos. Como ele acabou não escrevendo nada, eu resolvi aproveitar todo o trabalho e fazer um site só meu :-) O Alfred acabou criando um blog no TheSpoke - que muitos dizem ser o melhor de lá, mas como sou amigo dele eu não posso falar - e depois começou a escrever para a Fórum Access.

    O começo de um site é um paradoxo interessante de se lidar. Você escreve, só que não tem leitores, mas se você não escrever, nunca vai conseguir leitores. Hoje em dia o site tem uma média de 230 visitantes únicos por dia, segundo o Awstats. O número de acessos chegou a 370 no dia que eu enviei um link para o Slashdot. Como esperado, uma boa parte do bandwidth é consumida por joselitos que colocam o seus leitores de RSS para verificar o site a cada 5 minutos, mas por enquanto isso não me causa problemas. Como o site tem poucas imagens, ele é bem leve para carregar e eu ainda estou usando só 800MB por mês (o limite do meu plano é 2GB).

    Uma coisa legal de fazer quando se tem um site é acompanhar o que as pessoas procuram no Google para chegar até o seu site. Desde que o site começou, o que mais faz as pessoas pararem aqui é a busca por informações sobre "placenta baixa", por causa do artigo "Ouça o que eu digo: não ouça ninguém", onde eu uso um caso de diagnóstico de placenta baixa para dizer como é importante obter informações antes de tomar um decisão. Esse mês, pela primeira vez na história do site, a procura por informações sobre o Google Maps passou a procura por placenta baixa.

    Como parte das comemorações desse primeiro ano, eu escrevi o artigo sobre "Como ser um programador". Primeiro eu escrevi o "Como ser um BOM programador", e espero que ele tenha ajudado algumas pessoas a perceber como são simples os requisitos para ser um bom programador. Eu tentei com esse novo artigo ajudar as pessoas que querem virar programadores mas não sabem por onde começar.

    Parabéns para mim e para todos os leitores! Agora vamos seguir com mais um ano de escovação de bits :-)

    Em 21/07/2005 04:00 - Comentários (11)

    Windows Longhorn se chamará Windows Vista

    Windows Vista ? Não era mais fácil chamar de Windows 2015? Parece que a Microsoft contratou as mesmas pessoas que criam nomes para carros para criar nomes para softwares. Eu quero só ver qual será no nome do Office 12. Será que será Office Vista também ou vão inventar algo tão estranho quanto isso?

    Agora é só esperar as piadinhas infames que vão aparecer. Espero que esse Windows não estrague a vista... :-)

    Editado em 22/07/2005, 17:30:

    Site Oficial do Windows Vista

    Em 22/07/2005 14:13 - Comentários (11)

    A otimização do Visual C++ faz toda a diferença

    Estou fazendo um parser de IDL para meu projeto de computação distribuída. Nessa primeira fase, eu interpreto o aquivo IDL e preencho várias estruturas que descrevem as interfaces e seus métodos.

    Todo esse projeto está sendo feito em C++ padrão ISO, sem usar recursos específicos do Windows. Eu uso std::string em todo o programa, e o tratamento de erro é feito usando exceções. Para fazer o parse do arquivo eu usei o boost::tokenizer, que eu gostei muito. Você passa um std::string (ou dois iterators, início e fim) com o conteúdo do arquivo e ele te retorna um iterator forward-only para você percorrer os tokens do arquivo. Além disso você precisa informar quais serão os separadores de token, mas é só isso:

    #include <iostream>
    #include <boost/tokenizer.hpp>
    
    #include <string>
    
    using namespace std;
    using namespace boost;
    
    
    int main()
    {
       string str = "int main() { std::cout << "1bit.com.br" << std::endl; }";
       tokenizer<char_separator<char> > Tokenizer(string());
       
       Tokenizer.assign(str,char_separator<char>("tr " , "n"*,;:{}/\[]()"));
       
       //
       // percorre todos os tokens
       //
       for(tokenizer<char_separator<char> >::const_iterator i = Tokenizer.begin() ;
           i != Tokenizer.end();
           ++i)
       {
           cout << *i << "n";
       }
    }
    

    Depois de algumas noites e alguns fins de semana programando, eu finalmente terminei uma versão usável do meu parser. No final, eu coloquei um contador de tempo para medir se eu precisava ou não otimizar o código, e para comparar com o MIDL da Microsoft. Esse é o resultado que eu obtive com a versão debug (Athlon XP 2600+, Visual C++ 7.1) :

    // versão debug
    Parsing import file oaidl.idl
    Parsing import file objidl.idl
    Parsing import file unknwn.idl
    Parsing import file wtypes.idl
    Parsing import file ocidl.idl
    Parsing import file oleidl.idl
    Parsing import file servprov.idl
    Parsing import file urlmon.idl
    Parsing import file msxml.idl
    Parsing import file spbase.idl
    Finish (7625 ms)
    

    Mais do que 7 segundos, muito lento. Foi então que eu resolvi mudar para versão Release e configurar as otimizações com carinho. Veio então o resultado:

    // versão release
    Parsing import file oaidl.idl
    Parsing import file objidl.idl
    Parsing import file unknwn.idl
    Parsing import file wtypes.idl
    Parsing import file ocidl.idl
    Parsing import file oleidl.idl
    Parsing import file servprov.idl
    Parsing import file urlmon.idl
    Parsing import file msxml.idl
    Parsing import file spbase.idl
    Finish (265 ms)
    

    28 vezes mais rápido!! Todo o "overhead" de usar std::string desapareceu. Vendo o disassembly do código gerado, muitos dos métodos do std::string foram transformados em inline, e era como se eu estivesse usando string C nativa. Isso acaba com as desculpas de pessoas que não usam STL dizendo que ela deixa o programa mais lento. Além do overhead já ser pequeno pela simplicidade da implementação do STL, essa desculpa desaparece na versão Release. Mesmo porque, como o código da STL é simples, o compilador tem mais facilidade para otimizá-lo. Muito mais do que para otimizar a gambiarra complicada que você perderia dias e dias para fazer.

    Vale lembrar que eu me esforcei para fazer o código da forma mais clara e bug-free possível. Nem acesso a arquivos foi otimizado, eu simplesmente li o arquivo inteiro em uma std::string usando getline(f, str, f.widen(EOF)). Eu ainda posso otimizar isso usando FileMapping e passando o ponteiro de início e fim como iterators para o boost::tokenizer.

    Em 26/07/2005 18:53 - Comentários (5)

    Fatos verídicos

    - Quanto tempo você demora pra resolver esse bug?
    - 16 Horas.
    - Tudo isso?
    - Bom, primeiro eu tenho que entender como o sistema funciona.
    - O sistema inteiro?
    - Não necessariamente. Se no meio do estudo eu encontrar o motivo do bug, é só corrigí-lo.
    - Nesse caso, esqueça esse negócio de estudo, vá direto para o motivo do bug. 30 minutos esta bom ou é muito?

    Em 05/08/2005 18:29 - Comentários (6)

    Usando Win32 API para otimizar o I/O, parte 1

    Parte Zero     Parte 1     Parte 2     Parte 3     Parte 4     Parte 5     Parte 6     Fontes do parser

    Como eu já disse em um post anterior, estou fazendo um parser de arquivos IDL, usando somente C++ ISO (nada de Win32), STL e Boost. Apesar de estar realmente impressionado com o desempenho do meu parser do jeito que está, eu me propus a testar o nível de otimização de I/O que seria possível usando a API Win32 diretamente. Vou publicar uma sequência de posts contando minhas exeriências com isso, explicando os métodos e ferramentas que eu usei para chegar às conclusões.

    Primeiro, deixe-me explicar algo sobre desempenho (ou performance). Ultimamente eu tenho trabalhado bastante com isso, minha atribuição na empresa onde trabalho é (entre outras) pegar programas, serviços e componentes que já funcionam e melhorá-los. As melhorias são feitas em vários aspectos, tanto melhorias na estabilidade dos serviços quanto melhorias de desempenho.

    Existem vários fatores importantes para se fazer melhoria de desempenho em um software. Conhecimento da linguagem de programação usada (C++ no meu caso) é muito importante, assim como é importante o conhecimento do funcionamento das bibliotecas (como a grande maioria das bibliotecas C++ são pequenas e com o fonte disponível, essas tarefa exige muito estudo de código fonte e pouca adivinhação ou disassembly). Outro fator muito importante é o conhecimento do sistema operacional e quais interações seu programa tem com cada parte dele. Nesse caso específico, o conhecimento do funcionamento do I/O do sistema operacional é muito importante, até para podermos saber quais opções temos para fazer essa melhoria. Um melhoria pode envolver uma modificação - algumas vezes pequena - no método usado para se chegar a um resultado, ou uma mudança completa no enfoque ou no algoritmo.

    Todos os fatores que eu citei são importantes, mas o fator mais importante não é um conhecimento técnico. Chama-se mensuração. Só é possível dizer que alguma coisa foi melhorada se você souber exatamente o estado dela no momento atual, e se você conseguir mensurar exatamente qual o nível de melhoria entre uma modificação e outra. O conceito de melhor é algo muito subjetivo, não resolve o problema. Não basta o software ser "melhor", deve-se saber o "quão melhor" esse software é. E, depois disso, saber se o esforço dedicado a essa melhoria realmente valeu a pena, ou se foi muito trabalho para uma melhoria pequena.

    A estrutura do parser é simples, e da forma que está feito hoje (sem Win32), o código que faz o I/O é muito parecido com o abaixo:

    void MidlParser::ParseMidlFile(const char* fileName)
    {
       fstream f;
       string str;
       
       ...
       
       if(m_bParseAsImportFile)
       {
          if(!OpenIncludeFile(fileName, &f, &m_parsedFileName))
             throw ParseException(string("error opening import file "") + fileName + 
                                  "\"", *this, m_parsedFileName);
    	   ...
       }
       else
       {
          f.open(fileName, ios::in);
    
          if(f.fail())
             throw ParseException(string("error opening file "") + fileName + 
                                  "\"", *this, m_parsedFileName);
    	  ...
       }
       
       //
       // carrega o arquivo inteiro em uma string STL
       //
       getline(f, str, f.widen(EOF));
    	
       //
       // Inicializa o tokeninzer
       //
       m_Tokenizer.assign(str,char_separator("\t\r " , "\n\"*,;:{}/[]()"));
       
       ...  
       
       //
       // Loop que interpreta o IDL
       //
       for(;;)
       {
    	  // ...
       }
    

    Quando um arquivo é interpretado, diversas estruturas - IdlInterface, IdlMethod, IdlParameter, etc - são preenchidas com as informações interpretadas. Isso faz com que o interpretador fique completamente independente do gerador de proxy/stub que consumirá essas estruturas, o que me permite, futuramente, usar outro formato que não o Microsoft IDL.

    Para um arquivo IDL comum, aproximadamente 400kb de texto deve ser lidos, em aproximadamente 10 arquivos. A maioria dos arquivos interpretados fazem parte do Windows Platform SDK, que definem os tipos de dados padrão do COM. A saída do parser é essa:

    Parsing import file oaidl.idl
    Parsing import file objidl.idl
    Parsing import file unknwn.idl
    Parsing import file wtypes.idl
    Parsing import file ocidl.idl
    Parsing import file oleidl.idl
    Parsing import file servprov.idl
    Parsing import file urlmon.idl
    Parsing import file msxml.idl
    Finish (234ms)
    

    No próximo post da série veremos algumas estatísticas sobre o programa da forma que está e começaremos a fazer algumas otimizações.

    Em 08/08/2005 02:58 - Comentários (0)

    Usando Win32 API para otimizar o I/O, parte 2

    Parte Zero     Parte 1     Parte 2     Parte 3     Parte 4     Parte 5     Parte 6     Fontes do parser

    Antes de começar a otimização do I/O do parser, é necessário fazer algumas considerações e algumas medições. A proposta inicial é otimizar o I/O do parser, mas será que vale mesmo a pena?

    Fiz algumas medições para saber quanto tempo o parser demora para ler os arquivos que ele interpreta. Depois de várias medições (aproximadamente 50), cheguei a conclusão de que gastamos, em média, 20% do tempo com I/O. Em situações onde o computador acabou de ser ligado - ou seja, quando os arquivos não estão no Cache Manager do Windows - o tempo de acesso aos arquivos pode chegar a 50% do tempo gasto pelo parser. Não esqueça que só estamos medindo o tempo necessário para ler os arquivos (que como eu já disse, somam aproximadamente 400kb), não gravamos nada.

    Tempo médio gasto com I/O:  20%
    Tempo máximo gasto com I/O: 50%
    

    Já que a interpretação do arquivo em si leva 80% do tempo em média, não valeria mais a pena otimizá-la e de deixar o I/O para depois? Minha resposta é não, e vou explicar os motivos. A interpretação é o core do sistema, e o código que lida com isso foi feito da forma mais clara possível, para facilitar a manutenção e melhorias. E eu não estou disposto a sacrificar a clareza do código para que o parser fique mais rápido. Sendo assim, a otimização seria mais difícil, porque provavelmente envolveria mudanças em algoritmos, além de ser feita com essas requisição (clareza do código) em mente.

    O motivo principal para otimizar o I/O é que, de acordo com o que vimos no post anterior, o I/O corresponde a menos de 4 linhas de código, enquando a classe de parser tem aproximadamente 850 linhas. Essas 4 linhas de código, algumas vezes, chegam a representar 50% do tempo gasto. Sendo assim, o custo-benefício da alteração me pareceu muito satisfatório, já que é fácil otimizar o I/O, afinal, ele é uma parte pontual e fácil de ser isolada e compreendida. A otimização do I/O não vai comprometer a legibilidade do código.

    Conclusão: vale a pena otimizar o I/O. Depois dessas medições, no próximo post começaremos a testar as otimizações possíveis e comprar os resultados.

    Em 11/08/2005 20:27 - Comentários (0)

    Usando Win32 API para otimizar o I/O, parte 3

    Parte Zero     Parte 1     Parte 2     Parte 3     Parte 4     Parte 5     Parte 6     Fontes do parser

    Agora que já tivemos bastante bla-bla-bla e medições, está na hora de fazer alguma otimização. A primeira otimização que faremos consiste em mudar os acessos aos arquivos - que hoje são feitos usando a runtime do C++ - para usar a Win32 API diretamente. Duas modificações serão feitas:

    • Usar CreateFile/ReadFile para ler o conteúdo do arquivo;
    • Usar VirtualAlloc para alocar memória ao invés de alocar do heap (que é o que a std::string faz).

    Como um trecho de código vale sempre mais do que ~340 m/s, vou mostrá-lo antes e explicar depois:

    void MidlParser::ParseMidlFile(const char* fileName)
    {
       HANDLE hFile;
       char* szFileContent;
       DWORD dwFileSize;
       DWORD dwReaded;
    
       if(m_bParseAsImportFile)
       {
          //
          // vamos procurar o arquivos nas pasta determinadas para include
          //
          for(vector::const_iterator i = m_IncludePaths.begin() ; 
              i != m_IncludePaths.end() ; 
              ++i)
          {
             string str = *i + fileName;
    
             hFile = CreateFile(str.c_str(), GENERIC_READ, FILE_SHARE_READ, 
                                NULL, OPEN_EXISTING, NULL, NULL);
    
             if(hFile != INVALID_HANDLE_VALUE)
                break;
          }
    
          if(hFile == INVALID_HANDLE_VALUE)
             throw ParseException(string("error opening import file \"") + fileName 
                                  + "\"", *this, m_parsedFileName);
    
          dwFileSize = GetFileSize(hFile, NULL);
    
          //
          // ao invés de alocar do Heap (que é para alocações pequenas), 
          // vou alocar direto da memória virtual. O Heap Manager usa 
          // memória virtual para isso, então estaremos 
          // "pulando" um nível de abstração.
          //
          //szFileContent = (char*)HeapAlloc(GetProcessHeap(), NULL, dwFileSize);
    
          szFileContent = (char*)VirtualAlloc(NULL, dwFileSize + (dwFileSize % 4096), 
                                              MEM_COMMIT, PAGE_READWRITE);
    
          //
          // vamos ler o arquivo inteiro de uma vez
          //
          ReadFile(hFile, szFileContent, dwFileSize, &dwReaded, NULL);
          ...
       }
       else
       {
          hFile.Create(fileName, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING);
    
          if(hFile.m_h == INVALID_HANDLE_VALUE)
             throw ParseException(string("error opening file \"") + fileName + 
                                  "\"", *this, m_parsedFileName);
    
          dwFileSize = GetFileSize(hFile, NULL);
    
      //szFileContent = (char*)HeapAlloc(GetProcessHeap(), NULL, dwFileSize);
          szFileContent = (char*)VirtualAlloc(NULL, dwFileSize + (dwFileSize % 4096), 
                                              MEM_COMMIT, PAGE_READWRITE);
          
          ReadFile(hFile, szFileContent, dwFileSize, &dwReaded, NULL);
    
          ...
       }
    

    Algumas coisas importantes devem ser notadas nessa implementação. A primeira é que, além de mudar o acesso para usar CreateFile/ReadFile, eu pego antes o tamanho do arquivo, para ler tudo de uma vez. Não sabemos (por enquanto) como a runtime do C++ faz a leitura, mas ler o arquivo todo de uma vez só é a melhor forma. Outro detalhe da implementação é que eu uso VirtualAlloc para alocar memória e não HeapAlloc/new/malloc.

    Antes de explicar o motivo de usar VirtualAlloc, uma explicação sobre gerenciamento de memória no Windows. A função mais low-level para alocar memória em user mode é a VirtualAlloc. Ela aloca memória no endereçamento do processo, com a granulação de uma página de memória (no Windows, em user mode, e em arquitetura x86, uma página de memória é de 4kb). Sendo assim, só podemos alocar múltipos de uma página. Por isso eu coloquei um dwFileSize + (dwFileSize % 4096) no tamanho, para arredondar a quantidade alocada para o primeiro múltiplo de 4kb maior do que a quantidade necessária.

    Como muitas vezes os programas precisam de áreas de memória menores do que 4kb, usar no mínimo esse valor em cada alocação seria um desperdício muito grande. Por isso existem os heaps, que são pools de memória que servem alocações em granulações menores. Apesar de (na maioria das vezes) reduzir o desperdício de memória, o heap é mais lento do que o VirtualAlloc, porque uma alocação envolve algorimos de procura de blocos livres e desfragmentação da memória alocada para o heap.

    O Windows possui gerenciamento de heap nativo, tanto em user mode quanto em kernel mode. Em user mode a função que aloca memória de um heap é a HeapAlloc(HANDLE,DWORD,SIZE_T), onde o primeiro parâmetro é um HANDLE para um heap. Você pode usar o heap default do processo (chamando GetProcessHeap()) ou criar um heap usando HeapCreate(). A runtime do C/C++ usa o HeapAlloc, mas cria um heap separado do heap padrão do processo.

    Usei o VirtualAlloc porque ele é mais rápido do que o heap (já que aloca uma página diretamente e não tem problemas de fragmentação) e não envolve mais esforço de programação. Além disso, o desperdício da memória que ficará entre o que usamos e o fim da página será praticamente desprezível. Leremos aproximadamente 400kb, ou seja, 100 páginas. Mesmo se 100% das páginas forem desperdiçadas (o que é impossível acontecer), estaríamos usando 400kb a mais. Eu medi o desperdício para os arquivos do SDK, e ele é de 5,21% (o que é realmente desprezzzzível, como diria o Patolino).

    Voltando à otimização de I/O, aqui está a medição que eu fiz depois dessas otimizações:

    Média de tempo (50 chamadas):

    228,01 ms - versão usando a runtime do C++
    214,68 ms - versão usando Win32 API

    Melhoria: 6,21%

    Para primeira tentativa de otimização, 6,21% não é tão mal... Na próxima parte veremos se podemos fazer melhor.

    Em 17/08/2005 23:07 - Comentários (5)

    Explicando a sopa de letrinhas da programação C/C++ para Windows: MFC

    Outros posts dessa mesma série:   Win32     COM     ATL

    MFC: A Microsoft Foundation Classes é um framework e uma biblioteca de classes C++ que encapsula grande parte da API Win32. Ela foi criada pela Microsoft para facilitar a programação em Win32 usando C++, já que a API Win32 é exportada como funções C. Ela é um framework no sentido de que todos os aplicativos MFC devem "se encaixar" nesse framework de alguma forma, seguindo certos requisitos. Ela não foi feita para ser incluída em um projeto só para usar algumas classes, como é o caso do WTL (eu falarei de WTL em outro post).

    Como um trecho de código vale mais do que 2 + 2 palavras, vamos à ele:

    #include <afxwin.h>
    
    //
    // Minha janela principal
    //
    class CMyWindow : public CFrameWnd
    {
    private:
      CButton m_btnTest;
    
      DECLARE_MESSAGE_MAP();
    
    public:
      //
      // essa função não é da MFC, eu a criei para inicializar meus 
      // controles daqui
      //
      BOOL InitializeControls()
      {
        m_btnTest.Create("blah",BS_PUSHBUTTON , CRect(10,10,100,60),this, 1);
        m_btnTest.ShowWindow(SW_SHOW);
    
        return TRUE;
      }
    
      //
      // função que trata o evento de clique do botão
      //
      LRESULT OnButtonTestClick()
      {
        AfxMessageBox("button click!");
        return 0;
      }
    
      //
      // evento de duplo click
      //
      LRESULT OnLButtonDblClk(UINT, CPoint)
      {
        AfxMessageBox("dbl click!");
        return 0;
      }
    
      //
      // evento de resize
      //
      LRESULT OnSize(UINT nType, int cx, int cy)
      {
        CString str;
    
        //
        // vamos colocar a nova altura e largura como título
        // da janela
        //
        str.Format("largura: %d, altura:%d", cx, cy);
    
        SetWindowText(str);
    
        return 0;
      }
    };
    
    //
    // mapa de mensagens da classe CMyWindow
    // aqui colocamos de modo declarativo qual evento será
    // tratado por qual função
    //
    BEGIN_MESSAGE_MAP(CMyWindow, CFrameWnd)
      ON_BN_CLICKED(1, OnButtonTestClick)
      ON_WM_LBUTTONDBLCLK()
      ON_WM_SIZE()
    END_MESSAGE_MAP( )
    
    //
    // classe que representa a aplicação em si,
    // herda de CWinApp
    //
    class CMyApp : public CWinApp
    {
      //
      // Função chamada pela MFC assim que o aplicativo é
      // inicializado. Coloque aqui o que seria colocado
      // no WinMain
      //
      BOOL InitInstance()
      {
        CMyWindow* pMyWindow = new CMyWindow;
    
        //
        // vamos criar nossa janela e mostrá-la
        //
        pMyWindow->Create(NULL, "www.1bit.com.br");
    
        pMyWindow->InitializeControls();
        pMyWindow->ShowWindow(SW_SHOW);
        pMyWindow->UpdateWindow();
    
        m_pMainWnd = pMyWindow;
    
        return TRUE;
      }
    };
    
    
    CMyApp theApp;
    

    A MFC é a principal biblioteca de classes para C++ da Microsoft. Ela vem junto com o Visual C++, e a IDE tem total suporte para ela, facilitando a nossa vida com wizards e "clique com o botão direito e...". Fazer um "Dialog Application" usando MFC e o Visual C++ é quase tão fácil quanto fazer um programa em VB (meus alunos de C++ disseram isso). Como no VB, você desenha um botão visualmente, dá um duplo clique no botão criado e a MFC cria um handler para o evento de clique do botão. Além disso, a MFC contém diversas macros e funções para associar variáveis com controles Win32. Esse recurso é chamado de DDX, e é muito útil para formulários cheios de CheckBoxes e TextBoxes. Eu acho esse um dos recursos mais úteis da MFC, e reduz bastante o tempo de desenvolvimento de programas com essas características.

    A MFC também tem suporte à bancos de dados ODBC ou OLEDB. No caso de OLEDB, um wizard cria classes com propriedades (variáveis membro) equivalentes aos campos do banco de dados. Mas o grande destaque da MFC é "Document/View Model", que provê um framework para aplicativos que usam o conceito de documento (como o Word, CorelDRAW!, etc). Junto com o suporte a serialização, fica bem mais fácil fazer um aplicativo de edição de documentos com open/save, área de transferência e várias views de um mesmo documento.

    A MFC é bastante criticada por não usar recursos modernos do C++ e não ser totalmente type-safe. Apesar das melhorias no quesito type-safe a partir da MFC 7, as críticas ainda persistem. Apesar de tudo isso, a MFC ainda é muito usada, e é possível achar muita informação sobre ela na Internet. A literatura é vasta e existem diversos sites com seções especializadas em MFC (como o Code Project e o Code Guru). Muitas empresas (inclusive brasileiras) desenvolvem aplicativos em MFC, e a finada certificação "Developing Desktop Applications Using Microsoft Visual C++" era toda sobre MFC.

    Usando a MFC você tem a opção de fazer um link estático com a MFC ou usar a MFC em uma DLL (de aproximadamente 1 MB). No exemplo de código acima, o link estático fez o executável ficar com 100kb a mais, mas eu já vi casos onde o executável fica 300kb maior - o que em muitas situações é desprezível.

    Mais informações

    Documentação da MFC na MSDN
    Hierarquia de classes da MFC
    Code Project
    Livros sobre MFC na Amazon

    Em 23/08/2005 17:51 - Comentários (9)

    E tem gente que acha que Dilbert é ficção...

    Em 24/08/2005 13:59 - Comentários (1)

    Usando Win32 API para otimizar o I/O, parte 4

    Parte Zero     Parte 1     Parte 2     Parte 3     Parte 4     Parte 5     Parte 6     Fontes do parser

    Conhecer a arquitetura do sistema operacional e de todas as dependências do seu software é essencial na hora de fazer otimização. Muitas vezes, mudanças muito pequenas no código podem melhorar substancialmente um software. Até porque, uma mudança pequena pode causar grandes mudanças no funcionamento de um módulo do sistema operacional ou de alguma biblioteca.

    Além do conhecimento, é importante saber bem o que se está fazendo (e prestar atenção na documentação :-)). Na documentação do CreateFile na MSDN, estão listados diversos flags que você pode passar quando abre um arquivo. No nosso caso específico, o que mais chama atenção é o FILE_FLAG_SEQUENTIAL_SCAN:

    FILE_FLAG_SEQUENTIAL_SCAN A file is accessed sequentially from beginning to end. The system can use this as a hint to optimize file caching. If an application moves the file pointer for random access, optimum caching may not occur. However, correct operation is still guaranteed.
    Specifying this flag can increase performance for applications that read large files using sequential access. Performance gains can be even more noticeable for applications that read large files mostly sequentially, but occasionally skip over small ranges of bytes.

    Usando essa flag, estamos dizendo ao Windows que acessaremos o arquivo do início ao fim, sequencialmente. A função interpreta isso como uma dica, que pode ser usada para otimizar o cache do arquivo. Sabendo como acessaremos o arquivo, o Windows pode "prever" nosso comportamento e ficar preparado para isso.

    Modificando o trecho de código que abre o arquivo, ele ficará assim:

    //
    // chama atual para abrir um arquivo
    //
    hFile = CreateFile(str.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, 
                       OPEN_EXISTING, NULL, NULL);
    
    //
    // nova chamada para abrir um arquivo
    //
    hFile = CreateFile(str.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, 
                       OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
    

    Refeitos os testes de desempenho, temos os seguintes resultados:

    Média de tempo (50 chamadas):

    228,01 ms - versão usando a runtime do C++
    214,68 ms - versão usando CreateFile
    204,17 ms - versão usando CreateFile com FILE_FLAG_SEQUENTIAL_SCAN

    5,15% - melhoria em relação a versão Win32 sem FILE_FLAG_SEQUENTIAL_SCAN
    11,68% - melhoria em relação a versão inicial (runtime C++)

    Ganhamos mais ~5% de desempenho somente trocando UM flag em UMA chamada. Isso foi quase a mesma melhoria que ganhamos trocando todos os acessos feitos usando a runtime do C++ por chamadas Win32. Conhecimento é poder.

    Em 29/08/2005 16:15 - Comentários (0)

    Aos coitados que ainda precisam usar Visual C++ 6.0 (inclusive eu)

    Seguindo a série de otimização de performance e meu post sobre o otimizador do Visual C++ 7.1, eu resolvi tentar compilar o parser usando o Visual C++ 6.0 Service Pack 5, para fazer uma comparação com o VC7.1. Sim, TENTAR é o termo exato. Apesar de alguns dizerem o que Visual C++ 6 suporta Boost, isso não é bem verdade, não é a primeira vez que eu tenho problemas tentando fazer o Boost compilar no Visual C++ 6.0. Olha só o que eu consegui com minha tentativa:

    --------------------Configuration: spidl_vc6 - Win32 Debug--------------------
    Compiling...
    StdAfx.cpp
    stlport\stl\_alloc.h(354) : fatal error C1001: INTERNAL COMPILER ERROR
            (compiler file 'msc1.cpp', line 1794) 
             Please choose the Technical Support command on the Visual C++ 
             Help menu, or open the Technical Support help file for more information
    Error executing cl.exe.
    
    spidl_vc6.exe - 1 error(s), 0 warning(s)
    
    

    Pode parecer um erro da STLPort, mas eu uso a STLPort em vários projetos VC6 e não tenho problemas. O problema real é que o Boost (que eu uso no parser) é muita areia para o caminhãozinho do VC6.

    A empresa onde eu trabalho ainda usa Visual C++ 6.0, assim como muitas outras. Não é o nosso caso, mas eu já reparei que muitas empresas ainda usam esse compilador velho, desatualizado e fora do padrão por causa da confusão que a Microsoft fez colocando .NET em tudo que existia - tanto que o próximo Visual Studio será "Visual Studio 2005", sem o .NERD no final. Muita gente acha que o Visual Studio.NET é uma suite de ferramentas para desenvolver somente aplicações .NET. Se faz necessário explicar claramente e em letras garrafais:

    O Visual C++ 6.0 é MUITO velho, foi lançado em 1998, antes mesmo do padrão C++ ISO ter sido finalizado.

    O Visual Studio.NET 2003 contém o Visual C++ 7.1, que é o último compilador C++ lançado pela Microsoft. Ele tem uma ótima conformidade com o padrão ISO e tem um otimizador muito bom. Usando o Visual C++ 7.1 seus programas não virarão .NET, serão 100% nativos. O Visual C++ 7.1 (que, novamente, vem junto com o "Visual Studio.NET 2003") importa seus projetos Visual C++ 6.0 sem maiores problemas. Na versão 7.1, tanto a MFC quanto a ATL foram bastante melhoradas, e vale a pena fazer o upgrade. O Windows XP SP2 e o Windows Server 2003 são compilados usando o Visual C++ 7.1, o mesmo que você recebe no Visual Studio.NET 2003.

    O Visual C++ 7.1 (lembre-se, ele é parte do "Visual Studio.NET 2003") tem um flag (/clr) para compilar Managed C++ (o binding C++ para .NET), mas ele é opcional (afinal, é um flag). Usando o Visual C++ 7.1 você continuará gerenciando sua própria memória, aumentará sua produtividade (se você acha que C++ não é produtivo é porque você nunca usou), já que terá um compilador padrão ISO, e poderá usar Boost, Loki, ATL7, MFC7, etc. Ah, e além disso, você continuará fazendo programas muito mais rápidos do que seus amiguinhos do garbage collector... :-)

    Em 01/09/2005 19:39 - Comentários (7)

    Usando Win32 API para otimizar o I/O, parte 5

    Parte Zero     Parte 1     Parte 2     Parte 3     Parte 4     Parte 5     Parte 6     Fontes do parser

    Um fator muito importante na hora de melhorar o desempenho de um aplicativo é saber exatamente o que está acontecendo e o que ele está fazendo. Não podemos fazer modificações em um software sem saber se essas modificações vão trazer alguma melhora e sem saber o motivo da melhora. Lembre-se do que eu falei sobre mensuração, precisamos saber exatamente qual foi o grau de melhoria.

    Hoje não vamos otimizar nada, mas vamos descobrir porque a nossa versão ficou mais rápida do que a que usa a runtime do C++. Afinal, nosso código faz exatamente a mesma coisa: ler o arquivo inteiro. Só que nós lemos da melhor forma possível (o arquivo todo de uma vez só), e acreditamos que essa não seja a forma usada pela runtime do C++.

    Para descobrir como a runtime do C++ faz a leitura do arquivo, vamos usar o WinDbg. Com ele vamos saber exatamente quais parâmetros estão sendo passados para as funções da Win32 API. Como a Win32 API é o acesso mais low level que se pode ter em user mode, a runtime do C++ só pode acabar usando a Win32 API no final das contas.

    A função que faz a leitura de um arquivo é a ReadFile (a ReadFileEx é usada somente para leituras assíncronas). Sabendo disso, vamos colocar um breakpoint na função ReadFile e fazer um dump dos parâmetros a cada chamada. Carregue o executável no WinDbg (veja os tutoriais de Windbg caso ache necessário), e coloque o breakpoint na função ReadFile. Depois disso, go, go, go:

    0:000> bp KERNEL32!ReadFile
    0:000> g
    Breakpoint 0 hit
    eax=002f0650 ebx=002f26b8 ecx=002f26b8 edx=002f0081 esi=00000018 edi=0042dfc0
    eip=79736168 esp=0012f7f0 ebp=0012f824 iopl=0         nv up ei pl zr na po nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=0038  gs=0000             efl=00000246
    KERNEL32!ReadFile:
    79736168 55               push    ebp
    

    Chegamos ao ReadFile, agora precisamos fazer um dump dos parâmetros. Olhando a documentação da MSDN, vemos que o primeiro parâmetro é o HANDLE do arquivo, e o terceiro é a quantidade de bytes lidos. Fazendo dump desses parâmetros conseguimos saber exatamente como a runtime do C++ está fazendo a leitura. Código primeiro, explicação depois:

    0:000> * Informações do handle (primeiro parâmetro)
    0:000> !handle poi(@esp+4) 4
    Handle 3d4
      Name         	\Device\HarddiskVolume1\temp\test.idl
    0:000> * Quantidade de bytes lidos (terceiro parâmetro)
    0:000> dd @esp+0xc L1
    0012f7fc  00001000
    

    A primeira coisa que fizemos foi pegar informações sobre o HANDLE do arquivo. Usamos a extensão !handle, que com a flag 4 no final faz um dump de todas as informações do HANDLE, inclusive nome do arquivo quando cabível (pode ser um HANDLE de evento ou outra coisa...). Pegamos o primeiro parâmetro usando @esp+4 (já falei sobre posição de parâmetros na pilha x86 anteriormente) e colocamos poi() para tratá-lo como um ponteiro e pegar o apontado. Apesar do tipo ser HANDLE e não um ponteiro, HANDLE nada mais é do que um #define para void*. Depois disso usamos o dd (Dump DWORD) para ler o terceiro parâmetro - a primeira coluna é o endereço de memória lido e o L1 no final é para ler somente um DWORD, o default é ler 32 DWORDs. Note que tudo vem em hexadecimal, então 00001000 é na realidade 4096 em decimal, que na realidade é o tamanho de uma página de memória x86 (na realidade, eu já falei sobre alinhamento e paginação de memória nessa série).

    Temos as informações da primeira chamada à ReadFile, mas repetir esses comandos em todas as chamadas é muito trabalhoso. Por isso vamos criar um breakpoint que execute esses comandos para fazer um log de todos os ReadFile chamados. Vamos ao comando:

    0:000> bp kernel32!ReadFile "!handle poi(@esp+4) 4; dd esp+0xC L1 ; g"
    0:000> g
    

    Com esses comandos faremos o dump das informações que queremos e deixaremos o programa seguir (g). Vamos ao resultado:

    0:000> g
    Handle 3d4
      Name         	\Device\HarddiskVolume1\temp\test.idl
    0012f7fc  00001000
    Handle 3d4
      Name         	\Device\HarddiskVolume1\temp\test.idl
    0012f7fc  00001000
    Handle 3d4
      Name         	\Device\HarddiskVolume1\temp\test.idl
    0012f7fc  00001000
    Handle 3d4
      Name         	\Device\HarddiskVolume1\temp\test.idl
    0012f7fc  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OAIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OAIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OAIdl.Idl
    0012f200  00000001
    Handle 3d8
      Name         	...\Microsoft SDK\include\OAIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OAIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OAIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OAIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OAIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OAIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OAIdl.Idl
    0012f200  00000001
    Handle 3d8
      Name         	...\Microsoft SDK\include\OAIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OAIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OAIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OAIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OAIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OAIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OAIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OAIdl.Idl
    0012f200  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012ec04  00001000
    Handle 3c4
      Name         	...\Microsoft SDK\include\Unknwn.Idl
    0012e608  00001000
    Handle 3c4
      Name         	...\Microsoft SDK\include\Unknwn.Idl
    0012e608  00001000
    Handle 3c4
      Name         	...\Microsoft SDK\include\Unknwn.Idl
    0012e608  00001000
    Handle 3c0
      Name         	...\Microsoft SDK\include\WTypes.Idl
    0012e00c  00001000
    Handle 3c0
      Name         	...\Microsoft SDK\include\WTypes.Idl
    0012e00c  00001000
    Handle 3c0
    
      Name         	...\Microsoft SDK\include\WTypes.Idl
    0012e00c  00001000
    Handle 3c0
      Name         	...\Microsoft SDK\include\WTypes.Idl
    0012e00c  00001000
    Handle 3c0
      Name         	...\Microsoft SDK\include\WTypes.Idl
    0012e00c  00001000
    Handle 3c0
      Name         	...\Microsoft SDK\include\WTypes.Idl
    0012e00c  00001000
    Handle 3c0
      Name         	...\Microsoft SDK\include\WTypes.Idl
    0012e00c  00001000
    Handle 3c0
      Name         	...\Microsoft SDK\include\WTypes.Idl
    0012e00c  00001000
    Handle 3c0
      Name         	...\Microsoft SDK\include\WTypes.Idl
    0012e00c  00001000
    Handle 3c0
      Name         	...\Microsoft SDK\include\WTypes.Idl
    0012e00c  00001000
    Handle 3c0
      Name         	...\Microsoft SDK\include\WTypes.Idl
    0012e00c  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OCIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OCIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OCIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OCIdl.Idl
    0012f200  00000001
    Handle 3d8
      Name         	...\Microsoft SDK\include\OCIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OCIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OCIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OCIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OCIdl.Idl
    0012f200  00000001
    Handle 3d8
      Name         	...\Microsoft SDK\include\OCIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OCIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OCIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OCIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OCIdl.Idl
    0012f200  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\OleIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\OleIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\OleIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\OleIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\OleIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\OleIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\OleIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\OleIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ServProv.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ServProv.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00000001
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00000001
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c4
      Name         	...\Microsoft SDK\include\MsXml.Idl
    0012e608  00001000
    Handle 3c4
      Name         	...\Microsoft SDK\include\MsXml.Idl
    0012e608  00001000
    Handle 3c4
      Name         	...\Microsoft SDK\include\MsXml.Idl
    0012e608  00001000
    Handle 3c4
      Name         	...\Microsoft SDK\include\MsXml.Idl
    0012e608  00001000
    Handle 3c4
      Name         	...\Microsoft SDK\include\MsXml.Idl
    0012e608  00001000
    eax=00000000 ebx=7ffdf000 ecx=00000000 edx=00000000 esi=7846ee04 edi=00000000
    eip=7846ee0f esp=0012fe00 ebp=0012fec8 iopl=0         nv up ei pl zr na po nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=0038  gs=0000             efl=00000246
    ntdll!NtTerminateProcess+0xb:
    7846ee0f c20800           ret     0x8
    

    Pronto, descobrimos que a runtime do C++ (usando getline(f, str, f.widen(EOF))) pede 4096 bytes a cada leitura. Nosso parser fica mais rápido usando Win32 API diretamente porque lemos o arquivo todo de uma vez, além de passar a flag que avisa o Windows que leremos o arquivo sequencialmente. Olhe como fica o mesmo dump feito com a versão Win32:

    0:000> bp kernel32!ReadFile "!handle poi(@esp+4) 4; dd esp+0xC L1 ; g"
    0:000> g
    Handle 3d4
      Name         	\Device\HarddiskVolume1\temp\test.idl
    0012f834  00002cfd
    Handle 3d8
      Name         	...\Microsoft SDK\include\OAIdl.Idl
    0012f1d8  0000ecf9
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012eb7c  00012200
    Handle 3c4
      Name         	...\Microsoft SDK\include\Unknwn.Idl
    0012e520  000013fb
    Handle 3c0
      Name         	...\Microsoft SDK\include\WTypes.Idl
    0012dec4  00009ea7
    Handle 3d8
      Name         	...\Microsoft SDK\include\OCIdl.Idl
    0012f1d8  0000a35b
    Handle 3c8
      Name         	...\Microsoft SDK\include\OleIdl.Idl
    0012eb7c  00006d48
    Handle 3c8
      Name         	...\Microsoft SDK\include\ServProv.Idl
    0012eb7c  00000e93
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012eb7c  0001cd2f
    Handle 3c4
      Name         	...\Microsoft SDK\include\MsXml.Idl
    0012e520  00003048
    eax=00000000 ebx=7ffdf000 ecx=00000000 edx=00000000 esi=7846ee04 edi=00000000
    eip=7846ee0f esp=0012fe00 ebp=0012fec8 iopl=0         nv up ei pl zr na po nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=0038  gs=0000             efl=00000246
    ntdll!NtTerminateProcess+0xb:
    7846ee0f c20800           ret     0x8
    

    Agora temos um log bem menor porque só fazemos uma chamada à ReadFile por arquivo. Agora sabemos exatamente o motivo pelo qual nosso I/O é mais rápido.

    Em 08/09/2005 14:27 - Comentários (2)

    WTF?

    Como eu não terminei o novo post da série de performance, não postei nada semana passada, passei o fim de semana todo lendo os PPTs do PDC e não tenho nada de realmente útil para postar, vamos ao besteirol.

    Para reduzir o vazio existencial do meu RSS, vou colocar algumas seleções particulares do "Daily What The Fuck", site que enumera aqueles erros estranhos em softwares e afins, além daquelas imbecilidades que nós somos obrigados a aguentar quando damos manutenção em programas que, quando perguntamos pelo autor, ninguém sabe que foi (ou quem fui). Lembra do "Códigos que eu tenho que aguentar"?.

    Mensagens de erro pra lá de estúpidas
    Parece que alguém sabe contar

    Putz, eu poderia ter feito um post sobre a disponibilização para downloads dos PPTs das apresentações do PDC, assim pareceria um post menos inútil. Agora já foi, estou com preguiça de reescrever...

    Em 19/09/2005 21:15 - Comentários (0)

    Usando Win32 API para otimizar o I/O, parte 6

    Parte Zero     Parte 1     Parte 2     Parte 3     Parte 4     Parte 5     Fontes do parser

    Depois de turbulências, mudanças de endereço e besteiras, é hora de dar continuidade à série sobre otimização de desempenho de I/O. Nos nossos exemplos, eu usei um interpretador de IDL que deve ler o conteúdo do arquivo em questão e vários imports, para mostrar o grau de otimização que é possível usando a API Win32 ao invés da runtime do C++.

    Só para esclarecer: a runtime do C++ não é lenta, é até que bastante rápida. O grande negócio é que qualquer abstração deixa o código mais lento, já que são mais camadas intermediárias para fazer o que realmente se quer, e mais código será rodado para fazer a mesma coisa. É esse o motivo pelo qual, na esmagadora maioria das vezes, um programa feito em Visual C++ é bem mais rápido do que um feito em .NET ou Java. Além de abstrair a API nativa do sistema operacional - que a runtime do C++ também faz - o Java e o .NET abstraem também a arquitetura do computador onde o programa está rodando. A API nativa do Windows é a Win32, qualquer coisa que não use a API diretamente (VB6, Delphi, Java, .NET, [coloque-algo-que-não-seja-C-e-C++-puros-aqui] fica sempre mais lento do que usar a API diretamente. O fato é que as vezes a diferença é muito pequena (como no caso do Delphi) e não vale a pena o trabalho de usar Win32 (que não é pequeno). Na realidade, hoje todo mundo usa alguma abstração sobre Win32, seja ela uma grande abstração (.NET) ou uma pequena abstração (MFC).

    Nessa nossa otimização, iremos usar um recurso do Windows chamado File Mapping. Esse recurso permite mapear um trecho de um arquivo diretamente na memória, e a medida que a memória é lida, o conteúdo do arquivo é colocado nela automaticamente. Para o programa, é como se o conteúdo do arquivo já estivesse na memória, mas na realidade o arquivo é lido a medida que o programa acessa essa memória. Isso evita buffers intermediários, e no final das contas, é mais simples do que alocar memória, ler o arquivo e depois tratar.

    O File Mapping é implementado em kernel mode como um recurso chamado Section. Quando você mapeia um trecho do arquivo na memória (usando MapViewOfFile em user mode), o arquivo não é lido para memória na hora. As páginas de memória são marcadas como inválidas, o que faz com o que sejá disparada uma exceção quando essa memória é acessada. Essa exceção é tratada pelo Memory Manager, que faz a leitura do trecho acessado do arquivo para essa memória, marca a página como válida e retorna o controle para o programa, que agora terá o conteúdo esperado do arquivo nessa memória. O File Mapping também pode ser usado para compartilhar memória entre os programas. O Windows usa esse recurso para carregar executáveis e DLLs na memória, o que faz com que o conteúdo de uma DLL só seja carregado uma vez para todos os processos que estão usando-a.

    Para simplificar o uso do File Mapping no nosso interpretador e para isolar um pouco o código Win32 do nosso código, criei uma classe simples para tratar o File Mapping:

    //
    // Classe que mapeia um arquivo inteiro na memória
    // usando FileMapping
    //
    // Rodrigo Strauss - http://www.1bit.com.br
    //
    class Win32FileMapping
    {
       HANDLE m_hFile, m_hFileMapping;
       DWORD m_dwFileSize;
       void* m_p;
    
       void Clean()
       {
          m_hFileMapping = NULL;
          m_hFile = INVALID_HANDLE_VALUE;
          m_p = NULL;
          m_dwFileSize = 0;
       }
    
    public:
       Win32FileMapping()
       {
          Clean();
       }
    
       ~Win32FileMapping()
       {
          Free();
       }
    
       bool IsValid()
       {
          return m_p && m_hFileMapping && m_hFile != INVALID_HANDLE_VALUE;
       }
    
       void Free()
       {
          if(m_p)
             UnmapViewOfFile(m_p);
    
          if(m_hFileMapping)
             CloseHandle(m_hFileMapping);
    
          if(m_hFile != INVALID_HANDLE_VALUE)
             CloseHandle(m_hFile);
    
          Clean();
       }
    
       //
       // Essa função mapeia o arquivo inteiro na memória
       //
       bool MapFile(const char* fileName)
       {
          Free();
    
          m_hFile = CreateFile(fileName, 
                               GENERIC_READ, 
                               FILE_SHARE_READ, 
                               NULL, 
                               OPEN_EXISTING, 
                               NULL, 
                               NULL);
    
          if(m_hFile == INVALID_HANDLE_VALUE)
             return false;
    
          m_dwFileSize = GetFileSize(m_hFile, NULL);
    
          m_hFileMapping = CreateFileMapping(m_hFile, 
                                             NULL,
                                             PAGE_READONLY, 
                                             0,
                                             0, 
                                             NULL);
    
          if(m_hFileMapping == NULL)
        {
         Free();
             return false;
        }
    
          m_p = MapViewOfFile(m_hFileMapping, FILE_MAP_READ, 0, 0, 0);
    
          return true;
       }
    
       template<typename T>
       T GetStart()
       {
          return reinterpret_cast<T>(m_p);
       }
    
       template<typename T>
       T GetEnd()
       {
          if(!m_dwFileSize)
             return NULL;
    
          return reinterpret_cast<T>( ((unsigned char*)m_p) + m_dwFileSize );
       }
    };
    

    Essa classe mapeia o arquivo inteiro na memória, e retorna os ponteiros de início de de fim através das funções template GetStart() e GetEnd(). Note que o GetEnd() retorna um ponteiro para o primeiro T depois do fim do arquivo. Esse o mesmo conceito usado pelos containers STL, onde o end() retorna um elemento inválido após o último item. O último item de um container é [container.end() - 1] (se, e somente se container.begin() != container.end())

    O boost::tokenizer tem duas opções de inicialização: passar uma string com o conteúdo a ser interpretado, ou um iterator inicial e um iterator final. No nosso caso, o iterator inicial é GetStart() e o final GetEnd(). Não se esqueça que em STL, o conceito iterator é uma generalização de um ponteiro: um objeto que aponte para outro objeto (ou seja, sobrecarregue o operador *) e que (não necessariamente) possa ser incrementado e decrementado (operadores ++ e --) para acessar outros itens (para mais detalhes veja a documentação no site da SGI). Então um ponteiro É um iterator, o que resolve o nosso problema de uma forma bem simples.

    Vamos ao código (é por isso que você está lendo isso, não é?):

    void MidlParser::ParseMidlFile(const char* fileName)
    {
       ...
       Win32FileMapping fileMapping;
    
    
       if(m_bParseAsImportFile)
       {
          //
          // tenta abrir o imported file nas pastas de include
          //
        for(vector<string>::const_iterator i = m_IncludePaths.begin() ;
                i != m_IncludePaths.end() ; 
                ++i)
          {
             string str;
    
             str = *i + fileName;
    
             fileMapping.MapFile(str.c_str());
    
             if(fileMapping.IsValid())
                break;
          }
    
          if(!fileMapping.IsValid())
             throw ParseException(string("error opening import file \"") 
           + fileName + "\"", *this, m_parsedFileName);
    
          //
          // inicializa o tokenizer com os ponteiros do FileMapping
          //
          m_Tokenizer.assign(fileMapping.GetStart<char*>(),
                             fileMapping.GetEnd<char*>(),
                             char_separator<char>("\t\r " , "\n\"*,;:{}/\[]()"));
    
    
          m_ParsedIncludeFiles.push_back(fileName);
    
       }
       else
       {
          fileMapping.MapFile(fileName);
          
          if(!fileMapping.IsValid())
             throw ParseException(string("error opening file \"") 
           + fileName + "\"", *this, m_parsedFileName);
    
          //
          // inicializa o tokenizer com os ponteiros do FileMapping
          //
          m_Tokenizer.assign(fileMapping.GetStart<char*>(),
             fileMapping.GetEnd<char*>(),
             char_separator<char>("\t\r " , "\n\"*,;:{}/\[]()"));
    
          m_parsedFileName = fileName;
    
          ...
       }
    }
    

    Como visto, nosso código não ficou mais complicado por causa do File Mapping. Nossa classe facilitou muito e deixou o código claro, além da inteligente decisão de fazer uma classe simples que resolvesse nosso problema pontual ao invés de tentar fazer um framework genérico para uso avançado de File Mapping. Um péssimo costume de "programadores orientados à objetos" é fazer classes super genéricas que resolvam todas as situações possíveis e imagináveis naquela área (eu sei bem porque eu era assim...).

    Chega de yada-yada, vamos aos números:

    Média de tempo (50 chamadas):

    228,01 ms - versão usando a runtime do C++
    214,68 ms - versão usando CreateFile
    204,17 ms - versão usando CreateFile com FILE_FLAG_SEQUENTIAL_SCAN
    186,55 ms - versão usando File Mapping

    Comparação entre a implementação com File Mapping e as anteriores
    22,22% - Melhoria em relação versão que usa a runtime do C++ (versão inicial)
    15,08% - Melhoria em relação versão que usa CreateFile
    09,44% - Melhoria em relação versão que usa CreateFile com FILE_FLAG_SEQUENTIAL_SCAN

    Nossa melhoria em relação à versão inicial foi de 22%, e as mudanças foram pequenas e pontuais. Nessa última tentativa, fizemos a classe de File Mapping (menos de 90 linhas de código e anos de estudo de C++ e Win32) e modificamos o código que carrega o arquivo (menos de 10 linhas de código e mais de 10 livros de programação depois). O nosso programa de exemplo não é tão I/O intensive, mas nossa otimização melhorou bastante o desempenho em termos pencentuais.

    Mesmo assim, há algo para se pensar: será que valeu a pena? Essa série foi interessante para ilustrar conceitos de otimização de performance e Win32, mas agora chega a hora de explicar mais um conceito: comparação percentual não é suficiente para fazer um estudo sobre uma determinada otimização. 22% é uma otimização considerável, mas e em termos absolutos? Será que 41,46 milisegundos é algo que faça diferença no tempo de compilação? Nosso caso o esforço de programação foi pequeno (o de escrever os posts foi imensamente maior), mas mesmo assim deve ser levado em consideração.

    Quando for mensurar alguma otimização, considere TODOS os parâmetros, inclusive os intangíveis, como o impacto que essa otimização causará no usuário. No caso do nosso interpretador, talvez ~40ms não faça tanta diferença. Mas em uma chamada de componente que roda no backend de uma bolsa de valores e que é chamado milhões de vezes em um único dia, 5ms faz uma diferença MUITO grande.

    Lembre-se: programação é uma arte.

    Em 27/09/2005 15:44 - Comentários (2)

    Quando a Microsoft quer portabilidade, ela usa C++

    O Windows Presentation Foundation - antigo Avalon - ou WPF é a novo "subsistema" de apresentação do Windows. Metade dele foi feito em C#, com o backend baseado em DirectX para conseguir a perfomance necessária. As novidades são muitas. Além de ser managed, essa plataforma possibilita o design declarativo usando XAML (um XML que descreve as janelas/canvas/controles, algo como o SVG), permite integração de vídeo, animação, gráficos vetoriais e possui um sistema de roteamento de eventos entre os controles bem interessante. O WPF será nativo no Windows Vista (antigo "code-named Longhorn") e haverá uma versão que rodará em Windows XP SP2 e Windows Server 2003 SP1.

    No PDC 2005, foi apresentada uma versão do WPF chamada WPF/E, que é a "Windows Presentation Foundation Everywhere". Essa é a versão portável da WPF, que a Microsoft disponibilizará para outras plataformas. Na demostração dessa versão (reduzida) do WPF, o palestrante usou browser Safari, em um Mac, e abriu tranquilamente um arquivo XAML.

    O detalhe interessante é que o WPF/E foi implementada em C++, e não em C#. Afinal, ele tem que ser portável...

    Em 30/09/2005 15:27 - Comentários (0)

    Chernobyl: O retrato do limite da estupidez humana

    Meu amigo Gilberto me mandou um link com a linha de tempo dos eventos que causaram o acidente de Chernobyl. Eu já tinha lido sobre o acidente - inclusive sobre a moça que percorreu a cidade fantasma - mas nunca uma descrição que mostrasse o grau de estupidez que foi necessária para que o acidente acontecesse.

    O acidente aconteceu durante um teste para ver se o gerador, em baixa potência, conseguia se retroalimentar o suficiente para se resfriar, no caso dos geradores de backup demorarem para entrar em funcionamento. Os operadores e engenheiros fizeram algo como "desliga o tratamento de erro, log e backup que nós vamos fazer um teste rápido em produção". Detalhe: era uma usina nuclear, e não um servidor de produção. O trecho que deixa mais evidente os procedimentos adotados é o seguinte:

    "O operador tentava, sem êxito, por meio de controles manuais, manter os parâmetros para o reator poder funcionar com segurança. A pressão de vapor e o nível da água caíram abaixo do permitido, fazendo soar os alarmes que exigiam o desligamento do reator. O operador desligou o próprio sistema de alarme."

    Eles fizeram o equivalente em C++ (que eu já vi MUITO por aí) a:

    int main
    {
      try
      {
        // um monte de código
      }
      catch(...)
      {
        // nada aqui
      }
      return 0;
    }
    

    Isso serve para que todos lembrem:

  • Sistemas de segurança devem estar ativados, sempre. Se seu sistema tem uma contingência, ela deve receber o mesmo - ou até mais - cuidado do que o sistema principal. Quando algo der errado e seu sistema de segurança ou contingência não funcionar, a probabilidade de algum desesperado fazer uma besteira é muito maior.
  • O backups devem estar sempre ativados e funcionando, pois você nunca sabe quando precisará deles. O mais importante: teste a recuperação do seu backup e se ele funciona em situações extremas. Em Chernobyl, quando eles ativaram o sistema de segurança para desabilitar tudo, descobriram que esse sistema não funcionava em situações extremas.
  • Nunca pense que o pior não vai acontecer. Todo elevador (e toda usina nuclear) tem um sistema de segurança simples e muito eficiente que evita que ele caia caso o cabo arrebente. Você só ouve estórias de elevadores que cairam porque algum técnico imbecil achou que não haveria problema em deixar o freio de segurança desligado por alguns dias. Não deixe o no-break do seu servidor sem bateria "por alguns dias", você pode se arrepender.
  • Ou, como o Gilberto disse, "foi genial, eles deveriam abrir uma empresa de software". Aí sim eles estariam no ramo certo...

    Em 04/10/2005 19:47 - Comentários (5)

    Explicando a sopa de letrinhas da programação C/C++ para Windows: COM

    Outros posts dessa mesma série:   Win32     ATL     MFC

    COM - sigla para Component Object Model - é um padrão binário de componentes que foi criado para facilitar o uso de objetos entre as diversas linguagens de programação. Sendo uma das tecnologias mais importantes desenvolvidas pela Microsoft, ela é usada em praticamente todas as partes do Windows e em quase todos os produtos da Microsoft.

    Os pilares básicos desse tecnologia são o acesso aos objetos a partir de interfaces, uma interface padrão (IUnknown) para controle de referência e acesso às outras interfaces (da qual todas as interfaces criadas devem herdar), e o padrão binário da vtable (tabela com os ponteiros para as funções da interface) para que os objetos feitos em diversas linguagens possam se comunicar entre si. Para um objeto ser chamado de objeto COM, ele só precisa seguir essas premissas. Quando a implementação é em C++, isso que dizer que o objeto deve herdar de IUnknown e implementar seu três métodos:

    interface IUnknown
    {
        HRESULT QueryInterface(REFIID riid,[out]void **ppvObject);
        ULONG AddRef();
        ULONG Release();
    };
    

    No seu uso mais comum, além das premissas que eu já enumerei, é necessário registrar o objeto e o objeto que sabe criá-lo (objeto que implementa a interface IClassFactory). Entre as linguagem compatíveis com COM estão: C, C++, Delphi, VBScript, JScript, Visual Basic 4/5/6, todas as linguagens .NET (C#, VB, etc), PHP, Python, Perl e outras.

    O COM serve de base para diversas outras tecnologias criadas pela Microsoft, que veremos mais a frente. As tecnologias baseadas em COM são geralmente nada mais são do que uma lista de interfaces COM que determinam a comunicação entre os diversos componentes (de um modo geral, não necessariamente componentes COM). Para ser um COM, um objeto só precisa implementar a interface IUnknown. Para ser um controle ActiveX (ActiveX é uma das tecnologias baseadas em COM), um objeto precisa implementar IUnknown e mais as interfaces específicas da tecnologia ActiveX. Veja algumas tecnologias que usam COM como base:

    • ActiveX: Usada para criar controles (ActiveX control) que tem interação com o usuário. Seu uso é muito comum no Visual Basic 6 e anteriores.O próprio Internet Explorer nada mais é do que um programa que hospeda o controle ActiveX de renderização de HTML da Microsoft (mshtml.dll).
    • OLE: Object Linking and Embedding é a tecnologia de troca de dados entre aplicativos que sucedeu o DDE. Quando você copia um desenho no CorelDRAW! e cola no Microsoft Word, toda a comunicação entre eles para que isso seja possível é feita usado OLE. Nesse exemplo específico, o Microsoft Word chama um objeto COM/OLE do CorelDRAW! solicitando que ele desenhe o objeto para que o Word possa exibí-lo.
    • Structured Storage: Parte do OLE, permite que um arquivo possa guardar informações de vários programas. Voltando ao nosso exemplo anterior, quando o usuário salva um documento Microsoft Word que contém um gráfico do CorelDRAW!, o Word faz uma chamada para um objeto COM/OLE do CorelDRAW!, que salva o seu desenho dentro do arquivo DOC do Word. Quando o documento é carregado, ocorre o inverso.
    • MMC: O Microsoft Management Console é uma iniciativa da Microsoft de padronização da aparência e funcionamento das ferramentas de gerenciamento do Windows. O Snapins do MMC nada mais são do que objetos COM que implementam as interfaces específicas para se comunicar com o MMC. Para entender melhor, vá em "Start Menu" >> "Run" ("Menu Iniciar" >> "Executar" para alguns), digite "compmgmt.msc". Tudo que você vê aí é implementado em COM.
    • Windows Shell: A API para usar os recursos do Windows Shell (basicamente Windows Explorer) é exportada como objetos COM. Já mostrei antes um exemplo de como mudar a imagem de fundo do desktop usando o objeto COM do ActiveDesktop.
    • Shell Extensions: Quando você instala o WinZip ou WinRAR, eles adicionam ao menu de contexto do Windows Explorer opções para manipulação de arquivos ZIP ou RAR e para compactar arquivos comuns. Quem controla isso são objetos COM chamados pelo Explorer e registrados pelos respectivos aplicativos.
    • Internet Explorer: Todas as barras de ferramentas personalizadas do Internet Explorer (como as feitas pelo Yahoo! e Google) são objetos COM. Na realidade, todas as extensões feitas ao Internet Explorer são objetos COM, seja barra de ferramentas, um Pane (se você usa IE, use o CTRL+H para descobrir o que é um Pane), seja um Browser Helper Objects.
    • Automation: É a possibilidade de controlar um aplicativo usando objetos COM exportados por ele. Entre os aplicativos que suportam automation estão todos do pacote Office (Word, Excel, etc). As extensões para aplicativos feitas em VBA nada mais são do que programas VB(A) que usam esses objetos Automation para controlá-los. Hoje em dia é possível fazer em .NET o que se fazia com o VBA, mas os objetos .NET nada mais fazem do que repassar as chamadas para os objetos COM.
    • DirectX: O DirectX é 100% implementado como objetos COM.
    • Microsoft Office Addins, Extensions: Todos os Addins e extensões para o Office são feitos em COM, inclusive exemplos conhecidos como a intergração do Google Desktop Search e do MSN Desktop Search com o Outlook. Hoje é possível fazê-los em .NET, já que a Microsoft provê objetos .NET que encapsulam os objetos COM do Office (ou seja, no final das contas, é tudo COM).

    Além dessas tecnologias que eu detalhei, existem várias outras, como WMI, Scripting, ASP, Windows Media Player e outros.

    Eu tinha preparado uma lista de programas Microsoft ou módulos do Windows que usam COM (Windows Explorer, Internet Explorer, Office, Exchange, SQL Server, ISA, Visual Studio, etc, etc, etc). Depois cheguei a conclusão que é mais fácil listar os módulos que não usam COM: o kernel do Windows (que é feito em C, não em C++), o Bloco de Notas e a Calculadora. Alguém conhece mais algum que não use nada de COM?

    Em 16/10/2005 16:15 - Comentários (7)

    Meu apelo: tirem seus blogs do TheSpoke

    Faço aqui meu apelo para que os blogger brasileros tirem seus blogs do TheSpoke e mudem-se para um site melhor. O TheSpoke novo consegue ser pior do que a primeira versão (que já era bem ruim), e os responsáveis fizeram uma péssima migração . Aqui vão meus motivos:

    • É necessário efetuar login para fazer comentário. É necessário efetuar login para votar em uma pesquisa. É necessário efetuar login se você clica em "Community" >> "Members". Isso é um portal de blogs (e coisas inúteis como fotos) ou um clubinho fechado?
    • Eu não gosto da navegação do site, acho confusa demais. Você clica em "Community" >> "Blogs" e vai para uma lista de posts, não para uma lista de blogs. Estando em um post (como esse), onde eu clico para chegar na página principal do blog? Se eu clico no username do blogger ele me pede senha. Grrr...
    • Ele é muito mal feito. Eu estava tentando colocar um comentário e não lembrava se já criei um username. Tentei criar um usuário e inocentemente pensei que, se eu já tivesse criado um usuário rodrigostrauss, ele avisaria. A mensagem de erro foi "An error occured while attempting to create this user". Já vi muito estagiário fazer melhor.
    • A função de search é muito ruim. Procurando "Alfred Gary Myers", por exemplo, não retorna nenhum resultado. Eu ainda não achei uma forma de encontrar um blog lá sem ser pelo Google. E depois da migração, nem o Google está encontrando.
    • Já ví vários "Error - We are currently unable to serve your request". Só faltou complementar a mensagem com "Bad, bad server. No donut for you".
    • O RSS é resumido. Eu gosto de ler os posts no meu leitor de blogs, e não ter que clicar e ir até o site. Está faltando dinheiro para pagar bandwidth ou foi só uma péssima idéia mesmo?

    Já me falaram que é bom ter blog no TheSpoke por causa da visibilidade. Que visibilidade se é impossível encontrar alguma coisa lá e acho que o Google não se entendeu direito com ele? Eu acho que não vale a pena passar por tudo isso. É só escrever alguma coisa interessante (como código) que o próprio Google se encarrega de te dar visitantes, mesmo fora de um portal de blogs (como é o meu caso).

    Ok, o TheSpoke2 tem uma vantagem em relação ao primeiro. Não vi mais aquelas pesquisas do tipo "Qual o sentido da vida?" na primeira página.

    Em 19/10/2005 15:29 - Comentários (4)

    Tem gente que veste a camisa da empresa

    Lembra quando eu falei que eu não visto a camisa da empresa? Pois é, tem muita gente que veste...

    Em 20/10/2005 14:57 - Comentários (0)

    Explicando a sopa de letrinhas da programação C/C++ para Windows: ATL

    Outros posts dessa mesma série:   Win32     COM     MFC

    ATL - Active Template Library - é uma biblioteca de templates C++ criada pela Microsoft para simplificar a programação de objetos COM/OLE/ActiveX em C++. Ela foi criada inicialmente pela equipe do Visual Basic para simplificar o desenvolvimento interno, já que o VB até a versão 6 é todo baseado em COM. Hoje em dia ela é distribuída junto com todas as versões do Visual C++. É uma biblioteca pouco intrusiva, implementada em camadas e que tem um overhead muito pequeno, por ser baseada em templates. É usada pela Microsoft internamente em seus produtos, como o Windows Explorer, Windows Movie Maker, MMC e vários outros (ao contrário da MFC, que é pouco usada dentro da Microsoft).

    O desenvolvimento COM em C++, apesar de não ser muito complicado, é trabalhoso. Muitas interfaces que precisam ser implementadas contém muitas funções cujo código de implementação é o mesmo para todos os componentes, o que torna o trabalho chato e repetitivo. A implementação da interface IUnknown, por exemplo, é sempre a mesma: controle de referência e solicitação das interfaces suportadas.

    Além de suportar a implementação de objetos COM, a ATL é uma biblioteca com diversas classes e templates que facilitam muito a programação Windows, como classes para acesso ao registro, comunicação HTTP e SMTP, criptografia, BASE64, acesso à arquivos, ACLs, listas e hashmaps, etc.

    Como um trecho de código vale muito mais do que 186.112.794 palavras, veja como é implementado um objeto COM e seu Class Factory em C++ puro:

    //
    // interface do nosso objeto COM
    //
    __interface __declspec(uuid("977BF132-B6B6-4d70-88BD-C427A2724B48"))
    ITest : IUnknown
    {
       HRESULT WINAPI Method1(BSTR str, ULONG ul);
    };
    
    //
    // Objeto que implementa a class ITest
    //
    class CTest : public ITest
    {
       DWORD m_ref;
    public:
       CTest()
       {
          m_ref = 0;
       }
    
       //
       // implementação de IUnknown
       //
       STDMETHOD(QueryInterface)(REFIID riid, void **ppvObject)
       {
          if(InlineIsEqualUnknown(riid))
          {
             AddRef();
             *ppvObject = static_cast<IUnknown*>(this);
             return S_OK;
          }
          else if(InlineIsEqualGUID(riid, __uuidof(ITest)))
          {
             AddRef();
             *ppvObject = static_cast<ITest*>(this);
             return S_OK;
          }
          
          return E_NOINTERFACE;
       }
    
       STDMETHOD_(ULONG,AddRef)()
       {
          return ++m_ref;
       }
    
       STDMETHOD_(ULONG,Release)()
       {
          DWORD ref = --m_ref;
    
          if(ref == 0)
             delete this;
    
          return ref;
       }
    
       //
       // implementação de ITest
       //
       STDMETHOD(Method1)(BSTR str, ULONG ul)
       {
          MessageBoxW(NULL, str, L"", MB_ICONEXCLAMATION);
          return S_OK;
       }
    };
    //
    // Class Factory para o nosso obejto
    //
    class CTestClassFactory : public IClassFactory
    {
       DWORD m_ref;
    public:
       CTestClassFactory()
       {
          m_ref = 0;
       }
    
       //
       // quando o objeto é registrado, a runtime do Microsoft COM 
       // chama a função GetClassObject exportada pela DLL do objeto.
       // como vamos fazer tudo na mão agora, vamos criar esse helper
       //
       static HRESULT CreateClassFactory(REFIID riid, void **ppv)
       {
          HRESULT hr;
          IUnknown* p;
    
          try
          {
             p = new CTestClassFactory();
          }
          catch(...)
          {
             return E_OUTOFMEMORY;
          }
    
          p->AddRef();
    
          hr = p->QueryInterface(riid, ppv);
    
          p->Release();
    
          return hr;
       }
    
       //
       // implementação do IClassFactory
       //
       STDMETHOD(CreateInstance)(IUnknown *pUnkOuter, REFIID riid, void **ppvObject)
       {
          HRESULT hr;
          IUnknown* pUnk;
    
          if(pUnkOuter)
             return CLASS_E_NOAGGREGATION;
    
          try
          {
             //
             // pelo padrão C++, se o new falha uma exceção é disparada
             //
             pUnk = new CTest();
          }
          catch(...)
          {
             return E_OUTOFMEMORY;
          }
          
          pUnk->AddRef();
    
          hr = pUnk->QueryInterface(riid, ppvObject);
    
          
          pUnk->Release();
    
          return hr;
       }
       STDMETHOD(LockServer)(BOOL fLock)
       {
          return S_OK;
       }
    
       //
       // implementação de IUnknown
       //
       STDMETHOD(QueryInterface)(REFIID riid, void **ppvObject)
       {
          if(InlineIsEqualUnknown(riid))
          {
             AddRef();
             *ppvObject = this;
             return S_OK;
          }
          else if(InlineIsEqualGUID(riid, IID_IClassFactory))
          {
             AddRef();
             *ppvObject = static_cast<IClassFactory*>(this);
             return S_OK;
          }
    
          return E_NOINTERFACE;
       }
    
       STDMETHOD_(ULONG,AddRef)()
       {
          return ++m_ref;
       }
    
       STDMETHOD_(ULONG,Release)()
       {
          DWORD ref = --m_ref;
    
          if(ref == 0)
             delete this;
    
          return ref;
       }
    };
    
    //
    // E no sétimo dia Deus disse: "int main"
    //
    int main()
    {
       HRESULT hr;
       BSTR bstr;
       IClassFactory* pClassFactory;
       ITest* pTest;
    
       //
       // criando o class factory
       //
       hr = CTestClassFactory::CreateClassFactory(IID_IClassFactory, (void**)&pClassFactory);
       if(FAILED(hr))
          return hr;
    
       //
       // solicitando gentilmente para que ele crie um objeto daquele tipo
       //
       hr = pClassFactory->CreateInstance(NULL, __uuidof(ITest), (void**)&pTest);
       if(FAILED(hr))
       {
          pClassFactory->Release();
          return hr;
       }
    
       //
       // usando o objeto
       //
       bstr = SysAllocString(L"Uma string bem legal");
       
       hr = pTest->Method1(bstr, 20);
       
       SysFreeString(bstr);
    
       if(FAILED(hr))
       {
          pClassFactory->Release();
          pTest->Release();
          return hr;
       }
    
       //
       // liberar as interfaces
       //
       pClassFactory->Release();
       pTest->Release();
    
    
       return S_OK;
    }

    Lembre-se, a única coisa realmente útil para nós nesse código é a implementação de ITest::Method1. O resto é tudo suporte ao contador de referências que todo objeto COM deve ter. Com ATL, nossa implementação seria assim:

    class ATL_NO_VTABLE CTest1 : 
      public CComObjectRootEx<CComSingleThreadModel>,
      public CComCoClass<CTest1, &CLSID_Test1>,
      public ITest1
    {
    public:
    
       DECLARE_REGISTRY_RESOURCEID(IDR_TEST1)
    
       BEGIN_COM_MAP(CTest1)
           COM_INTERFACE_ENTRY(ITest1)
       END_COM_MAP()
    
    public:
    
       STDMETHOD(Method1)(BSTR p1, DWORD p2)
       {
          MessageBoxW(NULL, str, L"", MB_ICONEXCLAMATION);
          return S_OK;
       }
    };
    
    int main()
    {
       HRESULT hr;
       CComPtr<ITest1> pTest;
       
       hr = CTest1::CreateInstance(NULL, &pTest);
       if(FAILED(hr))
          return hr;
    
       hr = pTest->Method1(CComBSTR(L"Uma string muuuuito mais legal"), 150);
       if(FAILED(hr))
          return hr;
    
       //
       // "olhe mamãe, eu sei coletar meu próprio lixo"
       //
    
       return S_OK;
    }

    Explicação: STDMETHOD nada mais é do que uma macro que coloca o retorno da função com HRESULT (padrão de retorno de erros COM) e coloca o calling convention como __stdcall, o mesmo das APIs do Windows.

    Salvamos algumas dezenas de linhas de código usando ATL, já que ele tem uma implementação para IUnknown (single threaded e multi threaded, a nossa é só single) e um Class Factory, além de toda a implementação para que o nosso objeto seja registrado e usado por qualquer cliente COM, seja VB6, .NET, Delphi, etc. Se estivessemos fazendo um servidor OLE, a quantidade de código boilerplate que deixaríamos de escrever seria da ordem de centenas de linhas. E com a elegância e leveza que só o ATL tem :-)

    Em 25/10/2005 03:55 - Comentários (3)

    Por que sempre tentamos tudo do jeito mais difícil?

    Eu sou um leitor assíduo do Slashdot (para mais informações veja o artigo sobre o Slashdot na Wikipedia), aponto meu browser para lá algumas dezenas de vezes por dia. Uma das partes do Slashdot que eu mais gosto é a "Ask Slashdot", onde alguém com um problema de TI para resolver pergunta aos leitores e eles entopem os comentários de repostas (úteis e inúteis). Eu já aprendi várias coisas lendo o Ask Slashdot e passei a conhecer vários softwares e soluções que eu nem sabia que existiam. Alguns funcionários de empresas gigantescas, que já tiveram problemas gigantescos e souberam como resolver colocam suas experiências lá quase sempre, além de acadêmicos, pesquisadores e outros escovadores de bit de alto nível.

    Um dos "perguntadores" de hoje disse que precisa criar um sistema de storage (armazenamento) com até 1 Petabyte de capacidade (~1024 TB, ~1024*1024 GB) com hardware barato e comum, sem gastar muito dinheiro. Além disso, ele disse que no começo não precisava de total segurança dos dados (acho difícil alguém ter 1 PB de dados e não precisar de backup, mas tudo bem...).

    Estou rindo até agora com um comentário que resume bem o pedido dele e de dezenas de pessoas que recorrem diariamente ao Ask Slashdot:

    Dear Slashdot,
    I have been tasked with (insert very difficult, very important job). This is very important to my company. I have (insert number much lower than it should be) dollars to do this. I do not want to use (insert company name specializing in this exact thing) because management thinks they are too expensive. I think I can do this (insert better/faster/cheaper/...) than said company, even though they have vastly more experience and have invested much more time and research than I have. My continued and future employment probably rests on this project. Please advise.

    Por que sempre tentamos fazer as coisas do jeito mais difícil? Ou melhor, por que as pessoas que administram as empresas sempre pedem para que façamos as coisas do jeito mais difícil?

    Em 26/10/2005 01:09 - Comentários (3)

    Mais um blog brasileiro sobre C++

    Wanderley Caloni Jr - meu velho companheiro de escovação de bits e especialista em padrão C e C++ - resolveu criar vergonha na cara e compartilhar seus conhecimentos com a humanidade. Ele começou a escrever um blog, onde além de falar sobre C e C++, ele fala também sobre o Borland C++ Builder, o único e verdadeiro ambiente RAD para C++ na plataforma Windows. Eu venho acompanhando os post sobre Builder desde o começo, é muito bom respirar ares não-Microsoft de vez em quando (considerando que estou estudando Python, está virando "de vez em sempre"). Além disso o nome Borland me faz lembrar o "Borland Turbo C++ for DOS", nos meus felizes primeiros dias de estudo C++...

    O blog dele é uma ótima leitura, porque além de falar sobre C++, ele escreve código no blog - coisa bem escassa no Brasil....

    Em 27/10/2005 14:58 - Comentários (6)

    Visual Studio 2005 já disponível

    O Visual Studio 2005, versão final, já está disponível para download aos assinantes MSDN em http://msdn.microsoft.com/subscriptions/.

    Visual Studio 2005 significa:

    E a nova MSDN é compatível com o Firefox, o que é muito bom. Ah, e claro, tem também o .NET Framework 2.0 e o C# 2.0.

    Em 27/10/2005 20:54 - Comentários (4)

    Vista a camisa da empresa se puder

    Em um dos meus últimos posts eu coloquei um link para meu post "Eu não visto a camisa da empresa", e acabei por consequência revivendo-o. (Blogs têm esse problema de navegação, os post antigos ficam adormecidos até que alguém os ache pelo Google ou que você coloque um link para ele). Esse post, juntamente com o "Os contos que as empresas contam" gerou bastante discussão sobre o relacionamento entre funcionário/consultor e empresa e a postura de cada um, já que no post eu coloquei qual foi a postura que eu adotei depois das pouco mais de 10 empresas onde eu trabalhei com informática desde que eu tinha 15 anos, que foi quando eu comecei.

    Não sou muito de re-explicar meus posts, mas um comentário colocado no post "Eu não visto a camisa da empresa" me fez escrever uma resposta tão grande que eu resolvi transformar em post, por dois motivos. Um é para dar oportunidade de gerar mais discussão sobre o tema. O outro é que eu acho que o respeito com que o Daniel abordou o tema merece uma resposta e uma consideração. Da mesma forma que o mais comum é as empresas não respeitarem seus funcionários, o mais mais comum é não ter muito respeito nos comentários quando a opinião não é igual a minha. Recomendo que você leia o comentário do Daniel antes de ler o resto desse post.

    Segue aqui minha meio-resposta-meio-post-meia-calabresa:

    Muito do que eu escrevo aqui no site são coisas que eu gostaria de ter lido quando eu tinha menos experiência no mercado, mas ninguém escrevia. Exemplos claros são os FAQs sobre C++, Win32, COM, ATL, etc. Muitas pessoas não estudam coisas que gostariam porque não sabem por onde começar. Então eu tento dar um começo para as pessoas, como em "Como ser um programador".

    Sobre o posts "Eu não visto a camisa da empresa" e "Os contos que as empresas contam", eu quero passar para as pessoas as más experiências que eu passei, para que elas tentem perceber essas situações quando elas estiverem acontecendo. Eu já passei por várias situações (e empresas) como as que eu citei. Infelizemente o mundo não está cheio de pessoas honestas, e eu conheço muita gente que já passou por essas situações. Existem MUITAS empresas de software que não dão o menor valor para seus programadores, MUITAS mesmo. Como já disse o Joel, tratar cientistas de foguete como crianças é uma péssima idéia. Não se esqueça que essas "crianças" podem colocar o curriculum na APINFO e desaparecer em uma semana. Quem mais perde com isso são as empresas e seus super-administradores.

    Existem também pessoas que têm sorte e conseguem trabalhar em uma empresa que dá valor aos seus funcionários e onde o ambiente de trabalho é bom. Essas pessoas podem e devem vestir a camisa da empresa, afinal, a empresa veste a camisa dos funcionários. Foi exatamente isso que eu disse. Algumas partes do post podem ser "radicais", mas eu explico meu ponto de vista em várias pontos, como "não visto a camisa de empresa nenhuma pelo simples fato de que nenhuma empresa até hoje vestiu a minha camisa". O motivo é simples. Quando vestirem, eu visto também. Eu já cansei de me dedicar sem receber nada em troca. Eu, como qualquer programador normal, só peço que me deixem programar em paz.

    As pessoas com mais tempo de mercado e mais vivência não precisam muito das informações que eu tentei passar nesses posts. Mas quem está começando precisa. Eu precisava quando comecei e não tive. Eu e muita gente que eu conheço foi iludida. E isso não é muito difícil, porque eu gosto muito do que faço e uma promessa de um projeto interessante muitas vezes vale mais pra mim do que uma promessa de aumento de salário.

    O Daniel cita no comentário que "nenhum trabalhador (em qualquer area) que possa se dizer dedicado e concentrado, tem tempo pra ficar lendo noticias do Slashdot "dezenas de vezes por dia"". Eu discordo e vou explicar o motivo. Slashdot fala sobre tecnologia, sobre meu trabalho. Todo economista DEVE ler muito sobre economia. Se um programador passa o dia lendo sobre esportes (conheço alguns assim), aí sim é um problema. Muitas das coisas que eu sei hoje se devem ao Slashdot, CNET, OSNews, MSDN Magazine, The Register, ArsTechnica, aos blogs que eu leio (Raymond Chen, Joel Spolsky, Eric Lippert, Mark Russinovich, etc). Isso obviamente toma tempo (não muito quando você usa RSS), mas minha visão como arquiteto de sistemas é muito mais ampla do que a de muita gente pelo fato de eu conhecer diversas tecnologias e me manter MUITO bem informado. Por exemplo, eu sei o que é AJAX e ATLAS e o cara que trabalha com ASP/HTML/JavaScript aqui na empresa não sabe. É importante se manter bem informado, é importante ler sobre tecnologia. Eu respiro tecnologia 24 horas por dia, e isso me ajudou inúmeras vezes e vai continuar ajudando. Além disso, os melhores programadores e arquitetos que eu conheço têm a mesma atitude quanto à isso. Eu recomendo que quem quer ser um bom programador faça o mesmo, mantenha-se informado. Sem deixar de programar é claro. "Dezenas de vezes" ao dia é quase uma hipérbole, e para ser um bom programador é preciso programar muito dia e noite. (Se eu não passasse o dia (e a noite) programando eu não teria conhecimento técnico para escrever o que escrevo no site, e teria mais um desses "blogs de tecnologia" pt-br que só repete os sites de notícias e anuncia eventos)

    Meu ponto foi e continua sendo: se você acha que vale a pena vestir a camisa, vista-a. Note que o "vale a pena" é um conceito muito pessoal. Isso pode "valer a pena" para uma pessoa pelo simples fato de ela ter um emprego. Minha decepção vem justamente do fato de eu ter tentado "vestir a camisa da empresa" várias vezes e não ter visto retribuição alguma. Vista a camisa da empresa se puder.

    Em 08/11/2005 18:14 - Comentários (7)

    As implicações do HyperThreading

    Slava Oks, da equipe do SQL Server, escreveu em um post recente que os clientes têm reportado problemas de queda de performance em servidores com processadores HT (como o Pentium 4 HT). Nesse post ele explica o problema e prova que ele realmente acontece usando um programa de exemplo. Veja o post dele para mais detalhes. E eu vou aproveitar esse assunto para divagar...

    O que acontece é o seguinte: apesar de ter dois núcleos lógicos separados, em um processador HT eles compartilham os caches L1 e L2 entre si. Isso quer dizer que, se uma thread que acessa uma grande quantidade de memória de uma vez (como o lazy writer do SQL ou uma thread que lida com streaming de outra aplicação qualquer) estiver rodando no mesmo processador físico, ele vai acabar por sujar o cache toda vez. Esse efeito de cache trash também acontece com um processador single core, mas o que piora a situação nesse caso é que, além do cache miss, a worker thread do SQL vai ter que esperar a lazy writer thread liberar um spinlock. Em um processador single core, um spinlock não trava coisa nenhuma, já que não há possibilidade do scheduler do Windows colocar duas threads para rodar ao mesmo tempo. O máximo que acontece (em kernel mode) é subir a IRQL para que nenhuma INT interrompa a thread enquanto ela está sincronizada.

    O problema de cache trash não ocorre quando dois processadores físicos são usados porque, como os caches são separados, assim que o segundo processador liberar o spinlock as estruturas usadas pela worker thread estaram no cache (muito provavelmente). Assim, a thread voltará rodando em velocidade muito maior do que uma thread no segundo core de um processador HT, já que ela teria vários roundtrips até a RAM graças aos cache miss.

    Expandindo o problema além do SQL Server, já é sabido que aplicações multimídia são os maiores "destruidores de cache", já que elas lêem grande quantidade de dados na memória de forma sequencial, sem reaproveitar. Os aplicativos comuns (digamos, não-multimídia) costumam acessar quase sempre as mesmas posições na memória, o que faz com que os caches do processador aumentem bastante o desempenho. Os caches L1 e L2 estão "mais perto" do processador, e seu tempo de acesso é bem menor do que o da memória RAM. Como as aplicações multimídia lêem muitos dados e de forma sequencial, não há como fazer cache disso, e o processador quase sempre terá um "cache miss" tanto em L1 quanto em L2 na hora de fazer uma leitura na memória.

    O efeito de cache trash também acontece quando você usa uma aplicação multimidia no seu computador com um processador, já que todas as threads de todos os programas usam o mesmo L1 e L2. Sendo assim, ouvir mp3 ou rodar qualquer tipo de software que de streaming reduz o desempenho de computadores com o cache menor (como Celeron), já que o player vai sujar o cache durante o tempo todo e as outras thread vão sofrer com o cache miss toda hora. Por isso que os processadores para servidor (como o Xeon) têm um cache maior, porque a probabilidade de uma thread encontrar os dados nos cache é maior, o que faz com que a thread rode mais rapidamente. Só que a memória cache é cara, o que explica a maior disponibilidade em servidores...

    Para mais informações, leia:

    Em 14/11/2005 16:14 - Comentários (4)

    Não custa repetir: Dilbert não é ficção

    Em 14/11/2005 18:02 - Comentários (4)

    Mais problemas com o HyperThreading

    Pesquisando mais sobre HyperThreading e as implicações do cache compartilhado, cheguei à um artigo que já saiu a um bom tempo: Hyper-Threading Considered Harmful (pdf). Nesse artigo, o autor explica mais um problema sobre o cache compartilhado, só que dessa vez uma falha de segurança.

    Como eu já disse no post anterior, os caches L1 e L2 dos processadores HT são compartilhados entre os núcleos lógicos. Sendo assim, em um processador HT, duas threads rodam ao mesmo tempo e usando o mesmo cache (isso não acontece em dois processadores, porque apesar das threads rodarem ao mesmo tempo, cada processador tem seu cache). Isso posibilita que uma thread consiga pegar dados de outra thread quando eles são colocados no cache. Junte isso com a possibilidade da primeira thread estar lendo - e consequentemente colocando no cache - uma chave de criptografia, e pronto, está criada a falha de segurança.

    Apesar de não ser possível pegar todos os dados da outra thread, algumas partes de uma chave de criptografia é muito melhor do que nada, e isso muito mais perigoso quando uma chave é usada constantemente, dando à thread espiã a possibilidade de tentar pegar a chave por pedaços a cada vez que a chave de criptografia é usada.

    Em 16/11/2005 03:07 - Comentários (0)

    Programando direito com o Slashdot

    No Ask Slashdot de ontem, um slashdotter perguntou sobre como montar um padrão de desenvolvimento para um recém criado departamento de desenvolvimento de software. Como sempre, as respostas foram muito boas, recomendo a leitura. O que eu acho mais interessante é que ninguém recomenda RUP... :-)

    E isso me faz lembrar de como muitas das coisas que foram respondidas lá são óbivias para todo mundo mas muita gente não faz. E esse muita gente inclui "eu mesmo" em alguns pontos...

    Coisas obviamente necessárias para fazer um software que presta:

    • Especificação. Simples. Alguns DOCs, nada muito complicado, mas que diga o que deve ser feito, como deve ser feito e porque deve ser feito assim.
    • Comentário no código. Na minha opinião MAIS IMPORTANTE DO QUE A ESPECIFICAÇÃO, pelo fato de ser mais simples de escrever e estar mais ligado ao processo de desenvolvimento. Se você não comenta seu código, está errado, isso é essencial. Essencial. Essencial. for(;;) cout << "Essencial" << endl.
    • Software de controle de bugs. Essencial também. Pode ser uma planilha Excel ou algo como o Bugzilla (que usamos aqui onde eu trabalho).
    • Controle de versão. Eu espero mesmo que ninguém questione esse. O básico do básico. Pode ser um Source Safe (que funciona bem para projetos pequenos), CVS, SVN ou até um Preforce (dizem que é o melhor, US$ 800,00 por usuário).
    • Testes. Uma equipe de testes ou testes unitários. Eu tenho o segundo. Como faço só software server-side e nem tudo está 100% orientado a objetos do jeito que deveria ser, faço softwares que agem como clientes e testam todas as possibilidades e fazem testes de stress. Isso é importante e reduz muito o stress de ter um cliente dizendo que seu software é uma porcaria porque você não testou uma situação de uso que, para ele, é básica.
    • Build automático. É importante, mas muita gente não faz. Eu não fazia, até que compilar 10 dependências na mão começou a atrapalhar o meu trabalho. Alguns arquivos BAT resolvem o problema. Tenho feito meus últimos em Python. Se você usa .NET, estude o MSBuild.

    Acho que esses são os básicos do básico. Não são muito difíceis de implementar e trazem um imenso retorno em curto prazo.

    Em 17/11/2005 14:51 - Comentários (10)

    Google no Brasil: Agora é pra valer

    Acho que quase todo mundo já sabe que o Google veio para o Brasil. Eles compraram, esse ano, a empresa mineira Akwan e estão montando um equipe aqui no Brasil.

    Ontem foi um dia particularmente importante para essa entrada do Google no Brasil. Hoje no "Official Google Blog" eles anunciaram o lançamento do Google News em português, e ontem fizeram um anúncio de 2 páginas no "Estado de São Paulo" (alguém consegue um link para isso? Caderno A, página ~14) explicando sua entrada no Brasil, seu modelo de negócios e noticiando a abertura do escritório em São Paulo.

    E antes que alguém pergunte: sim, eles já estão contratando programadores no Brasil. Só programadores C/C++, é claro, é óbvio. :-)

    Em 18/11/2005 13:06 - Comentários (0)

    Fazendo o WinDbg entender o arquivo PDB compilado pelo Visual C++ em DEBUG

    Eu já apanhei bastante com erros do tipo "type information missing" no Windbg, tentando fazer debug de executáveis ou DLLs compilados em versão DEBUG com o VC6. O PDB gerado pelo VC6 - quando você compila em DEBUG - não é compatível com o WinDbg. Depois de várias tentativas e algumas perguntas no grupo de WinDbg gringo, consegui encontrar uma solução definitiva. Segue a receita de bolo:

    • Primeiro você precisa configurar o VC para gerar o PDB. Meu artigo sobre WinDbg explica como fazer isso;
    • Coloque a configuração "Debug Info" exatamente como manda o artigo. Ao invés de "Program Database for Edit and Continue", a configuração deve ser "Program Database". O Edit and Continue é um dos motivos do PDB não ser entendido pelo WinDbg;
    • Entre na aba "Link" do "Project Settings" e adicione "/pdbtype:con /EDITANDCONTINUE:no" na parte de "Project Options", logo depois de todos os outros flags que já estão configurados. Isso vai garantir que o "Edit And Continue" estará desabilitado e que todas as informações de debug estarão dentro do PDB gerado.

    Pronto. Agora é só dar um rebuild no projeto, o PDB gerado será compatível com o WinDbg.

    Em 18/11/2005 15:00 - Comentários (2)

    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 - Comentários (2)

    Onde está o STL.NET?

    Uma das maiores promessas do Visual C++ 2005 era a STL.NET, uma adapatação da STL para ser usada com tipos managed. Isso faria com que nós, programadores C++, continuassemos usando a STL mesmo programando em .NET - mesmo porque, comparado com a STL, os containers e algoritmos do .NET Framework parecem brincadeira de criança. O próprio Stan Lippman escreveu um artigo em 2004 falando da STL.NET. Ela possibilitaria que você escrevesse - em .NET, 100% managed e safe - um código assim:

    ref class ProgrammingLanguage
    {
      bool _hasMultipleInheritance;
      bool _isStandard;
      bool _hasBoost; //apelei... :-)
      String^ _name;
    public:
      ProgrammingLanguage(String^ name, bool hasMultipleInheritance, bool isStandard, bool hasBoost):
        _name(name),
        _hasMultipleInheritance(hasMultipleInheritance),
        _isStandard(isStandard),
        _hasBoost(hasBoost)
        {}
    
        property String^ Name
        {
          String^ get()
          { return _name; } 
        }
    
        ref class IsBestLanguage
        {
        public:
          bool operator()(const ProgrammingLanguage^ language)
          {
            return language->_hasMultipleInheritance &&
                 language->_isStandard &&
               language->_hasBoost;
          }
      };
    };
    
    int main(array<System::String ^> ^args)
    {
      vector<ProgrammingLanguage^>^ Languages = gcnew vector<ProgrammingLanguage^>();
      vector<ProgrammingLanguage^>::iterator iBestLanguage;
    
      ProgrammingLanguage ^CSharp, ^CPlusPlus, ^VB, ^Java;
    
      CSharp = gcnew ProgrammingLanguage("C#", false, true, false);
      CPlusPlus = gcnew ProgrammingLanguage("C++", true, true, true);
      VB = gcnew ProgrammingLanguage("Visual Basic / Visual Fred", false, false, false);
      Java = gcnew ProgrammingLanguage("Java", false, false, false);
    
      Languages.push_back(CSharp);
      Languages.push_back(CPlusPlus);
      Languages.push_back(VB);
      Languages.push_back(Java);
    
      iBestLanguage = find(Languages->begin(), 
                           Languages->end(), 
                           gcnew ProgrammingLanguage::IsBestLanguage());
    
      if(iBestLanguage != Languages->end())
        Console::WriteLine("A melhor linguagem de programação é: " + *iBestLanguage);
    
       return 0;
    }
    

    Pois é, mas por algum problema a STL.NET não saiu junto com o Visual Studio 2005. Eu procurei em todos os headers do VC8 e não achei nada. Quando eu cansei de fuçar, resolvi perguntar no fórum de Visual C++ da Microsoft gringa. E a resposa não foi bem o que eu esperava... A STL.NET vai ser disponibilizada como download, futuramente. Só espero que eles tenham atrasado a STL.NET por causa das várias novidades que eles lançaram na parte unmanaged - que é o que interessa do final das contas.

    (Quanto à brincadeira sobre a melhor linguagem de programação, não fique bravo, eu não sou o único que fala besteira)

    Em 28/11/2005 00:57 - Comentários (6)

    Sobre comentários no código

    Esse é mais um post que começou como resposta para um comentário, mas cresceu demais e aprendeu a falar :-). Eu já estava a muito tempo ensaiando para escrever algo sobre a importância de comentar muito bem o código, e agora vou aproveitar o gancho para passar minhas opniões e gerar mais discussão sobre o assunto. Esse post começou como uma resposta para os comentários em "Programando direito com Slashdot".

    Minha teoria é simples:

    Você consegue fazer um script com 10 linhas de código que remove todos os comentários dos arquivos, se você não gosta de ler comentários. Agora eu quero ver você fazer um script que coloque os comentários de acordo com o que você estava pensando...

    Comente tudo que puder, o mais explicado possível, se você pecar pelo excesso é só apagar os comentários depois. Os comentários são essenciais para saber qual idéia você teve na hora de desenvolver uma rotina ou algoritmo. Eu sou um programador C++, algumas vezes virtuoso, leio assembly com relativa facilidade, e não acho que código, em qualquer linguagem, seja claro. Colocar um comentário em uma expressão "variavel +=2;" não é exagero. Qualquer um percebe que você está somando 2 ao valor da variável, o que importa é dizer porque você está somando 2 e como isso causa impacto no restante do seu programa.

    Comentar o código não gasta tempo a mais, faz parte do processo de desenvolvimento. Se você não coloca comentários no seu código, está pulando um parte importante desse processo, está fazendo pela metade. Pode parecer radical, mas eu levei alguns anos para perceber a importância disso, porque já dei manutenção em código bem comentado e código sem comentários. A diferença do tempo que você leva para entender o código e fazer as mudanças necessárias é absurda. Comentar código economiza tempo e dinheiro. Quem nunca se perguntou "o que esse cara queria fazer aqui"?

    Se alguém aqui já teve oportunidade de ver o fontes do kernel do Windo^H^H^H^H^H^H^H^H^H^H^H^H^H^H^H^H os samples do DDK do Windows, sabe a diferença que faz um código bem comentado e bem organizado. Eu sinto muita diferença no meu trabalho quando vou dar manutenção em um código da equipe antiga (sem comentários) e da equipe atual (bem comentado). Posso garantir que, muitas vezes, o tempo para correção de um bug em um código bem comentado é menos da metade do tempo que se leva para corrigir um bug em um código sem comentários. Escrever código mais claro ajuda muito, mas não resolve o problema. O ideal é código claro e comentado. Se as empresas soubessem o quanto isso abaixa o custo de desenvolvimento, iam parar de pensar só em metodologia e pensariam um pouco mais em melhorar o código e a capacidade técnica de quem o escreve...

    Existe sim o risco dos comentários ficarem errados com o tempo, alguém pode modificar o funcionamento de alguma coisa e deixar o comentário antigo. Sim, da mesma forma que seu software pode ter um bug. Esse é um risco pequeno perto dos grandes ganhos que um código bem comentado gera. Sem comentários, se você ficar 6 meses sem mexer no projeto e esquecer como ele funciona, vai ter que entender o código todo novamente (ou ler a documentação, que também pode estar desatualizada). Se você tiver bons comentários, toda vez que você for mudar alguma coisa, por mais que o intervalo seja longo, você terá um ponto de início. Sem comentários você tem um monte de código. E como eu disse - e torno a dizer - nenhum código, em nenhuma linguagem, é claro. Qualquer sistema que seja mais complexo do que um Hello World não é claro. Você pode achar claro enquanto faz, mas quem vem depois de você não achará, seja por falta de conhecimento da regra de negócio, ou mesmo falta de conhecimento na linguagem ou ambiente de programação.

    Comentar o código é de suma importância, e isso deve ser pensado e conversado antes de tentar implementar qualquer metodologia. Comentar o código é um processo rápido, não traumático e resolve muitos problemas. Isso traz grandes vantagens a curto prazo e é muito mais barato e fácil do que implementar uma metodologia. É uma coisa que você pode começar hoje, agora, não depende de aprovação de gerente nenhum, não depende da má vontade do setor administrativo ou financeiro. Além de fazer um bem para empresa comentando bem o seu código, você fará um grande bem para você, pois terá bem menos dor de cabeça quando precisar fazer uma alteração em um sistema 4 anos depois. E a gente sabe que isso acontece.

    Em 29/11/2005 18:00 - Comentários (10)

    Reunião de programadores C++

    Um dia desses, conversando com o Wanderley Caloni Jr e falando sobre como é interessante trocar informações e experiências sobre C++, eu tive a brilhante e original idéia (ninguém nunca fez isso antes) de propor encontros razoavelmente regulares para fazermos isso. Eu pensei em algo mais ou menos assim:

    • Peridiocidade dos encontros de X em X meses. Ainda não fechamos isso.
    • X pautas por reunião, votadas pelos participantes. Já tenho algumas sugestões como, recursos arcanos do C++ (essa é com o Wanderley), ferramentas, bibliotecas, organização de código, etc.
    • Troca de experiências sobre C++ em diversos sistemas operacionais. Por exemplo, eu mostraria para o pessoal de UNIX o Visual C++ e aprenderia com eles sobre emacs ou KDevelop.
    • Começar de forma muito simples, como uma mesa redonda, e se a coisa avançar, arrumar um lugar para palestras e apresentações.
    • Reuniões em bares ou restaurantes onde possamos conversar, levar acompanhantes e nos sentirmos seguros levando notebooks ou Pockets. Podemos até subir uma rede wifi e aumentar a diversão :-)
    • Como eu e o Wanderley somos de São Paulo, os primeiros encontros seriam aqui.
    • Isso é uma boa desculpa para se encontrar e tomar um chopp/guaraná/cachaça.

    Gostaria que as pessoas da comunidade C++ dessem sugestões sobre essa idéia. Estamos pensando em agendar o primeiro encontro para dia 17/12/2005 (sábado) durante a tarde, em um Outback (comida australiana, Shopping Center Norte ou Eldorado, tem infra para crianças e a comida é muito boa). Como pauta do primeiro encontro eu sugiro uma apresentação dos participantes (o que será desnecessário se só eu e o Wanderley aparecermos) e uma discussão sobre o presente e o futuro do mercado de trabalho C++.

    Coloquem as sugestões nos comentários e quando a gente fechar alguma coisa eu escrevo um novo post com o que definimos.

    Em 02/12/2005 16:09 - Comentários (25)

    Compilação das idéias sobre a reunião de programadores C++

    Vou compilar nesse post as idéias que foram dadas nos comentários do post anterior, e via MSN:

    • Fazer um site: Todo mundo falou nisso. Acho uma boa idéia, e acho que um Wiki seria ideal para isso, já que todo mundo pode mexer a vontade sem muita coordenação e complicação;
    • Sugestões de pautas: Foram muitas e de ótima qualidade. Acho que já temos pautas por um bom tempo, só falta fazermos as reuniões e encontrar pessoas dispostas a participar. Divulguem esse encontro entre os programadores C++ que você conhece em São Paulo;
    • Montar uma "comunidade C++" brasileira: Acho uma ótima idéia, um ótimo meio para trocarmos idéias e experiência. Tudo isso através dos encontros e do site;
    • Filmar os encontros e fazer podcasts: Para filmar é só colocar o tripé lá e ligar a câmera, depois o vídeo pode ser colocado no Google Videos para download. Para gravar só audio, qualquer mp3 player hoje em dia faz isso. Acho tecnicamente plausível e não muito complicado;
    • Participações virtuais: Se escolhermos um bar ou restaurante que tem wifi (como o "America" em São Paulo), podemos ter participações via Skype ou software similar. Acho uma ótima idéia, só precisamos testar se esse modelo funciona e se é tecnicamente viável num wifi "de grátis" (NAT, banda, etc);
    • Fazer encontros virtuais via Skype ou TeamSpeak: Podemos nos organizar para fazer isso. Não sei se a fórmula funciona, mas podemos tentar. Eu ainda prefiro os encontros "live" (e a desculpa para sair e tomar cachaça/chopp/guaraná?), mas é uma boa alternativa para quem não mora em São Paulo (e não quer organizar um encontro na sua própria cidade).

    Como eu já disse nos comentários do post anterior: minha idéia é fazer algo democrático, não estou coordenando nem organizando nada. Usei meu site para divulgar a idéia por causa da visibilidade e do público alvo. Dêem suas sugestões - e mais importante, confirmem suas participações - e vamos ver se conseguimos levar essa idéia adiante.

    Em 05/12/2005 17:46 - Comentários (9)

    Encontro de programadores C++ em São Paulo

    Como já dito anteriormente, eu tive a brilhante e original de idéia de propor um encontro de programadores C++ para trocarmos idéias, falar sobre código e tomar umas biritas. Depois de alguns dias de discussão, e algumas dezenas de comentários depois, fechamos a seguinte configuração para o primeiro encontro:

    O que: Encontro de programadores C++, em São Paulo.
    Quem: Qualquer programador C++ ou pessoa interessada em desenvolvimento de software usando a linguagem C++, em qualquer plataforma (Windows, Linux, etc).
    Quando: Dia 17/12/2005, sábado, às 15:00.
    Onde: No Outback do Shopping Eldorado, em São Paulo - SP.
    Pautas do dia: Apresentação dos participantes e discussão sobre a criação de uma comunidade de programadores C++ (com site, contatos, reuniões frequentes, artigos, fórum, e tudo que uma comunidade tem direito). Depois, entre um chopp (ou guaraná) e outro, vamos discutir também o mercado de trabalho para programadores C++ no Brasil.

    Perguntas-ainda-não-feitas-mas-que-eu-acho-que-alguém-vai-fazer:

    O encontro é pago? Não, você só paga o que consumir no Outback
    Não tenho muita experiência com C++ mas tenho muito interesse. Posso ir? Sim.
    Por que essa data? Porque ainda temos mais de uma semana para nos programar e acho que no sábado fica mais fácil para todo mundo. Se não ficar, nós mudamos o dia da semana para o próximo encontro.
    Por que esse horário? Se você quiser almoçar no Outback é só tomar um café da manhã mais tardio e almoçar lá. Se você quer economizar $$ ou não gosta da comida de lá, você pode vir "almoçado" de casa e só tomar chopp.
    Posso levar acompanhante? Sim. O Outback é um lugar que você pode levar crianças se necessário. Além disso, tem o shopping caso algum acompanhante enjoe de ouvir um monte de gente falando de programação.
    Posso levar notebook ou um livro para mostrar alguma coisa: Sim, claro.
    Quanto eu gasto no Outback? Quando eu almoço lá com a minha esposa gasto aproximadamente R$ 30,00 por pessoa. Mas você pode não almoçar e só tomar chopp/refrigerante. Lá é um misto de bar e restaurante e o clima é bem descontraído.
    Mas será que vai ter mesa pra todo mundo? Eu pretendo chegar um pouco antes para reservar as mesas.
    Putz, no Outback? Não podia ser em outro lugar? Como precisavamos decidir logo o lugar, fechamos o Outback. Lá podemos discutir outros lugares para os próximos encontros.
    Como eu chego lá? Ligue para o Shopping Eldorado ou para SpTrans (156) e pergunte.

    Acho que é isso. Confirme sua presença nos comentários desse post, para que eu possa saber quantas mesas serão reservadas. Diga também quantos acompanhantes você pretende levar.

    Nos vemos lá!

    Em 07/12/2005 19:40 - Comentários (46)

    64bits ou 32bits?

    Apesar de ter um notebook AMD 64bits, eu instalei o Windows XP 32bits nele. Esse trecho encontrado em "Platform SDK: Performance Monitoring" resume um dos motivos:

    If you provide counters on a 64-bit computer, you must install both the 32-bit and 64-bit version of your provider on the computer if you want to support both 32-bit and 64-bit consumers. Place the 32-bit version in the %windir%\syswow64 folder and the 64-bit version in the %windir%\system32 folder.

    O que me deixa muito transtornado é que teremos que conviver com essas escolhas pelas próximas décadas.

    Em 15/12/2005 19:08 - Comentários (2)

    Encontro de programadores C++ confirmado!

    Está tudo confirmado para o "Encontro de programadores C++", no sábado, dia 17/12, às 15:00 hs, no Outback do Shopping Eldorado. Chegarei lá um pouco antes para reservar as mesas. A reunião será feita na varanda do Outback, que está localizada no fundo do bar, à direita. Deixarei as recepcionistas avisadas sobre o encontro e nossa localização, já que a maioria dos participantes não se conhece pessoalmente.

    Só lembrando: isso é um encontro de programadores, não uma festa ou baile. Então, quem puder, chegue no horário para podermos começar as discussões e apresentações o mais cedo possível e com poucas interrupções.

    Em 16/12/2005 19:17 - Comentários (6)

    Resultado do Encontro de Programadores C e C++

    Depois da primeira praia (depois do Natal tem mais), uma pausa para escrever sobre o encontro :-)

    Dez pessoas estavam presentes, o que foi o número que eu esperava (quase metade dos que confirmaram). Fizemos as apresentações e cada um falou em que área trabalha e algumas das ferramentas que usa. Achei essa parte bem interessante, é muito bom conhecer outras pessoas que usam a mesma linguagem para resolver problemas diferentes. Acho que a oportunidade de troca de idéias nos encontros é muito grande, espero que esse seja o primeiro de muitos outros.

    Eu gosto da comida do Outback, mas pensando melhor, é caro demais para encontros periódicos. Precisamos pensar em algum lugar mais barato e democrático. Além disso, eles pecam bastante na parte de organização de encontros, já que não é possível reservar mesas, nem se alguém do grupo chegar antes. Entendo o problemas deles - quem reservou pode não vir - mas muitos lugares aceitam reservas mesmo correndo esse risco. Podíamos pensar na praça de alimentação de algum shopping onde as mesas não sejam fixas ao chão, para podermos juntá-las. Dessa forma cada um come o que quer, pagando quanto quiser. Se alguém tiver alguma boa sugestão de lugar ela será muito bem vinda.

    Conversamos no encontro sobre o site e a comunidade. Uma das coisas que eu conversei com o pessoal no final (alguns já tinham ido embora) é que a comunidade deveria ser de C e C++, não exclusivamente de C++. Afinal, estamos no mesmo barco e é mais interessantes unir todo mundo do que segmentar. Vi no encontro que tem bastante gente que trabalha com C, principalmente o pessoal de dispositivos. Acho muito interessante ter esse pessoal no mesmo grupo. Quanto ao site, decidimos testar o formato de Wiki para começar. Como todos vão podem editar, isso permite que iniciantes com boa vontade escrevam tutoriais sobre tópicos mais básicos, sabendo que se escreverem algo errado, alguém pode corrigir. Além disso isso facilita muito a administração, já que ninguém terá a responsabilidade de ser o grande administrador do site e o processo de publicação fica bem simples, o que impede que alguém de férias ou sem tempo deixe de publicar alguma coisa.

    Como combinado, criei o site temporariamente em http://www.1bit.com.br/cpp/wiki, e criei uma página inicial para discutirmos como as coisas serão feitas e organizadas. Assim que decidirmos nome de domínio e tudo mais que precisamos para lançar o site definitivo, eu copiarei o conteúdo desse wiki para o novo. Dêem uma lida no conteúdo e me ajudem a escrever mais sobre o encontro e sobre o que faremos.

    Resumindo: apesar de um pouco cansativo (o encontro levou umas 4 horas) eu acredito que o encontro serviu para o que foi proposto. Conto com todos para a organização de mais encontros e para a formação de uma comunidade brasileira de programadores C e C++!

    Gostaria de agradecer aos sites que fizeram a divulgação do encontro, como o br-linux, Linha de Código, Slackware Brasil, Linux News e Notícias Linux. O alcance da divulgação pode ser visto pelo Google. Obrigado a todos.

    Em 22/12/2005 16:13 - Comentários (4)

    Wiki C/C++

    Como dito antes, já coloquei no ar o Wiki para nossa futura comunidade de programadores C/C++. Alguns autores estão adaptando e republicando seus artigos lá, e alguma coisa sobre conceitos básicos das linguagens já está sendo escrito. Ainda precisamos de um designer ou alguém com o mínimo de bom gosto para melhorar a página inicial, mas isso já é outra história. :-)

    Todos estão convidados a escrever, mesmo os iniciantes. A grande vantagem do formato Wiki é que, se você escrever alguma coisa errada, alguém pode corrigir. Como o Wiki não deve ficar muito grande, não será difícil monitorar o conteúdo. Publiquem sem medo, só tenham em mente que tudo que você escrever pode ser modificado, melhorado ou até mesmo removido caso os membros da comunidade entendam que seja inadequado.

    Tudo no Wiki é modificável. Quando você encontrar algum erro, corrija-o. Não é necessário discussão para isso, o formato Wiki foi escolhido justamente por essa vantagem. Caso o seu problema seja informação sobre autoria, é possível listar todas as modificações de um usuário no Wiki. Se o problema era um link para colocar no seu curriculum, isso deve resolver :-)

    Precisamos discutir a criação e formalização da comunidade. Precisamos de um nome e uma idéia de domínio para o site. Já criei um tópico no Wiki para discutirmos essas coisas, não deixem de participar. Estamos planejando uma atuação maior, na Internet (fórum, lista de discussão, wiki, etc) e no mundo real (encontros, palestras, etc).

    Nosso foco é C/C++, independente de sistema operacional, arquitetura ou grau de conhecimento. Todos serão bem vindos, quanto mais pessoas participarem mais poderemos nos ajudar e difundir conhecimento.

    O endereço (provisório) do wiki é www.1bit.com.br/cpp/wiki.

    Em 03/01/2006 21:03 - Comentários (1)

    Parece que a certificação de Visual C++ voltou (ou quase)

    O Fabio Galuppo disse no blog dele que nas novas provas para o Framework 2.0 (ainda em beta, segundo ele) vocês escolhe a linguagem de programação que será usada durante o teste. E, para meu espanto, estava lá na lista o C++/CLI. Ainda não tive notícias sobre provas do Visual C++ 2005 unmanaged - nem o pessoal da Microsoft soube responder se elas vão voltar - mas já é um grande progresso. Eu sinceramente duvido que elas voltem (ainda bem que eu já tirei a minha), mas...

    Essa mudança se deve provavelmente ao fato de que o C++/CLI consegue produzir assemblies 100% gerenciados, da mesma forma que o C# e VB8. "Da mesma forma" é modo de dizer, já que o C++/CLI usa o otimizador do Visual C++, o que faz com o que os assemblies sejam melhores do que os gerados pelo C# e VB. Eu já falei sobre o otimizador do Visual C++ antes.

    Ah, e só para lembrar, o Wiki C/C++ está a todo vapor, com muitas visitas e colaborações. Vamos ver se agora alguém se anima a escrever sobre C++/CLI :-)

    Em 09/01/2006 14:01 - Comentários (1)

    Larry Osterman fala de COM

    Larry Osterman, um dos programadores da velha guarda da Microsoft está escrevendo uma série de posts sobre o funcionamento do registro de objetos COM. Muito bom, vale a pena acompanhar.

    Em 10/01/2006 15:06 - Comentários (0)

    Caro usuário: leia atentamente as instruções antes de usar

    imagem original

    Em 17/01/2006 15:49 - Comentários (9)

    Explicando a sopa de letrinhas da programação C/C++ para Windows: WTL

    Outros posts dessa mesma série:   ATL     COM     MFC     Win32

    WTL (Windows Template Library) é uma biblioteca de templates C++ que encapsula a parte da Win32 API que lida com interface gráfica (GDI). Ela contém classes para criar janelas, controles Win32 (botões, barras de progresso, etc), dialogs, wizards, etc.

    A WTL é uma extensão da ATL, que já contém algumas classes básicas para manipulação de janelas. Essas classes da ATL foram criadas inicialmente para ajudar na confecção de controles ActiveX e seus Property Pages. Sobre esse suporte básico da ATL, um programador da Microsoft chamado Nenad Stefanovic criou diversas outras classes para encapsular o restante da GDI. Esse conjunto de classes acabou se transformando no que conhecemos como WTL.

    Por ser uma extensão da ATL, a WTL segue a mesma filosofia: classes template enxutas (inspiradas pela STL), liberdade de uso das classes fora de um framework (ao contrário da MFC) e código simples e rápido. Por ser todo baseado em templates, não é necessário usar uma LIB ou DLL, seu executável fica completamente independente. Além disso - também pelo fato de usar templates - o tamanho do executável fica muito pequeno, sendo possível criar executáveis de 70 kb ou menos sem dependência de DLLs ou runtimes. Compilando seu executável em Release, a WTL não causa nenhum overhead em relação a programas feito em Win32 puro, sem classes. Como o código da WTL é muito enxuto, a maioria das funcões acabam "sumindo" durante a compilação, pois são colocadas inline.

    Há algum tempo atrás o projeto WTL foi colocado no SourceForge para que a comunidade C++/Win32 pudesse ajudar o Nenad nas correções e sugestões. Mesmo assim, o Nenad ainda é o coordenador e responsável pelo projeto, garantindo que a filosofia ATL/WTL não suma a medida que milhares de classes sejam adicionadas à biblioteca. Na realidade poucas classes foram adicionadas à WTL depois disso, com destaque para as classes para Wizards.

    Muitas classes da WTL reproduzem funcionalidades disponíveis na MFC, como as classes CRect, CString (que a partir do Visual C++ 7.0 faz parte da ATL e não da MFC) e muitas outras. O suporte a janelas também é bem parecido com a MFC, usando mapas de mensagens. Não é difícil para um programador MFC usar WTL, a adaptação é fácil.

    Apesar de todas as vantagens que eu citei, existem algumas desvantagens. Apesar dessa biblioteca ter nascido dentro da Microsoft, ela nunca ofereceu suporte para a WTL. Ela chegou a ser disponibilizada junto com o Platform SDK e o download ainda pode ser feito diretamente nos servidores da Microsoft, mas a biblioteca oficial da Microsoft para C++ ainda é a MFC. Mesmo assim existe um comunidade grande voltada para a WTL, e sempre que eu tive problemas uma pergunta ou uma busca na lista de discussão foi suficiente - pelo próprio fato de ser uma biblioteca simples e enxuta.

    Outro problema da WTL sempre foi a falta de suporte da IDE para ela. A MFC provê diversos wizard e funcionalidades na IDE para assinatura de eventos e criação de classes, suporte que nunca foi dado à WTL. Esse problema foi resolvido por uma santa alma que disponibilizou no CodeProject o WTLHelper, que chega a ser melhor do que o suporte do Visual Studio para a MFC (ClassWizard ou suporte do Visual Studio 7+). Mesmo assim, não deixa de ser um produto não-oficial e que passa longe do controle de qualidade da Microsoft.

    Como um trecho de código vale mais do que 20 palavras, vamos a um programa simples feito em WTL:

    #define WINVER      0x0400
    #define _WIN32_WINNT  0x0400
    #define _WIN32_IE    0x0400
    #define UNICODE
    #define _UNICODE
    
    #include <atlbase.h>
    #include <atlapp.h>
    #include <atlmisc.h>
    #include <atlstr.h>
    
    extern CAppModule _Module;
    
    #include <atlwin.h>
    #include <atlframe.h>
    #include <atlctrls.h>
    #include <atldlgs.h>
    #include <atlctrlw.h>
    
    CAppModule _Module;
    
    class CMainFrame : public CFrameWindowImpl<CMainFrame>
    {
    public:
      WTL::CButton m_btnTest;
      WTL::CEdit m_edtTest;
    
      //
      // faz a janela ficar com o fundo cinza de dialog
      // (ao invés de branco padrão)
      //
      DECLARE_FRAME_WND_CLASS_EX(NULL, 0, NULL, COLOR_BTNFACE)
    
      BEGIN_MSG_MAP(CMainFrame)
        MESSAGE_HANDLER(WM_CREATE, OnCreate)
        COMMAND_HANDLER(1, BN_CLICKED, OnButtonClick)
        CHAIN_MSG_MAP(CFrameWindowImpl<CMainFrame>)
      END_MSG_MAP()
    
      LRESULT OnCreate(UINT, WPARAM, LPARAM, BOOL&)
      {
        SetWindowText(L"WTL Rules!");
    
        //
        // cria um botão e um edit
        //
        m_edtTest.Create(m_hWnd, CRect(10,10,200,32), NULL, 
          ES_LEFT | WS_VISIBLE | WS_CHILDWINDOW, WS_EX_CLIENTEDGE , 2);
        m_edtTest.SetWindowText(L"digite algo aqui");
        m_edtTest.SetFocus();
        m_edtTest.SetSel(0, m_edtTest.GetWindowTextLength());
    
        m_btnTest.Create(m_hWnd, CRect(120,45,200,80), NULL, 
          BS_PUSHBUTTON | WS_VISIBLE | WS_CHILDWINDOW, NULL, 1);
        m_btnTest.SetWindowText(L"Botão");
    
        //
        // usa o fonte padrão de dialog 
        // (tem que fazer isso porque criamos um janela, não um dialog)
        //
        m_edtTest.SetFont((HFONT)GetStockObject(DEFAULT_GUI_FONT));
        m_btnTest.SetFont((HFONT)GetStockObject(DEFAULT_GUI_FONT));
    
        return 0;
      }
    
      LRESULT OnButtonClick(WORD, WORD, HWND, BOOL&)
      {
        ATL::CString str;
    
        m_edtTest.GetWindowText(str);
        MessageBox(str);;
        return 0;
      }
    };
    
    int Run()
    {
      CMessageLoop theLoop;
      CMainFrame wndMain;
      
      //
      // no WTL, existe um objeto separado que controla o message loop
      // vamos adicioná-lo ao _Module, que é o objeto global do ATL que
      // controla toda a aplicação
      //
      _Module.AddMessageLoop(&theLoop);
    
      //
      // agora é só criar a janela e rodar o message loop
      //
      wndMain.CreateEx(NULL, CRect(100,100,320,220),
        WS_CAPTION | WS_SYSMENU | WS_THICKFRAME);
      wndMain.ShowWindow(SW_SHOW);
      wndMain.UpdateWindow();
    
      int nRet = theLoop.Run();
    
      _Module.RemoveMessageLoop();
    
      return nRet;
    }
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int)
    {
      //
      // primeiro inicializamos o COM, ATL e Common Controls
      //
      ::CoInitialize(NULL);
      _Module.Init(NULL, hInstance);
      AtlInitCommonControls(ICC_COOL_CLASSES | ICC_BAR_CLASSES);
    
      //
      // e agora colocamos o programa para rodar
      //
      int nRet = Run();
    
      _Module.Term();
      ::CoUninitialize();
    
      return nRet;
    }
    

    Para compilar esse trecho de código é só instalar e configurar a WTL, criar um programa "Win32 Application" e substituir o código gerado pelo Wizard por esse. Além disso, quando você instala o WTL você terá um novo Wizard no Visual Studio para criar um "ATL/WTL Application".

    Mais Informações:
    WTL no SourceForge
    Lista de discussão e suporte WTL
    WTLHelper
    Seção do CodeProject sobre WTL

    Em 19/01/2006 20:32 - Comentários (11)

    Recursos de segurança no Visual C++ 2005

    Acho que todos que lêem meu blog já devem ter percebido que eu realmente gostei do Visual C++ 2005. Dentre as inúmeras novidades e melhorias encontramos diversas opções para deixar nossos programas mais seguros e resistentes contra buffer overflow e outros tipos de ataque. Para mais detalhes leia o artigo da MSDN "New Security Features in Visual Studio 2005".

    Em 24/01/2006 14:46 - Comentários (0)

    Bugs no Intel Core Duo

    Eu não gosto muito de ficar colocando links aqui (eu presumo que as pessoas lêem meu blog por causa das coisas que EU escrevo, e quem quer notícias frescas lê o Slashdot) mas esse eu não resisti...

    Saiu uma lista de bugs do Intel Core Duo, o novo processador da Intel, com 34 bugs que soam realmente absurdos para qualquer um que conheça o mínimo de estrutura de processador. Alguns deles me parecem realmente graves, principalmente os relacionados com Virtual Memory e Virtual Mode. Segundo fontes que eu não sei se são confiáveis, a Intel não pretende corrigir muitos dos bugs (o que tem sentido do ponto de vista econômico, isso deve custar um fortuna). Eu entendo que processadores complicados como os atuais tenham bugs, mas esses me pareceram (não sou o especialista de plantão, vou esperar o Jon Hannibal do Ars Technica se pronunciar) realmente desastrosos. Vamos esperar o tempo passar para ver a reação do mercado e da Intel.

    Talvez a nossa exposição a esses bugs sejam um pouco menor no Windows 64 bits, já que, para meu desespero, tudo que rodar em kernel mode vai precisar ser assinado...

    Em 24/01/2006 20:00 - Comentários (7)

    Writing code is like writing poetry

    A coisa mais bonita que eu li nos últimos tempos:

    Comparing writing specifications to writing code

    Em 09/02/2006 19:34 - Comentários (2)

    WinDbg, cada dia melhor

    Saiu a versão 6.6.3.5 do WinDbg, com melhorias que o deixam definitvamente entre os melhores debuggers existentes. Como um imagem vale mais do que um monte de palavras, dê uma olhada nisso:

    Depois da imagem, vamos ao bla-bla-bla:

    • Coloração do código: Recurso simples, básico, e presente na maiorias dos debuggers que não fazem força para parecer coisas de hacker. Modificação muito bem vinda, que facilita bastante a visualização e leitura do código.
    • Visualização de variável por tooltips: mais um recurso do debugger do Visual Studio que foi incorporado ao WinDbg. É equivalente a chamar "dt [nome-da-variável]".
    • Visualização user-friendly de GUIDs: Não sei se é realmente um melhoria na versão nova, mas eu só percebi agora :-) Agora o WinDbg mostra os GUIDs usando a mesma representação usada para gravá-los no registro.

    Eu já costumo usar mais o WinDbg do que o debugger do Visual C++, e a cada atualização do WinDbg eu o uso mais, mesmo que o debugger do Visual C++ também esteja melhorando a cada dia que passa. Eu uso o WinDbg quando a coisa é mais "hardcore" - congelar threads, analizar memória e locks, fazer disassembly de alguma API do Windows, fazer debug de serviços em servidores, live programming, etc - e o debugger do Visual C++ para coisas mais triviais como testes na própria máquina, geralmente de programas que têm mais lógica de negócio do que interação com o Windows.

    Você pode fazer o download diretamente da página do Debugging Tools for Windows. Não esqueça de conferir a lista oficial de novidades.

    Em 16/02/2006 15:31 - Comentários (0)

    Aprendendo ponteiros de uma forma divertida

    Uns malucos de Stanford fizeram um vídeo para explicar aos iniciantes como funcionam os ponteiros e referências. Com certeza eu vou usar esse vídeo nos meus cursos de C++!

    Binky Pointer Fun Video

    Como dizem na gringa, I'm ROFLMAO :-)

    Em 17/02/2006 16:45 - Comentários (4)

    Resolvendo problemas em servidores de produção

    Uma das minha atribuições onde trabalho é responder perguntas como "Por que o servidor está lento?" e "O servidor consome 100% de processador mas não conseguimos isolar a situação. Você pode ver isso?". O problema é que, em muitas vezes, eu não posso instalar praticamente nada no servidor, algumas vezes nem fazer login nele eu posso. E nesses casos, somente o "Debugging Tools for Windows" salva.

    Hoje eu encontrei um artigo e um documento muito bons sobre o assunto. O primeiro é o Root Out Elusive Production Bugs with These Effective Techniques, que fala sobre o AdPlus - ferramenta usada para gerar dumps de processos enquanto eles estão rodando - e sobre PerfMon. O segundo é o Application debugging in a production environment, um livro de 125 páginas que fala desde como gerar symbols e fazer debug remoto usando o Visual C++ até como usar o WinDbg e calling conventions.

    Em 17/02/2006 21:03 - Comentários (0)

    Você lê Slashdot? Então já pode ser contratado pela Microsoft

    Além dos requisitos usuais ("To fill this position we need a strong developer with C/C++ and assembly code skills"), uma vaga no time do Rotor tem alguns requisitos interessantes...

    Em 21/02/2006 18:23 - Comentários (11)

    Distribuindo aplicações feitas com o Visual C++ 8

    O Visual C++ 8 (que faz parte do Visual Studio 2005) trás além de diversas melhorias, mudanças na distribuição dos softwares compilados com ele. Algumas coisas importantes devem ser notadas, como o fato de que distribuir aplicações em versão DEBUG é uma violação de EULA.

    O Visual C++ é atualmente a única ferramenta da Microsoft que possibilita que você compile um programa que não depende de runtimes ou DLLs adicionais, um programa que é 100% Windows e só. Mesmo assim, para fins de redução de tamanho dos arquivos binários e para facilitar a distribuição, muitos programadores preferem fazer link do aplicativo com a msvcr80.dll, mfc80.dll e atl80.dll. Nesse quesito, a grande mudança no Visual C++ 8 é que as runtimes da MFC e ATL devem ser distribuídas usando MSI ou mantidas na pasta do aplicativo (o que é uma recomendação antiga da Microsoft para diminuir do DLL Hell). O MSI (ou merge module se você fizer seu próprio setup) instalará as runtimes usando o recurso Shared Assemblies do Windows XP ou superior, para evitar que atualizações da runtime quebrem programas que estejam funcionando.

    (Só não esqueça que o Shared Assemblies é um recurso do Windows e não do .NET. As runtimes do Visual C++ 8 não têm qualquer dependência do .NET Framework e seus programas C++ serão nativos como sempre foram. C++ == nativo/unmanaged, C++/CLI == .NET/managed.)

    Apesar dos novos complicadores, existe uma documentação bem explicada e detalhada na MSDN, com destaque para o How to deploy using XCOPY e Visual C++ Libraries as Shared Side-by-Side Assemblies, que lista as DLLs necessárias e suas respectivas funções.

    Se mesmo assim a coisa complicar, configure o seu projeto para fazer link estático com todas as bibliotecas (C++ runtime, MFC ou ATL) e você terá o velho e bom executável sem dependências e que roda em qualquer máquina com Windows instalado. :-)

    Em 22/02/2006 20:41 - Comentários (11)

    Por que meu Windows Vista está lento?

    O Windows Vista inclui agora um recurso que informa o usuário quais programas estão demorando para inicializar e deixando o computador mais lento. Nem sempre o culpado é o antivírus ou um spyware... :-)

    Em 23/02/2006 00:12 - Comentários (0)

    Quando você usa o Visual Basic 6, você usa também o Visual C++ 6

    Um belo dia estava eu em minha casa corrigindo um bug em um programa feito em Visual Basic. Esse é um programa que eu fiz quando ainda trabalhava como desenvolvedor autônomo (ou seja, além do emprego eu pegava trabalho por fora para fazer em casa), e a escolha óbvia era fazê-lo em Visual Basic 6.

    Depois de corrigir os bugs, pedi gentilmente ao VB para que gerasse o executável. A IDE então me reportou que ouve um erro durante a compilação e, caso eu quisesse mais informações, que lesse um arquivo de log. Tamanha foi a minha surpresa quando abri o arquivo de log e me deparei com isso:

    D:\DATA\blablabla\frmTabelas.frm(3398) : fatal error C1001: INTERNAL COMPILER ERROR (compiler file 'E:\8783\vc98\p2\src\P2\main.c', line 494) Please choose the Technical Support command on the Visual C++ Help menu, or open the Technical Support help file for more information

    Pelo que parece, o Visual Basic 6 usa pelo menos uma parte do backend do compilador do Visual C++ 6.0. Talvez isso explique um comparativo de performance que deixou algumas pessoas chocadas, onde um componente feito em Visual Basic 6 ficou quase 3 vezes mais rápido do que um feito em C#. O fato do VB6 usar o backend do VC6 é uma boa explicação para isso.

    Seria interessante ver uma nova comparação, mas dessa vez com o .NET 2.0. Na nova versão o JIT foi melhorado e o JIT 64 bits é feito pela equipe do Visual C++ - que é, de longe, a equipe da Microsoft que mais entende de otimização. Acredito que o VB6 ainda será mais rápido (não dá pra comparar código nativo gerado pelo backend do Visual C++ com código gerenciado) mas acredito - e espero - que a diferença nesse quesito seja menor.

    Em 24/02/2006 17:14 - Comentários (4)

    Se alguém salvou o dump, me mande

    Meu site anda com alguns problemas estranhos, mas eu não consegui isolar a situação do bug. Por acaso alguém salvou o dump? Pode ser minidump mesmo, eu tenho todos os binários guardados em algum backup que eu fiz em algum lugar do passado e que está em algum lugar que eu não me lembro. :-)

    (Aproveitando a sessão besteirol, vamos ao pensamento nerd do dia: Ainda bem que na gringa não era carnaval, assim eu pude ler meus blogs de programação favoritos na segunda e na terça.)

    Em 01/03/2006 17:49 - Comentários (1)

    Ganhe um drive USB da Microsoft

    A Microsoft gringa está distribuindo drives USB com informações sobre licenciamento do Windows. Não sei se funciona mesmo, o site disse que o meu chegará em no máximo 8 semanas. É só seguir as seguintes instruções:

    • Entre no site Mistery Solved;
    • Clique no link "Valuable Information" que está à direita e faça login com seu Passport;
    • Preencha com suas informações pessoais (não esqueça o endereço);
    • No final tem um pequeno quiz. As respostas são 2, True, True, True;
    • Vão te mandar um e-mail de verificação. É só receber e clicar no link;
    • Pronto, é só esperar seu USB drive.

    Lembre-se: o site não diz o tamanho do drive USB que vão te mandar. Se você não tem nada para fazer com a informação que vão mandar, e nada para fazer com um drive USB de 16MB, é melhor não perder seu tempo.

    Em 01/03/2006 20:20 - Comentários (21)

    Mais blogs sobre kernel mode

    A "blogasfera" de desenvolvedores de drivers e código kernel mode em geral está aumentando. Encontrei algumas boas novidades esses dias, entre eles Doron Holan, Craig Rowland e Peter Wieland, todos da Microsoft. Além disso, existem os tradicionais non-MS Steve Dispensa e o mestre Mark Russinovich.

    Em 02/03/2006 17:45 - Comentários (2)

    Péssima notícia: a STL.NET vai atrasar de novo...

    Como eu já tinha dito antes, o pessoal do time do Visual C++ não conseguiu entregar a STL.NET a tempo do lançamento do Visual Studio 2005. Um programador do time do Visual C++ me respondeu no newsgroups que a STL.NET seria disponibilizada para download depois de mais testes e ajustes.

    Pois é, a coisa piorou. Um outro programador da equipe do Visual C++ colocou no blog dele que a STL.NET vai atrasar ainda mais. Segundo ele, devido ao compromisso assumido pela equipe do Visual C++ com o service pack do Visual Studio 2005, que como todo mundo sabe, foi lançado com bugs críticos em aberto. Entendo que existem prioridades, mas na minha opinião isso é um grande furo no compromisso com o C++/CLI (não esqueça, C++/CLI é uma extensão C++ para .NET e não é o bom e velho C++ nativo). A STL.NET era um dos recursos mais esperados na área de integração C++ e .NET, e com certeza ajudaria muito na migração de aplicativos C++ para a plataforma .NET.

    Em 03/03/2006 16:46 - Comentários (1)

    Mais informações sobre o deploy de aplicativos feitos com o Visual C++ 8

    Continuando o assunto sobre deploy de aplicativos feitos usando o Visual C++ 8, encontrei um artigo no Code Project muito bom sobre o assunto: Bootstrapper for the VC++ 2005 Redists.

    Em 08/03/2006 15:52 - Comentários (0)

    2º Encontro de Programadores C e C++

    Está lançada a fase de sondagem para decidirmos lugar e data para o 2º Encontro de Programadores C e C++. Não deixe de dar sua sugestão e nos ajudar a decidir democraticamente onde e quando nos juntaremos para falar de programação além do normal :-)

    Se ninguém tiver nada contra, eu acho que o FAQ e as "regras" do primeiro encontro continuam válidas.

    Em 10/03/2006 22:41 - Comentários (1)

    Dicas para comprar na Amazon

    Sempre que eu preciso de um livro técnico, eu compro na Amazon. Eu não costumo comprar livros em português (já expliquei os motivos várias vezes), e os preços da Amazon são quase sempre mais baratos do que os das livrarias nacionais (como Livraria Cultura e Siciliano). Como eu já comprei livros lá várias vezes, de várias formas e com diversas formas de entrega, aprendi algumas coisas interessantes.

    Primeira coisa: a Amazon envia somente livros, CDs e DVDs para o Brasil (nunca comprei DVDs e CDs, as dicas aqui só valem para livros). Nem adianta tentar comprar um aparelho de DVD ou um iPod que eles não enviam para cá. Existem umas pessoas/empresas nos EUA que te oferecem um endereço lá para receber a entrega, e depois eles mandam para o Brasil. Como eles geralmente mandam para cá informando que é presente (para passar na alfândega), eu não tenho muita certeza da legalidade disso. Um amigo meu já comprou um drive de disquete em uma outra loja americana e mandou entregar aqui. Pagou uma pequena fortuna de taxa de Alfândega. Comprar aparelhos eletrônicos lá e mandar entregar aqui é furada, o preço vai acabar saindo mais caro do que comprar em um shopping aqui com garantia.

    Segunda coisa: só dá para comprar na Amazon com cartão de crédito internacional. Só com cartão internacional. Não existe outra forma de pagamento. Só cartão de crédito internacional. :-)

    Existem 3 tipos de entrega na Amazon:

    • Standard International Shipping (averages 13-21 days)
    • Expedited International Shipping (averages 7-11 business days)
    • Priority International Courier (averages 2-6 days)

    Quanto mais rápida a entrega, mais caro (veja a tabela de preços para detalhes), mas a diferença não está só no prazo. Na Standard (13-21 dias) a entrega é feita na sua casa, enquanto na Expedited (7-11 dias) o pacote é entregue na agência dos correios mais próxima da sua casa, e você tem que retirar. Segundo o que me falaram no correio, essa ordem de exigir a retirada vem do correio de lá. Outra curiosidade: "correio de lá" == "correio da Alemanha". Todos os livros novos que eu recebi da Amazon vieram da Alemanha, e não dos Estados Unidos.

    A Amazon, além de vender seus próprios livros e produtos, serve de "marketplace" para que outras livrarias e lojas menores vendam a partir do site dela. Quando você encontra um livro, eles também te dão a opção de comprar dessas outras livrarias:

    Como visto na imagem acima, essas livrarias (ou mesmo pessoas), além de venderem livros novos, também vendem livros usados. Você pode dessa forma conseguir um bom desconto por um livro em bom estado, algumas vezes pagando menos da metade do preço. Eu já comprei vários livros usados. Na única vez que o livro não chegou, eu só enviei um e-mail dizendo que o livro não chegou e eles extornaram o pagamento na hora. Existem algumas livrarias que aceitam a devolução do livro caso você ache que o estado dele não é satisfatório. Algumas chegam a deixar você ficar com o livro e devolver um parte do dinheiro se você preferir! Apesar de não saber o quão fácil é esse recurso, ele me parece bem interessante.

    Além dos livros usados, outra dica é comprar um livro novo de uma livraria do marketplace ao invés de comprar da própria Amazon. Quando você compra um livro da Amazon, o livro geralmente vem dentro do prazo. Se eles dizem que levará no mínimo 13 dias, provavelmente não chegará antes de 13 dias. Mas na maioria das vezes que eu comprei livros de outras livrarias, a entrega sempre chegou bem antes do prazo, geralmente em 5 dias úteis. O prazo que eles informam é de "3 to 6 weeks", e é mais inteligente contar com esse prazo. Mesmo assim eu já comprei assim umas 5 vezes e o livro sempre chegou rápido.

    A coisa mais importante para comprar de livrarias do marketplace é verificar se eles enviam o livro para o Brasil. Todas as que enviam têm um "International shipping available" no campo "Availability". Se não estiver explicitamente indicado, a livraria não envia para fora dos EUA.

    Não se esqueça: essas dicas de entrega valem para quem, como eu, mora em São Paulo. Não sei dizer se isso funciona igual para entregas em outras cidades. Good shopping!

    Em 13/03/2006 21:32 - Comentários (3)

    Belíssimo!

    Retirado da página do meu sócio-camarada Fernando Roberto no Wiki C/C++:

    "Foi no curso de Informática Industrial que descobriu a beleza do software do tipo que se estiver funcionando perfeitamente ninguém nota a presença dele."

    Sim, isso é belíssimo! E é uma boa explicação para minha preferência por software server-side e kernel-mode. Eu discuti com um pessoal do trabalho semana passada sobre as vantagens e desvantagens de fazer programas para usuários. E cheguei a conclusão que é bem melhor fazer programas onde os usuários são outros programas. :-)

    Em 15/03/2006 17:13 - Comentários (9)

    2º Encontro de Programadores C e C++

    A (comunidade | grupo | bando) de programadores C e C++ tem o imenso orgulho de anunciar o 2º Encontro de Programadores C e C++, em São Paulo. O encontro será realizado no dia 25/03/2006, sábado, as 14:00hs. E como da primeira vez, será aberto para todos os interessados independente do nível de conhecimento em C e C++ ou qualquer outro fator (plataforma, SO, etc). Não se esqueça de confirmar sua presença no nosso Wiki!

    Em 17/03/2006 22:04 - Comentários (0)

    Microsoft libera os fontes do MechCommander 2

    Para os interessados em desenvolvimento de games, a Microsoft liberou os fontes do MechCommander 2 (C++/DirectX), preparados para serem compilados usando o XNA Build que fará parte da plataforma de desenvolvimento de games XNA. Um ótimo divertimento para quem tem interesse em desenvolvimento de games e tem paciência ou banda para o 1GB de download...

    Em 23/03/2006 21:19 - Comentários (0)

    Mais WinDbg

    Atendendo a pedidos, Doron Holan está escrevendo uma série muito boa sobre comandos úteis do WinDbg e dicas de debug em geral.

    Em 24/03/2006 14:47 - Comentários (0)

    Como já disse o Alfred...

    Como já disse o Alfred, a inteligência artificial não oferece perigo. O que realmente oferece perigo é a burrice natural...

    Em 27/03/2006 22:39 - Comentários (3)

    Afinal, o que é o Visual C++ Express Edition?

    A Microsoft disponibilizou a algum tempo atrás o Visual C++ Express Edition, uma ótima ferramenta para quem está começando a programar em C++. Vamos à um resumo do que a versão Express oferece e o que pode ser feito com ela (comparando com o Visual C++ Professional ou Visual Studio 2005).

    O que o Express tem e o que ele é:

    • É grátis. A Microsoft disse que pretende mantê-lo assim até Novembro de 2006. Mesmo assim, se você baixou antes disso ele continuará grátis;
    • Usa o Visual C++ 8, um dos melhores compiladores C++ da atualidade. Mesmo nessa versão Express o compilador vem completo, com suporte a otimização e tudo mais;
    • Designer de Windows Forms. Quem pretende usar o C++/CLI para desenvolver aplicativos .NET. o Visual C++ Express vem com a ferramenta para desenhar forms, igual ao VB8 ou C#;
    • IDE do Visual Studio. Sim, um produto C++, grátis, com um compilador com ótimo suporte ao C++ ISO, e uma IDE decente. Intellisense, gerenciamento de projetos e tudo que precisamos para fugir de notepads ou editores onde você precisa de pós graduação para usar.

    O que o Express não tem e o que ele não é:

    • MFC e ATL. As bibliotecas que são o carro chefe do Visual C++ não foram incluídas. Mesmo assim, no Platform SDK eles são incluídas em versões antigas e limitadas;
    • Profile Guided Optimization. Infelizmente e obviamente, o recurso que deixou o SQL Server 30% mais rápido não foi incluído na versão Express;
    • Dialog Editor. Como o grande foco do Visual C++ é fazer aplicativos nativos, a Microsoft retirou o designer para dialogs Win32, por razões óbvias. Mesmo assim, você ainda consegue usar dialogs Win32 no seus projetos. Você pode editá-los na mão (urgh) ou usar um outro editor;

    O que você consegue fazer com o Visual C++ Express:

    • Estudar C e C++ e fazer aqueles seus exercícios da faculdade ou do livro que você está lendo;
    • Usar STL, Boost e Loki para aprender C++ moderno de uma vez. Assim você poderá sair finalmente dos anos 90 e dessa vida de classes, heranças e só :-)
    • Fazer aplicativos Win32 completos. Para isso você só precisa só baixar o Windows Platform SDK. Não se esqueça que o Office e o SQL Server, por exemplo, são aplicativos Win32 completos e compilados com a mesma versão do compilador que vem junto com o Visual C++ Express Edition;
    • Fazer aplicativos .NET usando C++/CLI. Você tem acesso à todo o .NET Framework, inclusive Windows.Forms, ADO.NET, WinFX (Avalon/WPF e Indigo/WCF). O C++/CLI gera assemblies 100% .NET, da mesma forma que o C# ou Visual Basic 8;
    • Criar aplicativos MFC ou componentes COM ATL usando as versões das bibliotecas que estão incluídas no Windows Platform SDK.
    • Criar jogos usando DirectX ou OpenGL. Para DirectX, tudo que você precisa é do DirectX SDK.

    O que você não consegue fazer com o Visual C++ Express:

    • Fazer ou compilar aplicativos que usem a MFC 8 ou ATL 8
    • Mmmmm deixa eu pensar... Programar em VB você também não consegue. Faltou alguma coisa?

    Em 03/04/2006 19:58 - Comentários (36)

    Doenças de programador e o crônico problema da falta de concentração

    Encontrei (adivinha por onde?) um post muito bom sobre problemas de saúde que costumam acometer profissionais de informática (prefiro esse termo do que geek). Lá um médico fala desde problemas do sono e de cama ("The bed should only be used for two things: sex and sleep" - acho que isso exclui o uso do notebook) até as comuns dores nas costas.

    O que mais me chamou atenção foi a parte sobre dificuldade de atenção e foco. Isso é MUITO comum comigo e com muitos programadores que eu conheço. A pessoa consegue ficar programando horas a fio, mas dorme em uma reunião em menos de 5 minutos. Eu não faço mais reuniões (sou feliz, não sou?), mas quando eram necessárias eu ficava com sono a partir do momento que eu entrava na sala de reunião e via aquele monte de gente sentada. Outra coisa que me chamou a atenção foi ele falar que muitas pessoas gostam de trabalhar com computadores porque é a única coisa que consegue prender a atenção e deixá-las concentradas. O que mesmo assim pode ser difícil, principalmente para pessoas que como eu trabalham em ambientes barulhentos e cheios de baias.

    Essa parte de concentração me lembra um artigo do Joel onde ele fala que nós geralmente demoramos aproximadamente 15 minutos para entrar em um estado de concentração bom o suficiente para produzir um código de qualidade. Como fazer isso quando o ambiente de trabalho parece mais um mercado de peixe e as pessoas não pensam duas vezes antes de chamar tua atenção para algo completamente fútil e desnecessário? Por isso que na Microsoft (e outras empresas, espero) cada programador tem sua própria sala...

    Em 07/04/2006 15:44 - Comentários (8)

    Algumas fotos do 2º Encontro de Programadores C e C++







    Para comentários sobre o que foi falado no nosso grandioso evento, veja o blog do Wanderley Caloni.

    Em 10/04/2006 00:55 - Comentários (5)

    Windows Vista != .NET

    Richard Grimes, um conhecido escritor sobre tecnologia Microsoft que abandonou totalmente o .NET (já discutimos isso aqui no site) escreveu um novo artigo seguindo a teoria dele (que eu não concordo, diga-se de passagem) de que a Microsoft não confia no .NET. Nesse artigo ele faz uma medição sobre a quantidade de managed assemblies no Windows Vista, e afirma que o uso do .NET no Windows Vista é muito pequeno, principalmente se for levado em consideração as delarações e pressões feitas pela Microsoft em cima do .NET.

    Uma das frases dele é "Microsoft appears to have concentrated their development effort in Vista on native code development" - o que até me parece óbvio já que estamos falando de um sistema operacional. O interessante é que eu estava justamente pensando em escrever algo sobre isso. A primeira coisa que eu fiz após instalar o Vista em casa foi instalar o WinDbg e fazer dump dos symbols de vários executáveis, para saber o quanto de .NET estava sendo usado no Vista. O Windows Explorer tinha algumas partes ou extensões feitas em .NET nos builds alfa do Longhorn. Não existe mais nada de .NET no Windows Explorer, assim como não existe nada de .NET na grande maioria dos executáveis do Windows Vista.

    Com esses meus experimentos, eu fiz algumas descobertas bem interessantes: sabe quais bibliotecas tiveram seu uso aumentado consideravelmente no shell do Windows (Explorer e seus adendos) em relação ao Windows XP? ATL e WTL. Outro executável que usa bastante ATL e WTL é o MMC, que agora aceita snapins feitos em .NET (mas continua sendo feito em "old unmanaged code").

    Ao invés do Dr. Grimes procurar referências usando os symbols, ele fez um programa para descobrir quais programas que fazem parte do Windows Vista usam realmente .NET. A surpresa é que a lista é muito pequena. O resultado do estudo dele não me espanta. Acho que ele exagera um pouco no FUD, mas é perfeitamente compreensível e perdoável (para mim, uma fonte claramente não-neutra) considerando o gigantesco FUD feito pela Microsoft e comunidade .NET sobre C/C++ e o "old unmanaged code". Só acho que ele podia ter falado mais de Indigo/WCF (que me parece ser feito todo em .NET) e Avalon/WPF (que é 50% managed em usermode e fortemente baseado em DirectX unmanaged em kernel mode). Não dá pra esquecer que o Avalon é um sistema de composição que tem quase nada de dependência do Win32.

    Me parece que ele compartilha da minha opinião: o WinFX é parte do Windows da mesma forma que o .NET Framework é. Os desenvolvedores usam para desenvolver aplicações para Windows, mas a própria Microsoft não usa (a não ser em raras situações visivelmente experimentais e para substituir o VBA e VB6). Me parece que nada (ou quase nada) no Windows Vista é feito usando WinFX ou até mesmo .NET. Só o WinFX mesmo é feito em .NET. Tudo no Windows continua sendo feito no velho e carcomido Win32. Parece que tudo que eu escrevi a dois anos continua válido.

    PS1: Fora o fato de que o WinFX ainda é muuuuito lento. Eu baixei o Expression da Microsoft que é 100% managed e 100% Avalon/WPF. Rodei tudo em um Turion64 com 512 de RAM e uma GeForce 256MB. O programa consome centenas de MBs de memória e nem chega a ser usável de tão lento que é. Até para desenhar o menu ele demora (sim, isso é sério). Mas, como isso deve melhorar até o RTM, é melhor esperar para ver.

    PS2: Dan Fernandez da Microsoft é um dos pregadores que sempre ajuda a defender a visão de que a Microsoft usa bastante .NET internamente. Para ilustrar o seu ponto de vista ele divulgou a quantidade de linhas de código C# usada em vários produtos Microsoft. Mas sua declaração de que o Visual Studio .NET tem 8 vezes mais linhas de código managed do que o Avalon/WPF me deixou em dúvida. Um sistema que quer substituir o Win32 e que é sabidamente complexo com só 900 mil linhas de código C#? Apesar de ser só uma especulação, ainda acho que o Avalon/WPF deveria ser bem maior do que o Visual Studio.

    Em 25/04/2006 20:28 - Comentários (11)

    Tentando programar usando um software de reconhecimento de voz

    http://it.slashdot.org/comments.pl?sid=184319&cid=15218343 :-D

    Em 28/04/2006 19:56 - Comentários (3)

    O melhor emprego de todos

    Melhor emprego dos EUA, segundo a CNN Money.

    Em 04/05/2006 14:18 - Comentários (3)

    As 5 mentiras

    Você se lembra dos 3 contos que as empresas contam na hora de contratar? Agora que você já foi contratado, é bom que conheça também as 5 mentiras mais comuns.

    Em 04/05/2006 17:22 - Comentários (2)

    Tutorial de STL, parte 0: Templates

    Eu já vinha pensando a bastante tempo em escrever uma série sobre STL, e finalmente resolvi deixar de ser preguiçoso e fazê-lo :-). A STL é um dos recursos mais importantes do C++ - na minha opinião é o mais importante - e mesmo assim muitos programadores não conhecem. Mas como a STL é baseada em templates, vou falar primeiro sobre templates para depois começar a falar da STL.

    A funcionalidade de templates (algo como "gabarito" ou "modelo" em português) foi criada basicamente para resolver o problema de funções que precisam tratar diversos tipos de dados. Vamos começar pelo exemplo mais simples possível, uma função que soma dois valores. Precisamos de uma função soma que consiga operar sobre diversos tipos de dados. Usando linguagem C, a solução mais simples possível é:

    int soma_int(int x, int y)
    {
      return x + y;
    }
    
    float soma_float(float x, float y)
    {
      return x + y;
    }
    
    double soma_double(double x, double y)
    {
      return x + y;
    }
    
    char soma_char(char x, char y)
    {
      return x + y;
    }
    
    
    /* e assim sucessivamente...
    
       Essas funções estão em C, que não suporta sobrecarga de funções
       (várias funções com mesmo nome, mas com parâmetros diferentes)
       Isso facilitaria o código que usa as funções (já que todos chamariam
       a função "soma"), mas não resolveria o problema do código duplicado.
    */
    
    int main()
    {
      
      soma_int(2,2);
      soma_float(2.99f, 2.99f);
      soma_double(5.9999, 6.9999);
      soma_char('2', '2');
    
      return 0;
    }
    

    Isso é muito trabalhoso (ok, você pode usar regex e gerar tudo isso, mas...), além do que você precisaria criar novas funções para cada novo tipo de dado. Além disso, uma modificação no algoritmo tornaria a manutenção um inferno para qualquer coisa mais complicada do que a soma de dois valores. O mais importante a se notar nesse trecho de código é que, apesar da declaração das funções serem diferentes, o código é exatamente o mesmo.

    Outra possibilidade (leia-se gambiarra) para quem usa C e gosta de fortes emoções, seria fazer uma função de soma que suportasse vários tipo de dados, usando ponteiro void para passar os parâmetros:

    #define MAIOR_TIPO_SUPORTADO __int64
    
    void* soma_emocionante(void* px, void* py, char tipo)
    {
      static char buffer[sizeof(MAIOR_TIPO_SUPORTADO)];
    
      if(tipo == 'i')
      {
        *((int*)buffer) = *((int*)px) + *((int*)py);
        return buffer;
      }
      else if(tipo == 'f')
      {
        *((float*)buffer) = *((float*)px) + *((float*)py);
        return buffer;
      }
      else if(tipo == 'd')
      {
        *((double*)buffer) = *((double*)px) + *((double*)py);
        return buffer;
      }
      /* se não suportamos o tipo, retornamos NULL */
      return NULL;
    }
    
    int main()
    {
      int a = 10, b = 20, c;
      c = *(int*)soma_emocionante(&a, &b, 'i');
    }
    

    Eu poderia encher laudas e laudas de texto apontando os defeitos desse código. Não suporta multithread (viu a variável estática?), não resolve o problema do código duplicado (só reduz o número de funções, o que é uma vantagem discutível nesse caso), você só consegue somar variáveis, é fácil de cometer um erro e tomar um GPF, etc, etc. Quanto mais formos tentando resolver esse problema usando os recursos normais do C e do C++ pré padrão ISO (quando os templates não existiam), mais o nosso código vai parecer uma grande gambiarra.

    É exatamente esse tipo de problema que os templates resolvem, eles geram todo esse código para você automaticamente, em tempo de compilação, sem remendos, sem void* e sem gambiarras. Vamos ver como ficaria esse código usando templates (agora já em C++ ISO):

    template <typename T>
    T soma(T x, T y)
    {
      return x + y;
    }
    
    
    int main()
    {
      soma<int>(2,2);
      soma<float>(2.99f, 2.99f);
      soma<double>(5.9999, 6.9999);
      soma<char>('2', '2');
    
      return 0;
    }
    

    Quando compilamos esse código, o compilador gera automaticamente o código para a função, substituindo o parâmetro T pelo tipo de dado passado como parâmetro para o template. Isso simplifica bastante o corpo da função, mas não facilita muito o uso, já que só trocamos a sintaxe soma_TIPO para soma<TIPO>. Mas veremos como os templates resolvem esse problema nos próximos posts.

    Em 09/05/2006 16:04 - Comentários (25)

    Desenvolvedores de drivers: uma notícia boa e uma notícia ruim

    Notícia maravilhosa: O IFS Kit - necessário para desenvolver drivers de file system, incluindo filtros - fará parte do DDK. Agora ninguém precisa mais pagar US$ 109,00 por ele (fiquei surpreso, o preço era US$ 800,00 até alguns meses atrás). Meu próximo filtro de file system vai sair beeeem mais barato. :-)

    Notícia péssima: Só drivers assinados com um certificado comprado da Verisign que custa US$ 500,00 por ano rodarão no Windows Vista x64. Não se esqueça que só empresas conseguem um certificado autenticado pela Verisign. Pensando bem, não vai ser tããão mais barato. :-(

    Em 16/05/2006 23:01 - Comentários (22)

    Como fazer debug remoto com o Visual C++ 7.1

    Primeiro é necessário habilitar o servidor de debug na máquina remota:

    • Reze um pouco...
    • Compartilhe a pasta "C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\Packages\Debugger" que está na sua máquina. Nesse exemplo, vou chamar a pasta compartilhada de vc7debug.
    • Baixe o PsExec do site do SysInternals. Esse aplicativo permite que você execute um programa em outra máquina pela rede, mesmo sem ter acesso via Terminal Sevices - contanto que você tenha permissões na máquina remota, é claro.
    • Execute o msvcmon.exe na máquina remota, usando o psexec. Rode a seguinte linha de comando NA SUA MÁQUINA:
      psexec \\MAQUINA_REMOTA -u DOMINIO\SEU_USUARIO \\SUA_MAQUINA\vc7debug\msvcmon.exe -tcpip -anyuser -timeout -1
    • O psexec vai pedir sua senha de rede, digite-a.

    Uma das formas de se fazer debug é dar um attach em um processo que já está rodando. Essa é a forma mais simples, e funciona assim:

    • No Visual Studio, menu Tools >> Debug Processes...
    • Em Transport coloque TCP/IP, e em Name coloque o nome da máquina remota.
    • Clique em Refresh e a lista de processos da máquina remota deve aparecer.
    • Pronto. Agora é só selecionar um e clicar em Attach.

    A outra forma - a mais complicada - é configurar o Visual Studio para que toda vez que você fizer o debug normal (F5, F10) ele conecte na máquina remota e execute o programa lá. Faça assim:

    • Antes de começar configurar o Visual Studio, compartilhe a pasta onde fica o executável na sua máquina (pasta Debug geralmente). Nesse exemplo vou compartilhar como bin_debug.
    • Abra as propriedades do projeto
    • No TreeView da esquerda escolha Debugging.
    • Em Working Directory configure o diretório de trabalho da máquina remota.
    • Em Remote Settings >> Connection escolha Remote via TCP/IP.
    • Em Remote Settings >> Remote Machine coloque o nome da máquina remota.
    • Em Remote Settings >> Remote Command, coloque \\SUA_MAQUINA\bin_debug\SEU_EXECUTAVEL.exe.
    • Clique em OK, e reze um pouco. Depois disso aperte F5 ou F10 e veja se o debug começa.

    O problema mais comum nesse tipo de configuração é relativo a permissões. Se o Visual Studio reclamar que não consegue abrir o executável, rode o filemon NA MÁQUINA REMOTA e veja qual o motivo da falha.

    Com essas configurações que eu passei, o msvcmon (servidor de debug) aceitará conexão de qualquer usuário, e isso pode ser um problema. Se precisar de mais segurança, veja os parâmetros do msvcmon (rode msvcmon /?) e configure a conexão via Named Pipes ao invés de TCP/IP.

    ACHO que para o Visual C++ 8 os passos são os mesmos...

    Em 17/05/2006 21:08 - Comentários (1)

    Tutorial de STL, parte 1/2: Mais templates

    No primeiro post da série vimos qual a função básica dos templates: transformar os tipos de dados em parâmetros, assim como já são os valores. Isso permite que você faça uma função - na realidade um template de função - que consiga tratar vários tipos de dados e continuar tipada. Essa é a base do "generic programming", separar o algoritmo do tipo de dado. Dessa forma, qualquer tipo de dado que tenha suporte aos requisitos exigidos para o funcionamento do algoritmo pode ser usado. Esses requisitos em relação a um tipo de dado tem o nome de "concept", e será nativamente suportado pela linguagem no C++0x. Veremos mais sobre isso em breve.

    No nosso primeiro exemplo resolvemos o problema de duplicação do código da função, mas não resolvemos o problema do código que chama a função, pois só trocamos a sintaxe soma_int para sintaxe soma<int>. O que eu não contei na primeira parte é que o compilador C++ faz a resolução automática de tipos para os parâmetros, sempre que possível. Nosso código pode ser modificado para:

    template <typename T>
    T soma(T x, T y)
    {
      return x + y;
    }
    
    int main()
    {
      soma(2,2);
      soma(2.99f, 2.99f);
      soma(5.9999, 6.9999);
      soma('2', '2'); // não, o resultado não será '4'...
    
      return 0;
    }
    

    Nesse caso específico, o compilador consegue saber com certeza qual o tipo de dado envolvido nas chamadas. Dessa forma podemos deixar o trabalho por conta dele. Mas existem alguns casos que não são claros:

      int i = 10;
      float f = 10.2;
    
      double d = soma(i,f);
    
    ------ Build started: Project: 1bit_stl, Configuration: Debug Win32 ------
    
    Compiling...
    1bit_stl.cpp
    1bit_stl.cpp(115) : error C2782: 'T soma(T,T)' : template parameter 'T' is ambiguous
            1bit_stl.cpp(96) : see declaration of 'soma'
            could be 'float'
            or       'int'
    

    Oops. Sabemos que o compilador poderia ser bonzinho e promover os dois parâmetros para double, fazer a conta, e retornar o resultado. Sim, mas lembre-se da filosofia C++: você só paga por aquilo que você pedir. Nos primeiros casos o compilador fez a dedução porque era absolutamente óbvio, nesse caso não é. Conversões implícitas podem ser uma praga a te perseguir, e o compilador C++ te livra disso exigindo que você diga exatamente o que você quer.

    Outra coisa que podemos notar nesse exemplo: não existe sobrecarga de retorno de função. O fato de colocarmos o retorno da função em uma variável double não mudou em nada o comportamento da resolução de tipo. Em C++ você não pode fazer sobrecarga de função mudando somente o tipo de retorno. E esse nosso template nada mais é do que uma função vitaminada, as regras continuam valendo. Como nesse caso precisamos ser explícitos, vale a sintaxe normal:

      int i = 10;
      float f = 10.2;
    
      double d = soma<double>(i,f);
    

    Dessa forma o compilador faz a promoção automática dos tipos int e float para double, o que não nos causa nenhum problema de perda de dados.

    No começo do post eu falei sobre abstrair o algoritmo do tipo de dados, e é o que fizemos com nossa função soma. O conceito é simples: independente do tipo do dado, os passos necessários para se fazer uma soma são os mesmos. O que muda é a forma como a soma que é feita, que é uma característica intrínseca do tipo. Esse mesmo conceito se aplica para algoritmos mais complicados, como os de ordenação. Eles comparam os valores entre si, e a comparação de valores é diferente para cada tipo de dado. Mesmo assim, o algoritmo de ordenação continua o mesmo.

    Um exemplo clássico que vou usar para ilustrar isso é uma classe para representar um número complexo. Um número complexo é representado com ai + b, sendo a e b dois números reais, e i a parte imaginária (sendo i ao quadrado igual a -1 e tendo seus detalhes explicados na Wikipedia como de costume). As operações de soma e subtração de um número complexo são feitas da seguinte forma:

    Esses algoritmos acima não se encaixam com o nosso template de soma:

      complex_number c1  = {10, 20}, c2  = {40, 30};
      soma<complex_number>(c1, c2);
    
    ------ Build started: Project: 1bit_stl, Configuration: Debug Win32 ------
    
    Compiling...
    1bit_stl.cpp
    1bit_stl.cpp(98) : error C2676: binary '+' : 'complex_number' does not define this operator 
      or a conversion to a type acceptable to the predefined operator
            1bit_stl.cpp(128) : see reference to function template 
      instantiation 'T soma(T,T)' being compiled
            with
            [
                T=complex_number
            ]
    
    Build Time 0:00
    1bit_stl - 1 error(s), 0 warning(s)
    
    ---------------------- Done ----------------------
    
        Build: 0 succeeded, 1 failed, 0 skipped
    

    Note que os passos para nosso complicadíssimo algoritmo ainda são os mesmos - passo único: efetuar a operação de soma - o que muda é forma como a soma é efetuada. Para ficar mais claro, imagine uma equação quadrada:

    Nesse caso a soma é só um passo do algortimo para se chegar ao resultado. A forma como a soma é feita é diferente para números inteiros (int) ou reais (double), mas a ordem dessa soma dentro dos passos do algoritmo ainda é a mesma.

    Voltando ao nosso super algoritmo de soma, precisamos de um tratamento diferenciado para somar números complexos. Veremos as várias formas de resolver isso no próximo post.

    Em 30/05/2006 16:06 - Comentários (6)

    Tutorial de STL, parte 3/4: Ainda templates

    Como prometido, nessa parte veremos como resolver o nosso problema de somar um número complexo e manter nosso código genérico.

    Como dito na parte 1/2, temos basicamente duas soluções para esse problema. A primeira chama-se especialização de templates e é utilizada justamente em situações onde o template não consegue resolver o problema para todos os tipos de dados existentes. No nosso caso, o template de soma não é compatível com números complexos, já que precisamos somar a parte real e a parte imaginária separadamente. Para resolver esse problema criaremos uma especialização do template soma que contemple nosso complex_number:

    template <typename T>
    T soma(T x, T y)
    {
      return x + y;
    }
    
    struct complex_number
    {
      double a;
      double b;
    };
     
    template <>
    complex_number soma<complex_number>(complex_number x, complex_number y)
    {
      complex_number c;
      c.a = x.a + y.a;
      c.b = x.b + y.b;
      return c;
    }
    int _tmain(int argc, _TCHAR* argv[])
    {
      complex_number c1  = {10, 20}, c2  = {40, 30};
      soma<complex_number>(c1, c2);
     
      return 0;
    }

    Nesse caso especializamos o template para tratar as particularidades do tipo complex_number. Note que a definição da especialização do template vem depois da definição da classe complex_number. Isso nos faz lembrar que essa especialização poderia ser fornecida juntamente com o header do complex_number, e não junto com o header do template soma. Dessa forma, quando você criar um tipo, você pode também fornecer junto com ele as especializações necessárias para que templates conhecidos usem o seu tipo.

    Outra solução - melhor na minha opinião - é fazer com que o nosso tipo complex_number suporte o operador de soma, para que ele possa ser somado como qualquer outro número. Essa solução é melhor por tentar equalizar o tipo complex_number com os demais tipos, fazendo com que ele possa ser usado por mais funções template sem precisar especializar todas elas. Nosso código ficaria dessa forma:

    template <typename T>
    T soma(T x, T y)
    {
      return x + y;
    }
     
    struct complex_number
    {
      double a;
      double b;
      complex_number operator+(const complex_number& c)
      {
        complex_number result;
    
        result.a = a + c.a;
        result.b = b + c.b;
     
        return result;
      }
    };
     
    int _tmain(int argc, _TCHAR* argv[])
    {
      complex_number c1  = {10, 20}, c2  = {40, 30}, c3;
    
      c3 = soma(c1, c2);
     
      ASSERT(c3.a == 50 && c3.b == 50);
     
      return 0;
    }
    

    Nesse caso o template de soma continua exatamente o mesmo, o tipo deve suportar ser usado pelo template, e não o contrário. É importante saber esse conceito, pois ele é bastante usado pela STL. Muitos templates da STL esperam que o tipo usando suporte determinados operadores ou que tenham determinados typedefs para funcionar. Isso é o que se chama de polimosfismo em tempo de compilação: ao invés deu usar interfaces (classes abstratas) para garantir a interface (forma como um tipo se apresenta) dos objetos, isso tudo é feito somente tendo os nomes iguais. Como os templates são resolvidos em tempo de compilação, essa verificação é segura e garante que tudo funcionará perfeitamente em tempo de execução. Isso também é uma limitação, já que a resolução só pode ser feita em tempo de compilação - ao contrário do dynamic_cast.

    Especialização de templates é um tópico interessante, já que é o recurso que possibilita a técnica arcana de metaprogramação, que nos permite fazer trechos dos programa que rodam em tempo de compilação. O exemplo clássico de metaprogramação usando templates é o cálculo do fatorial em tempo de compilação. Primeiro veja a implementação comum de fatorial, que é o exemplo clássico de uma função recursiva:

    int fatorial(int n)
    {
      if(n == 0)
        return 1;
      else
        return n * fatorial(n - 1);
    }
    

    Note que a recursão termina quando o valor - que diminuia a cada recursão - chega a zero. Podemos usar o mesmo conceito usando especialização de templates, já que uma especialização funciona como um if para aquela condição específica:

    int fatorial(int n)
    {
      if(n == 0)
        return 1;
      else
        return n * fatorial(n - 1);
    }
    
     
    // template de fatorial
    template<int n>
    struct fatorial_t
    {
      enum
      {
        value = n * fatorial_t<n - 1>::value
      };
    };
     
    
    // especialização para quando chegar a zero
    template<>
    struct fatorial_t<0>
    {
      enum
      {
        value = 1
      };
    };
     
    int _tmain(int argc, _TCHAR* argv[])
    {
      int f1 = fatorial(1);
      int f2 = fatorial(2);
      int f3 = fatorial(3);
      int f4 = fatorial(4);
    
     
      f1 = fatorial_t<1>::value;
      f2 = fatorial_t<2>::value;
      f3 = fatorial_t<3>::value;
      f4 = fatorial_t<4>::value;
     
      return 0;
    }
    

    A diferença entre fatorial e fatorial_t é que o primeiro é calculado em tempo de execução, e o segundo em tempo de compilação. Quando o programa está rodando, o valor de template_t<>::value é uma constante. E antes que você pergunte, não é necessário saber metaprogramação com templates para usar STL, só usei o tópico como um exemplo das utilidades da especialização de templates - essa sim, usada pela STL.

    Acho que já vimos o que precisamos para entender a STL, no próximo post chegaremos onde realmente interessa.

    Em 15/06/2006 19:52 - Comentários (4)

    Novos recursos da IDE do Visual C++ 2005

    Aos que ainda não sabem, a equipe do Visual C++ na Microsoft mantém o blog "Visual C++ Team Blog". Um dos integrantes da equipe -cujo nome é impronunciável - escreveu um post muito bom sobre os novos recursos da IDE do Visual Studio 2005 para o Visual C++.

    O melhor de todos: Intellisense Support for Makefile Projects. Isso ajuda muito quem é obrigado a compilar por fora do Visual Studio, como é o caso de quem faz drivers. Para compilá-los é necessário usar a versão do Visual C++ que vem junto com o DDK. Além do compilador, você também é obrigado (existem gambia^H^H^H^H^H^H^H adaptações técnicas para fugir disso, mas...) a usar o BUILD para compilar, e dessa forma você não teria um projeto do Visual Studio.

    Em 19/06/2006 19:35 - Comentários (6)

    Material da palestra ministrada na Semana de Engenharia Mauá

    Como prometido aos presentes, estou disponibilizando o material usado na palesta "Por dentro do Windows: Gerenciamento de Memória", ministrada na Semana de Engenharia do Instituto Mauá de Tecnologia:

    • PPT usado na apresentação.
    • Process Explorer, usado para visualizar o uso da memória pelos programas. Ele pode ser baixado diretamente do site da SysInternals.
    • Fontes do projeto (Visual C++ 2005) usado para gerar carga de trabalho sobre o Memory Manager e visualizar as modificações no WorkingSet usando o Process Explorer.

    Qualquer dúvida usem os comentários desse post ou o formulário de contato.

    Em 07/07/2006 01:27 - Comentários (8)

    Tutorial de STL, parte 1: O que é a STL

    A STL (Standard Template Library) é a biblioteca padrão da linguagem C++, e faz parte de todos os compiladores que seguem o padrão ISO C++. É uma biblioteca quase que totalmente baseada em templates, buscando simplicidade, produtividade e rapidez. Além disso, é um ótimo exemplo da filosofia do C++: você só paga (em termos de performance) por aquilo que você realmente usa. Justamente por ser baseada em templates, somente os templates usados gerarão código que será incluído no seu binário.

    Temos na STL alguns estilos básicos de templates, chamados concepts. Esse concepts são templates/objetos que seguem um mesmo estilo e funcionamento, mas sem um implementação de interface para garantir similaridade (falamos nissos nos primeiros posts dessa série). A similaridade é verificada pelo compilador em tempo de compilação, já que ele sabe exatamente como o objeto é composto. Vamos aos concepts básicos da STL:

    • Containers: São objetos usados para guardar conjuntos de outros objetos ou valores. Entre eles temos o vector (um array de acesso sequencial), o list (lista ligada) e o map (mapa associativo de chave e valor). Veremos vários exemplos de containers em um post especial sobre o assunto.
    • Iterators: É uma abstração criada para unificar a forma de percorrer todos os itens de um container, de forma que o mesmo algoritmo possa ser usado em diversos containers. Isso nos permite trocar de containers de acordo com o nosso uso. Por exemplos, um std::list é mais rápido do que um vector para inserir itens no meios do container. Por outros lado, como um list é uma lista ligada, para acessar o décimo item é preciso percorrer todos os itens até ele. Abstraindo o algoritmo do container o programador consegue escolher o melhor container, de acordo com o padrão de uso.
    • Algoritmos: Funções templates que manipulam de alguma forma os containers, modificando seu próprios itens ou gerando cópias. Como dito, são feitos com o mínimo possível de conhecimento sobre o container que será utilizado, para que possa ser usado de forma genérica na maior quantidade possível de situações e usos. Já falamos, de forma simplificada, sobre abstração do tipo de dados na implementação de algoritmos, e falaremos nisso em detalhes depois.

    Um dos pontos chaves da STL é (ao contrário da orientação a objetos clássica) separar os dados dos algortimos que o manipulam. Dessa forma, um algortimo construído de forma genérica consegue manipular vários tipos de dados em diversos tipos de containers. Isso permite que você monte seu programa juntando as peças que melhor resolvem o seu problema, e elas continuam se encaixando mesmo depois de mudadas. Eu sei que essa explicação chega a ser ridiculamente teórica e abstrata, mas eu prometo que veremos bastante código usando STL daqui para frente. Considere essa parte como aquele primeiro capítulo do livro que você precisa ler novamente depois de ler o livro inteiro. :-)

    Em 11/07/2006 04:08 - Comentários (5)

    Meu Daily WTF

    Caso clássico do The Daily WTF, acontecendo comigo agora:

    c:\sources>svn update svn: Working copy '.' locked svn: run 'svn cleanup' to remove locks (type 'svn help cleanup' for details) C:\sources>svn cleanup svn: Your .svn/tmp directory may be missing or corrupt; run 'svn cleanup' and try again svn: Can't open file '.svn\tmp\entries': The system cannot find the path specified. C:\sources>svn cleanup svn: Your .svn/tmp directory may be missing or corrupt; run 'svn cleanup' and try again svn: Can't open file '.svn\tmp\entries': The system cannot find the path specified.

    Grr..

    Em 12/07/2006 16:32 - Comentários (2)

    Desenvolvimento para Windows Vista em C++ (sem o CLI)

    Como não é lá muito comum encontrar material sobre desenvolvimento para Windows Vista em C++, vou colocar aqui algunsa links interessantes que encontrei sobre o assunto:

    • Top 10 Ways to Light Up Your Windows Vista Apps: Fala dos novos Common Dialogs, que como fazem parte do sistema operacional, são implementados como componentes COM. Fala também sobre algumas novidades do Windows que continuam sendo disponibilizados somente como API Win32 ou COM, como o novo Event Viewer, Task Scheduler 2.0 e o novo de gerenciador de transações que faz parte do kernel do Windows Vista.
    • Transactions, Aero Wizards, And Task Dialogs In Windows Vista: Explica como fazer wizards fazer usando MFC (ou mesmo a API Win32 diretamente) compatíveis e com a aparência do Windows Vista
    • Windows Vista Core Audio APIs: As APIs para a nova audio stack do Windows Vista está disponibilizada somente via COM, voltada a programadores C++

    Sem esquecer que todo o resto da MSDN continua válido: Win32, COM, DirectX, etc.

    (Editado em 08/08/2006: correção sobre a "Core Audio APIs")

    Em 01/08/2006 15:54 - Comentários (5)

    Debug Markup Language: Mais uma carta na manga do WinDbg

    Um recurso muito interessante e escondido do novo WinDbg chama-se DML, ou Debug Markup Language. É uma linguagem makup bem simples, com alguns recursos do HTML, como links. O melhor uso disso é que seu código pode chamar OutputDebugString (ou o equivalente da sua linguagem ou framework) passando ao invés de simples strings, links que podem ser clicados no WinDbg. Esses links disparam comandos do WinDbg, cuja utilidade é limitada somente pela criatividade e necessidade do programador.

    O link <exec cmd="kb">Visualizar a pilha</exec>" terá como label a frase "Visualizar a pilha" e executará o comando "kb" quando for clicado. Simples assim.

    Para demonstar a utilidade esse novo recurso, fiz um programa que simula um servidor que recebe conexões de clientes. Toda vez que um "cliente" se conectar, o programa montará um log em DML que terá um link para que as informações sobre o cliente sejam visualizados. Note que para que o WinDbg identifique as mensagens como DML, é necessário colocar <?dml?> antes das mensagem.

    
    #include <windows.h>
    #include <vector>
    
    #include <string>
    #include <sstream>
    #include <strsafe.h>
    #include <boost/shared_ptr.hpp>
     
    //
    // informações do cliente conectado
    //
    struct ClientInfo
    {
      DWORD ClientId;
      wchar_t ClientName[64];
      DWORD ConnectTime;
    };
     
    int APIENTRY wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
    {
      std::vector<boost::shared_ptr<ClientInfo> > ClientVector;
     
    
      for(DWORD a = 0 ; a < 10 ; ++a)
      {
        std::wstringstream buf;
     
        //
        // criamos um novo cliente
        //
        boost::shared_ptr<ClientInfo> pNewClient(new ClientInfo());
    
     
        //
        // informações do novo cliente "conectado"
        //
        pNewClient->ClientId = a;
     
        //
        // montamos o nome dele com a versão segura
        // do sprintf
        //
    
        StringCbPrintfW(pNewClient->ClientName, 
            sizeof(pNewClient->ClientName), 
            L"Client %d", 
            pNewClient->ClientId);
     
        pNewClient->ConnectTime = GetTickCount();
     
        ClientVector.push_back(pNewClient);
     
        //
        // monta o DML com um link que mostra nosso cliente
        // no prompt do Windbg
        //
        buf << L"<?dml?><exec cmd=\"dt ClientInfo " << std::hex << pNewClient.get() 
          << "\">" << pNewClient->ClientName << "</exec>\n";
    
     
        OutputDebugString(buf.str().c_str());
     
        Sleep(3000);
      }
     
      //
      // isso é só um programa de testes, lembra?
      //
      Sleep(60000);
     
      //
    
      // olha mamãe, sem o garbage collector :-)
      //
     
      return 0;
    }
    
    

    E o resultado visto no WinDbg, depois de clicar no link "Client 4":

    Não esqueça que para que isso funcione você precisará da versão mais atual do WinDbg.

    Em 07/08/2006 13:29 - Comentários (3)

    Repita comigo novamente: Dilbert não é ficcção

    Em 07/08/2006 13:45 - Comentários (2)

    Microsoft libera kit para desenvolver jogos em .NET para XBOX 360

    Microsoft Invites the World to Create Its Own Xbox 360 Console Games for the First Time

    Como esse título bonito, a Microsoft anunciou que o SDK e as ferramentas para desenvolvimento de jogos para XBOX 360 - o "XNA Studio" - será disponível para meros mortais. Isso contrasta com o funcionamento atual do mercado, onde o kit para desenvolver jogos para um video game custa milhares de dólares e muitas vezes exige a assinaturas de contratos e NDAs.

    Dentro do press release existe um trecho "Visual Studio® Express and .NET", que me leva a pensar em duas coisas. A primeira é que a Microsoft portou o .NET para o XBOX 360, algo muito interessante. Isso não deve ter sido algo muito trivial, já que o XBOX roda um versão bastante modificada do Windows NT, além de usar um processador onde o .NET não roda hoje, o PowerPC. Alguém já leu algo sobre esse esforço? A segunda coisa é: espero que a Microsoft não tenha a péssima idéia de só permitir desenvolver jogos feitos em .NET com esse kit, forçando os amadores a adotar o Managed DirectX (já que os profissionais não o fazem)...

    Adicionado em 14/08/2006, 15:00:
    É, parece que aconteceu mesmo do jeito que eu temia. Segundo um post em um forum sobre o XNA Studio, somente será permitido jogos feitos em .NET...

    Em 14/08/2006 19:44 - Comentários (9)

    Tutorial de STL, parte 2: Containers

    Containers são objetos que contém outros objetos, como listas, pilhas, filas, etc. Os suporte a containers é um dos pilares da STL.

    O primeiro container que veremos - e também o mais simples - é o vector. Ele funciona de forma muito parecida com a array comum do C, com algumas diferenças básicas: o gerenciamento de memória é automático, seu tamanho pode ser modificado durante o tempo de execução, e quando você passa um std::vector como parâmetro a informação de tamanho não se perde. Isso sem contar as facilidades...

    Como um trecho de código vale mais do que uma porta de avião caindo, vamos a ele:

    
    #include <iostream>
    #include <vector>
    #include <assert.h>
     
    using std::cout;
    using std::endl;
     
    int main(int argc, char* argv[])
    {
      std::vector<int> v1(10);
      std::vector<int> v2;
    
     
      cout << "tamanho inicial de v1:" << v1.size() << endl;
      cout << "tamanho inicial de v2:" << v2.size() << endl;
    
     
      cout << "um item de v1" << v1[5] << endl;
     
      for(int a = 0 ; a < 10 ; a++)
        v2.push_back(a);
    
      cout << "novo tamanho de v2:" << v2.size() << endl;
     
      // exclui o último item do vector, reduzindo seu tamanho em 1
      v1.pop_back();
    
      cout << "um item de não existente em v2:" << v2[15] << endl;
     
      // a memória é contígua, como no array
      assert(&v2[5] - &v2[4] == 1);
      assert(((char*)&v2[5]) - ((char*)&v2[4]) == sizeof(int));
     
    
     
      return 0;
    }
    
    

    Algumas coisas merecem explicações, vamos à elas. O vetor v1 foi alocado da mesma forma que um array C, só que como ele é um objeto o tamanho é passado no construtor. A diferença mais notória entre o array e o vector é que os valores do vector são inicializados com zero. Na realidade, o que acontece é que o objetos criados no vector, são inicializados usando o seu construtor default. E o construtor default do tipo int inicializa a variável com 0. Um exemplo é o seguinte trecho de código:

    
      int x = int();
      std::cout << x << std::endl
    
    

    O código acima mostra que a variável x tem valor 0.

    Outra coisa notória é que, da mesma forma que um array C, é possível acessar um item com índice maior do que o tamanho do vector, usando o operador []. No código de exemplo eu acesso o item 15 de um vector que tem tamanho 10, e isso não gera exceção (mas, é claro, gera problemas). Nenhum container STL faz verificação de limite de tamanho usando o operador [], cabe ao programador verificar isso antes do acesso. Isso funciona dessa forma para que o acesso aos containers STL fique (quase) tão rápido quanto o acesso a um array. Filosofia C++: você só paga por aquilo que você usa. Caso você queira verificação de tamanho durante o acesso, o vector provê o método at(), que dispara um exceção caso o índice esteja além dos limites do container.

    O método push_back(), encontrado em vários containers, adiciona um item no final do container, aumentando o seu tamanho em um item. Após o loop do código acima, o método size() do container retornará 10. Da mesma forma, o método pop_back() exclui o último item do array, reduzindo seu tamanho em 1.

    Cada container STL tem suas característica particulares, e as do vector são basicamente:

    • suporte a acesso linear e aleatório dos elementos;
    • tempo constante para inserção e remoção de elementos no final do container;

    Para mais informações veja a Referência do std::vector no site da SGI.

    Em 16/08/2006 23:51 - Comentários (11)

    Disponibilizado o Service Pack 1 do Visual Studio 2003 (Visual C++ 7.1)

    Download
    Lista de bugs do Visual C++ 7.1 que foram corrigidosEm 18/08/2006 19:59 - Comentários (2)

    Entreviste a empresa

    A entrevista de emprego, que causa pavor em alguns profissionais, é muitas vezes vista de forma distorcida. Ela é usada basicamente para avaliar se o candidado está apto a trabalhar na empresa e para fazer a negociação salarial. Mas o ponto mais importante de uma entrevista de emprego é ignorado por muitos profissionais.

    Durante uma entrevista de emprego, a empresa faz a parte dela em te avaliar e testar. A sua parte é avaliar e testar a empresa, e não ser um agente passivo, como se estivesse fazer uma prova qualquer. Você DEVE avaliar muito bem a empresa, ela também tem que passar na sua prova. Não se esqueça que você será um entre muitos outros funcionários dessa empresa, mas esse será seu único emprego durante bastante tempo, o lugar onde você passará 8 horas do seu dia, 5 dias da semana. Considerando que você provavelmente passa mais tempo no trabalho do que em casa, você deve o mesmo cuidado para escolher uma empresa que você tem ao escolher uma casa para morar.

    Não se esqueça que a empresa te contrata porque ela precisa de você e das suas qualificações. Ela não está fazendo nenhum favor em te contratar, e somente o salário não é suficiente como pagamento. Você deve exigir respeito e um bom ambiente de trabalho, e não deixar que o trabalho interfira na sua vida pessoal. Essa é minha opinião, mas deixa eu parar com esse papo antes que isso pareça um artigo de auto-ajuda...

    Segue uma lista de coisas que eu acho importante perguntar durante a entrevista, coisas que você precisa saber antes de começar a trabalhar na nova empresa:

    • Detalhes sobre o projeto que você vai participar: tamanho do projeto, quantidade de pessoas, e principalmente, qual seu papel no projeto. Você pode descobrir tarde demais que você além de programar você terá que fazer implantação fora do horário uma vez por semana. É bom saber isso antes de começar.
    • Outras linguagens de programação: Se você não gosta de trabalhar com outra linguagem que não a sua do coração, é bom deixar bem claro isso durante a entrevista ou negociação. Isso pode te trazer sérios problemas futuramente. Muitos profissionais de RH não sabem que linguagem de programação não é uma questão de conhecimento, e sim de escolha. Eu tenho 8 anos de experiência com VB6, nem por isso eu aceitaria um emprego para fazer isso o dia inteiro, a não ser por muito dinheiro.
    • Salário e questões financeiras: Parece óbvio, mas muita gente (inclusive eu a alguns anos atrás) tem sérios problemas para conversar sobre dinheiro. Combine tudo direitinho, com o valor exato. Nada de "nós pagamos a média do mercado". Você precisa saber exatamente o valor do seu salário, não uma estimativa. Em muitas empresas as pessoas usam a tática de ficar empurrando a negociação de salário para outra pessoa - quando você fala com o gerente da área ele diz que isso é assunto do administrativo, e quando você fala com o administrativo eles falam que não têm autonomia para decidir. Você tem que saber o valor exato do seu salário, o dia do mês que ele será pago, e quem é o responsável por negociar seus aumentos.
    • Política de horas extras: Como nem todo mundo tem o auto-respeito (alguns chamariam de coragem) de negar qualquer proposta para trabalhar de graça, em muitas empresas as pessoas trabalham de graça e todo mundo acha isso normal. Caso você não goste de trabalhar de graça, acho bom que saiba antes de começar em um emprego onde você não terá muita opção. Caso a empresa só dê a opção de banco de horas, certifique-se que eles têm uma política para que você realmente possa tirar a folga correspondente às horas trabalhadas a mais. É muito fácil para empresa dizer que tem banco de horas, mas que você só pode tirar as horas quando "as coisas estiverem tranquilas". As coisas nunca estarão tranquilas.
    • Política de uso de Internet: Nós (programadores|analistas|engenheiros de sistemas) sabemos a importância da Internet para fazer nosso trabalho, não vou falar sobre isso aqui. Mas em muitas empresas, pricipalmente as empresas cuja atividade principal não é desenvolver software, as pessoas não sabem disso (e os administradores de rede não ajudam em nada). Certifique-se que você terá acesso aos sites de programação que você precisa.
    • Algum gerente precisa saber dos seus acordos. Não adianta combinar muita coisa com uma funcionária do RH e seu gerente não saber. Se ela sair, você ficará na mão e talvez até passe por mentiroso. Se você fez algum combinado sobre algo técnico (como "não mexo com SQL"), isso não siginifica nada para um funcionário do departamento pessoal. Seu (futuro) gerente é a única pessoa que tem poder suficiente para cobrar as coisas que você combinou durante a entrevista. Cuidado com as consultorias, elas geralmente usam a tática de manter um pelotão de mulheres bonitas no RH e como gerentes de contas. Elas muitas vezes nem sabem direito o que você vai fazer, e as consultorias contam com seu cavalheirismo ou falta de reação imediata para que você não arrume encrenca por causa de um combinado não cumprido. Não se esqueça de pedir detalhes do cliente onde você será alocado, e NÃO ESQUEÇA DE LER O CONTRATO COM ATENÇÃO.

    Nunca deixe de verificar esses pontos, principalmente se o motivo para não perguntar for medo de não parecer chato ou pedante. Tentar saber exatamente como funciona a empresa e ter tudo combinado antes de começar é uma atitude madura e altamente profissional. Se as pessoas que estão te entrevistando forem profissionais sérios, eles perceberão isso.

    Não esqueça que você pode usar as mesma táticas da empresa ao seu favor. As empresas costumam fazer pesquisas sobre os candidatos, mesmo que algumas delas sejam ilegais (como checar se o candidato tem problemas no SPC ou SERASA). Faça também suas pesquisas:

    • Básico do básico: pesquise sobre a empresa no Google, veja o que ela faz e o que falam dela. Você pode até procurar na APINFO os currículos de profissionais que já trabalharam lá. Assim você pode ter uma idéia de quanto tempo as pessoas costumam ficar na empresa.
    • Você pode pesquisar a situação da empresa junto à Receita Federal e outros orgãos publicos. Acessando o registro.br e digitando o domínio da empresa você consegue facilmente o CNPJ dela.
    • Pesquise sobre a empresa no Orkut, e não se esqueça que eles com certeza pesquisarão sobre você. É comum que existam comunidades de funcionários e ex-funcionários, você pode saber a opinião deles - o que é difícil, já que a maiorias das pessoas morre de medo de falar sobre a empresa - e conhecer o perfil de alguns dos seus futuros colegas de trabalho.

    Alguns dos requisitos que eu citei podem não existir e você pode achar que deve trabalhar nessa empresa da mesma forma. Sem problemas, só pense bem se esses pequenos detalhes não se tranformarão em uma grandes problemas no fututo. Existem (poucas) empresas flexíveis gerenciadas por pessoas inteligentes, você pode discutir as políticas com eles e convecê-los a revê-las.

    Mesmo depois de todos esses cuidados você ainda corre o risco de subir em uma barca furada. Ainda bem que existe APINFO.com :-)

    Em 30/08/2006 15:37 - Comentários (14)

    Palestra no DEVTECH 2006

    No dia 23 de setembro (próximo sábado) será realizado na Microsoft Brasil o DEVTECH 2006. Esse evento é mais uma das inúmeras iniciativas do DotnetRaptors, e terá um auditório com palestras para desenvolvedores e outro com palestras para o pessoal de Infra. O evento é gratuito, e as inscrições podem ser feitas diretamente no site de eventos da Microsoft.

    A novidade é que dessa vez, além dos palestrantes de costume, o escova bits que vos fala também falará aos presentes. Minha palestra será a "Por dentro do Windows: Gerenciamento de Memória", onde eu pretendo explicar como funciona o gerenciamento de memória do Windows e como os programadores podem usar esse conhecimento para melhorar seus programas. Como esse é um tópico low level sobre Windows, o conteúdo da palestra é de interesse de qualquer programador, independente da linguagem, runtime ou framework. E apesar da palestra não ser diretamente sobre codificação, é claro que a demo será feita em C++ :-)

    Em 18/09/2006 19:02 - Comentários (4)

    Downloads da palestra ministrada no DEVTECH 2006

    Como prometido aos presentes, estou disponibilizando o material usado na palesta "Por dentro do Windows: Gerenciamento de Memória", que ministrei no DEVTECH 2006.

    Qualquer dúvida usem os comentários desse post ou o formulário de contato.

    Em 25/09/2006 19:58 - Comentários (6)

    Apresentando o projeto OmniObjects

    Apresento aos meus prezados leitores meu primeiro projeto open source, o OmniObjects. Esse projeto tem o (nada) humilde objetivo de substituir do Microsoft DCOM com algo mais extensível, mais configurável e open source.

    Como todos sabem, eu gosto muito de COM, acho o padrão binário do COM um conceito simples, extensível e extremamente multiplataforma. O Firefox (que roda em mais plataformas e sistemas operacionais que meus dedos conseguem contar), por exemplo, é todo feito em XPCOM, que é uma versão do COM feita pelo pessoal da Mozilla Foundation. O CORBA usa exatamente o mesmo conceito. Além desse padrão binário ser independente de linguagem (coisa que o .NET também é), ele também é independente de runtime (coisa que tudo baseado em .NET não é). Não adianta, a coisa mais multiplataforma que existe é C/C++, ainda não inventaram nada que seja mais portável do que um código fonte.

    A Microsoft resolveu os problemas do DCOM criando a plataforma .NET, o Remoting e o Windows Communication Foundation. Muito bom, mas se você pretende continuar no C++ nativo, bare metal, sua única alternativa é continuar convivendo com as qualidades e com as limitações do DCOM, além da certeza de que a Microsoft não investirá mais muito tempo nisso. Apesar do DCOM ser uma plataforma madura e testada, lhe falta um toque de Internet. DCOM funciona muito bem em redes locais, mas não é configurável o suficiente para ser usado via Internet. Aí entra o projeto OmniObjects.

    O OmniObjects faz exatamente o que faz o DCOM: leva uma chamada à um método de um objeto de uma máquina para outra. Só que ao invés de existir um serviço no Windows que atende requisições em uma porta fixa, você mesmo pode fazer o host do componentes, da mesma forma que no .NET Remoting. Como um código vale mais que mais um entre os milhões de projetos no SourceForge, veja isto:

    int main()
    {
      OmniInitialize();
     
      OmniInitializeHosting(L"TCP", L"12345");
    
      //
      // pronto, já estamos atendendo requisições
      // usando TCP, e na porta 12345. 
      // 
      // "Olhe mamãe, sem a porta 135!"
      //
      _getch();
     
      OmniUninitialize();
     
      return 0;
    }
    

    Fazer host de objetos COM usando OmniObjects parece simples. E você nem precisa de loop de mensagens como no COM, ele cria uma thread separada para atender as requisições. O código para instanciar um objeto remoto também não é lá muito complicado. Veja com seus próprios olhos:

    int main()
    {
      OmniInitialize();
      {
        //
        // esse é o nosso smart pointer mais smart do que os outros
        // você não precisa usá-lo, pode usar _com_ptr_t, CComPtr
        // ou gerenciar na mão, como no tempo das cavernas
        //
        omni_ptr<ITest1> pTest1; 
        CComBSTR bstr(L"Olhe mamãe, eu consigo passar pelo firewall!"), bstrOut;
        unsigned int a = 10;
     
        OmniCreateRemoteInstance(L"tcp://127.0.0.1:12345", CLSID_Test1,
                                 NULL, __uuidof(ITest1), pTest1.ppvoid());
     
        pTest1->Method1(bstr, 25);
        pTest1->Method2(&bstr, &a, &bstrOut);
      }  
      OmniUninitialize();
      return 0;
    }
    

    Todo o resto é COM. O que muda em relação ao DCOM é a instanciação do objetos e o hosting. Claro, não tem segurança integrada com Windows, mas isso muitas vezes é uma vantagem. Imagine que você está fazendo um Instant Messenger. Usando OmniObjects você nunca mais vai precisar usar sockets e ficar fazendo parse de pacotes na memória. O código para enviar uma mensagem pode ser algo simples como pInstantMessengerServer->SendMessage("user12312", "Free your mind");. Só não esqueça que apesar de tudo parecer maravilhoso, o projeto OmniObjects ainda está na versão 0.10. Tudo ficará mais maravilhoso ainda (inclusive chamadas via HTTP, etc) ao longo do tempo. :-)

    Para fazer com que seu objeto sejá acessível pelo OmniObjects, tudo que você precisa fazer é compilar seu arquivo IDL com o OmniIdl.exe e compilar o projeto Visual C++ 8.0 que ele gerará. Isso produzirá a DLL de proxy e stub que fará toda mágica funcionar. Para mais detalhes e instruções veja a página do projeto OmniObjects.

    O OmniObjects é um projeto open source hospedado no SourceForge. Ele está disponibilizado usando licença BSD, que é basicamente: use os fontes para o que você quiser - inclusive para ficar milionário com um aplicativo comercial baseado nele e não me dar nenhum centavo e nem os fontes modificados de volta - contanto que não me responsabilize por nada que acontecer pelo uso desse fonte. Baixe a versão 0.1 enquanto ainda está quente e faça um bom proveito.

    Em 28/09/2006 04:42 - Comentários (18)

    Quer um bom motivo para migrar para o Visual C++ 8?

    Existem mais coisas, mas acho que só pelo preview de containers STL já vale a migração. Aos programadores Visual C++ 6.0, o que eu já escrevi para o Visual C++ 7.1 também vale para a versão 8.

    Em 02/10/2006 14:48 - Comentários (10)

    Disponível o OmniObjects versão 0.20

    Arrumei um boa desculpa para minha falta de posts no blog: minha profunda dedicação ao projeto OmniObjects. :-)

    Essa semana eu disponibilizei no site do projeto no SourceForge o OmniObjects versão 0.2, que agora tem toda a funcionalidade necessária para ser usável em algum projeto. Agora o proxy já faz o controle do contador de referência e libera o objeto remoto quando ele deixa de ser usado. Além disso, o suporte ao QueryInterface remoto também está funcionando perfeitamente. Qualquer componente com oleautomation pode ser usado com o OmniObjects, sem restrições.

    Aos que estão estudando programação Win32 em C/C++, o código fonte é bem ilustrativo e comentado. Lá você pode ver alguns usos práticos de DLLs, COM, ATL, Winsock, threads e sincronização entre outros. Você pode somente ver os fontes do OmniObjects diretamente no seu browser ou baixá-los diretamente do servidor SubVersion do SourceForge. Os fontes também estão contidos no ZIP do release 0.2.

    Em 14/10/2006 01:17 - Comentários (11)

    Tutorial de STL, parte 3: mais containers

    Já vimos os conceitos de containers na parte 2, além de brincar um pouco com o funcionamento do std::vector. Vamos aproveitar que estamos familiarizados com os conceitos, para conhecer alguns dos outros containers disponibilizados pela STL.

    Como vimos, o vector tem muitas similaridades com o array C, os objetos são mantidos na memória de forma contígua, o que permite os elementos sejam acessados rapidamente de forma aleatória - para encontrar um item só é preciso fazer uma soma. Apesar dessa grande vantagem, o vector tem uma grande desvantagem: quando um item é inserido no meio do container, é necessário mover todos os itens posteriores para haver espaço para o novo elemento. Isso pode ser demorado se o container tem uma grande quantidade de itens. Esse problema ocorre também quando precisamos inserir um item no início, o que é comum.

    Quando precisamos guardar dados de uma forma onde a inserção e remoção de itens seja mais eficiente, geralmente usamos uma lista ligada. Como um item tem um ponteiro para o próximo (e o anterior no caso de uma lista duplamente ligada), para inserir um item no meio só precisamos trocar o conteúdo de alguns ponteiros. Na STL a lista ligada é implementada na forma do container std::list. Vamos a alguns exemplos:

    #include <list>
    #include <string>
    #include <iostream>
     
    struct Pessoa
    {
      std::string _nome;
      unsigned int _idade;
    
      Pessoa(std::string nome, unsigned int idade):
        _nome(nome), _idade(idade)
      {
      }
    };
     
    int main()
    {
      std::list<Pessoa> pessoas;
     
      //
      // podemos inserir um itens no final..
      //
      pessoas.push_back(Pessoa("José João", 26));
     
      //
      // ... e no início, da mesma forma que com o vetor
      //
      pessoas.push_front(Pessoa("João José", 28));
    
      std::cout << "Primeira pessoa: "<< pessoas.front()._nome << std::endl;
      std::cout << "Segunda pessoa: " << pessoas.back()._nome << std::endl;
    
     
      //
      // mas isso não funciona, ao contário do vector
      //
      //pessoas[0];
     
      return 0;
    }

    Outro container bastante usado é o std::map, que cria um mapa entre chave e valor:

    #include <map>
    #include <string>
    #include <iostream>
    #include <sstream>
    
     
    struct Pessoa
    {
      std::string _nome;
      unsigned int _idade;
     
      Pessoa(): _idade(0) {}
     
      Pessoa(std::string nome, unsigned int idade):
        _nome(nome), _idade(idade)
      {
      }
    };
    
    int main()
    {
      std::map<unsigned int /* ID */, Pessoa> PessoasPorID;
     
      //
      // vamos "cadastrar" 100 pessoas
      //
      for(int a  = 0 ; a < 100 ; ++a)
      {
        std::stringstream nome;
    
        nome << "Pessoa " << a;
    
        //
        // caso não exista item para essa chave, ela é criada
        //
        PessoasPorID[a] = Pessoa(nome.str(), a);
      }
    
      Pessoa p;
      //
      // vamos pegar a pessoa com ID 50
      //
      p = PessoasPorID[50];
      std::cout << p._nome << " tem " << p._idade << " anos." << std::endl;
    
      //
      // Se você não tem certeza que o item existe,
      // não pode usar o operador []. 
      // Diferentemente do std::vector, ELE CRIARA O ITEM
      // 200 AUTOMATICAMENTE.
      // Maravilhas do C++: na dúvida, veja o fonte do operator[]
      // do tipo std::map.
      //
      p = PessoasPorID[200];
      std::cout << p._nome << " tem " << p._idade << " anos." << std::endl;
    
      //
      // se não tem certeza da existência, faça assim:
      // (eu sei, não é a forma mais eficiente, mas
      //  ainda não vimos iterators nessa série, lembra?)
      //
      if(PessoasPorID.find(400) != PessoasPorID.end())
        p = PessoasPorID[400];
     
      return 0;
    
     
    }

    Além desses containers notáveis que já vimos - todos baseados em templates - existem outros containers mais específicos, e outros chamados de adaptadores. O adaptadores criam containers com funcionamentos específicos usando o armazenamento de outros containers. Entre eles estão o std::queue (fila), e o std::stack (stack), que encapsulam outros containers mas disponibilizam somente as operações que fazem sentido para o tipo usado - push e pop para pilha, por exemplo.

    Mais informações
    MSDN: Standard C++ Library Reference
    SGI: Index: The Standard Template Library
    Wikipedia: C++ Standard Library

    Em 10/11/2006 16:53 - Comentários (6)

    Tutorial de STL, parte 4: iterators

    Apesar de existir muito mais coisas para falar sobre containers, vamos ver hoje o que são e para que servem os iterators. Mesmo porque, muitas das diferenças entre os containers só podem ser demonstradas usando iterators.

    Os iterators são um abstração da forma de acesso aos itens de um container. Eles foram criados para que os itens dos mais diversos containers possam ser acessados de uma mesma forma. Isso constitui o pilar da programação genérica, já que o código que manipula os itens de um container fica independente dos detalhes da implementação do container.

    Já vimos o std::vector, e sabemos que ele armazena os itens de forma sequencial. Da mesma forma, sabemos que o std::list armazena os itens em forma de uma lista ligada. Sendo assim, o método de acesso para itens dos dois containers é diferente. No caso do std::vector, para pegar o próximo item devemos pegar o ponteiro do item atual e somar um. No caso do list, devemos pegar o ponteiro para o próximo item diretamente na struct que controla o item, e derreferenciá-lo. No caso de uma red-black tree, como é comumente implementado o std::map, o acesso fica ainda mais complicado.

    O iterators são análogos aos ponteiros. São variáveis que quando derreferenciadas, pegam o conteúdo de uma varíavel. Como um trecho de código com comentários vale bem mais do que um trecho de código sem comentários, vamos ao que interessa:

    #include <iostream>
    #include <vector>
    
    #include <list>
    #include <map>
    #include <string>
    #include <sstream>
     
     
    
    int main()
    {
       static const unsigned int n = 10;
       int array[n];
       std::vector<int> v;
       std::list<int> l;
       std::map<int, std::string> m;
    
       //
       // preenchedo o array
       //
       for(int a = 0 ; a < n ; ++a)
       {
          //
          // vamos colocar itens não sequenciais, usando o 
          // dobro da nossa variável de controle
          //
          int x = a * 2;
    
          //
          // vou usar um stringbuffer para converter de int
          // para string. Nada de itoa, isso é C++ :-)
          //
          std::stringstream buffer;
          buffer << a;
     
          array[a] = x;
          v.push_back(x);
          l.push_back(x);
          m[x] = buffer.str();
       }
     
       //
       // pronto, agora temos todos os containers (sim, um array C
       // também é um container) preenchidos. Vamos à nossa primeira
       // tentativa (inútil) de acessar os itens da mesma forma
       //
       for(int a = 0 ; a < n ; ++a)
       {
          int x;
     
          x = array[a];
          x = v[a];
    
          // isso não funciona, o list não suporta acesso por índice
          // x = l[a];
    
          // também não funciona, essa sintaxe procura pela chave do map
          // e não pelo índice no map. Além disso, o retorno dessa expressão
          // é do tipo string, veja a declaração do map
          // x = m[a];
     
       }
     
       //
       // agora vou acessar os itens do array via ponteiro.
       // incrementamos o ponteiro até o fim do array
       //
       std::stringstream buffer;
       buffer << "array: ";
     
       for(int* i = array ; i != array + n ; ++i)
       {
          buffer << *i << ", ";
       }
    
     
       std::cout << buffer.str() << std::endl;
     
       //
       // veja como o acesso ao vector funcionará quase da mesma forma,
       // só muda a inicialização.
       //
       buffer.str(""); buffer.clear(); 
       buffer << "std::vector: ";
       for(std::vector<int>::iterator i = v.begin() ; i != v.end() ; ++i)
       {
          buffer << *i << ", ";
       }
       std::cout << buffer.str() << std::endl;
    
     
       //
       // Mais uma vez. O acesso ao std::list funciona da mesma forma que
       // para o array e para o std::vector
       //
       buffer.str(""); buffer.clear();
       buffer << "std::list: ";
       for(std::list<int>::iterator i = l.begin() ; i != l.end() ; ++i)
       {
          buffer << *i << ", ";
       }
       std::cout << buffer.str() << std::endl;
    
     
       //
       // O acesso ao map é um pouco diferente porque temos chave e
       // valor, mas o conceito continua o mesmo
       //
       buffer.str(""); buffer.clear();
       buffer << "std::map: ";
       for(std::map<int, std::string>::iterator i = m.begin() ; i != m.end() ; ++i)
       {
          buffer << "(" << i->first << "," << i->second << ") ";
       }
       std::cout << buffer.str() << std::endl;
    
     
       return 0;
    }

    O que deve ser notado no exemplo acima é que, independente do container, a forma de acesso é a mesma. O método begin() retorna um iterator para o primeiro item do container (por exemplo, a expressão *v.begin() retorna o conteúdo do primeiro item). O método end() retorna um item APÓS o último, e não pode ser derreferenciado, pois ele deve ser usado para testar se o iterator já passou do final. Mais código:

    #include <vector>
    #include <assert.h>
    
    int main()
    {
    
       std::vector<int> v;
       // 
       // Esse é o tipo do iterator para um vetor de int. 
       // Todos os containers da STL seguem esse padrão, o tipo do iterator
       // é definido por um typedef dentro do próprio container
       //
       std::vector<int>::iterator i;
    
       //
       // container vazio, begin() é igual a end()
       //
       assert(v.begin() == v.end());
    
       v.push_back(10);
       v.push_back(20);
    
       i = v.begin();
       assert(*i == v[0]);
    
       assert(v.begin() != v.end());
    
       // o primeiro item é igual a 10
       assert(v[0] == 10);
    
       // o begin() aponta para o item de índice 0
       assert(v[0] == *v.begin());
    
       // posso somar iterator como somo ponteiros.
       // begin() + 1 aponta para o segundo item.
       assert(*(v.begin() + 1) == 20);
    
       // temos somente dois itens. O item depois do último é o end()
       assert(v.begin() + 2 == v.end());
    
       return 0;
    }
    

    Mais informações no tópico sobre iterators na página da SGI.

    Em 22/11/2006 15:17 - Comentários (6)

    Posts por categoria e outras melhorias na organização

    Ao longo desses mais de dois anos de blog já escrevi sobre diversos assuntos, de C++ a Windbg, de dicas para entrevistas até divagações sobre computação distribuída. Muito bom para quem acompanha tudo via rss, mas muito ruim para quem quer ler tudo depois de algum tempo.

    O formato de blog é muito bom para publicar pensamento aleatórios, o problema é que a chave primária é uma data. Um mês depois, ninguém se importa se eu falei do Visual C++ Express no dia 20 de novembro ou 17 de outubro, o que interessa é a informação. Eu coloquei um formulário de busca faz um tempo, mas ainda não resolveu o problema. Então, esses são os recursos que eu coloquei no site para quem quiser encontrar alguma coisa no blog:

    • Busca
      Lembre-se do "vou consultar do Google antes de fazer perguntas"
    • Posts em ordem cronológica
      Se você tem tempo e paciência, pode ler todos os posts do blog em ordem cronológica.
    • Lista com todos os posts
      ditto
    • Post por categoria
      Acho o melhor de todos, principalmente porque as séries que eu escrevi estão organizadas com seus posts agrupados, facilitando bem a leitura.

    Espero que ajude. Sugestões?

    Em 29/11/2006 16:17 - Comentários (1)

    Ano novo, a mesma escovação de bits de sempre

    Estou voltando das férias para mais um ano de escovação de bits. Gostaria de saber de vocês algumas sugestões sobre o que escrever esse ano. Apesar de poder usar a quantidade de comentários nos posts como um termômetro de aceitação dos assuntos, acho que seria interessante que vocês me dissessem o que gostariam de ver escrito no blog. Só não precisam sugerir que eu continue a série sobre STL, a parte 5 já está no forno...

    Lembrem-se: dentro dos assuntos que eu escrevo e gosto, é claro. Além de tudo que vocês já leram aqui, eu quero ver se começo a escrever também sobre Python, que é uma linguagem que eu tenho usado muito e estou gostando. Inclusive, minha primeira sugestão para mim mesmo é escrever sobre esse contraste entre programar em Python e programar em C++, e explicar em quais caso eu uso qual linguagem. :-)

    Em 12/01/2007 16:33 - Comentários (12)

    Tutorial de STL, parte 5: algoritmos

    Hoje vamos falar sobre os algoritmos da STL (algorithms), que nada mais são que as funções template que efetuam algum tipo de operação sobre os itens de um container. Isso mesmo, os tais algorithms nada mais são do que funções. O que os faz especiais é a programação genérica, que permite que um mesmo algoritmo possa ser aplicado a diversos tipos de containers, sem distinção. Dessa forma, ao invés de cada container prover métodos com o algoritmos necessários, todos eles ficam separados e pode ser aplicados a maioria deles.

    Para tornar os algoritmos genéricos, precisamos primeiro abstrair os containers. Existem duas formas básicas para resolver isso em C++: usando interfaces e funções virtuais, ou fazendo o chamado "polimorfismo em tempo de compilação", usando templates e concepts, onde ter os métodos das classes com os mesmos nomes já é suficiente. A primeira opção não se enquadra no conceito C++ de "generic programming", e apesar da segunda se enquadrar, não é possível usá-la com os containers STL. Por exemplo, apesar da maioria dos containers oferecer o método push_back, alguns não o fazem (como o std::map).

    Como dito na parte 4 dessa série sobre STL, o conceito de iterators existe para abstrair as particularidades do armazenamento de cada container. Dessa forma, a ação de acessar e percorrer os itens de um container se torna igual para a maioria dos containers. Apesar de não ser possível abstrair todas as características de um container, a abstração da forma de acesso aos itens já é suficiente para que os algortimos funcionem. Além disso, isso torna tudo mais flexível, já que permite que você aplique um algoritmos somente em determinados itens de um container.

    Como um trecho de código vale muito mais do que uma lei estúpida feita por alguém mal informado, vamos lá:

    #include <vector>
    #include <algorithm>
    #include <assert.h>
     
    //
    // esse é o nosso algoritmo que soma um número a
    // cada item de um container. Note que durante a execução
    // o tipo T será o tipo do iterator, e não do container
    //
    template<typename T >
    void soma_num(T begin, T end, int num)
    {
      for(T i = begin; i != end ; ++i)
        *i += num;
    }
     
     
    int main()
    {
      std::vector<int> v;
      std::vector<int> l;
      int arr[10];
    
      for(int a = 0 ; a < 10 ; ++a)
      {
        v.push_back(a);
        l.push_back(a);
        arr[a] = a;
      }
     
      //
      // vamos somar 10 aos número de todos os containers
      //
      soma_num(v.begin(), v.end(), 10);
      soma_num(l.begin(), l.end(), 10);
     
      //
      // somando 10 somente aos 5 primeiros itens do array
      //
      soma_num(arr, arr + 5, 10);
    }

    Para maioria dos algoritmos, o acesso a todos os itens do container já á mais do que suficiente. A STL contém diversos algoritmos em várias áreas, entre eles:

    • procurar itens dentro de um container (find, find_if)
    • contar itens que atendem a uma determinada condição (count, count_if)
    • cópia de itens entre containers e trechos de containers (copy, copy_n)
    • o famoso map reduce (transform, accumulate)
    • troca de itens (reverse, swap, rotate)
    • operações com std::set, conjuntos (set_union, set_intersection, set_difference)
    • e muitos outros (min, max, random_shuffle, fill generate)

    A lista e documentação dos algoritmos pode ser encontrada na página da SGI ou na MSDN. Segue um exemplo de uso dos algoritmos:

    int main()
    {
      std::vector<int> v1, v2;
     
      for(int a = 0 ; a < 100 ; ++a)
        v1.push_back(a);
     
      //
      // cria um iterator especial que insere um novo
      // items em um container toda vez que houver 
      // uma atribuição. 
      //
      std::back_insert_iterator<std::vector<int>> bi(v2);
     
      //
      // isso faria um v2.push_back(10);
      // *bi = 10;  
      //
     
      //
      // vamos copiar os itens de um container para outro.
      // o algoritmo copy tem duas linhas: é um loop for
      // que copia todos os itens em sequência. Veja os fontes
      // em stl_algo.h
      //
      std::copy(v1.begin(), v1.end(), bi);
     
      //
      // embaralha o v1
      //
      std::random_shuffle(v1.begin(), v1.end());
     
      //
      // ordena v1
      //
      std::sort(v1.begin(), v1.end());
     
      for(int x = 0 ; x < 100 ; ++x)
      {
        assert(v1[x] == v2[x]);
      }
    }

    Em 13/02/2007 16:26 - Comentários (0)

    Meu caso com o Python, parte 1

    Acredito que vocês têm percebido que, além de tudo que eu falo, faço e escrevo usando C++, eu tenho falado bastante sobre Python. Além de falar eu tenho estudado muito Python de um ano pra cá, e em alguns projetos tenho usado mais Python do que C++. Como isso foi uma mudança grande para mim, acho que essa experiência pode ser aproveitada por mais pessoas.

    Acredito que a maioria dos programadores usam scripts para automatizar tarefas repetitivas. Para algumas tarefas, talvez teria sido melhor fazer um programa completo e compilado, mas foi mais simples migrar um .BAT para um .VBS (ou usar shell script de uma vez...) e ficar remendando ele resolver o problema. Até que você encontra um grande problema: quando seu script começa a ficar maior e mais complexo, você acaba batendo toda hora nos limites das linguagens (foi isso que me fez pular do VB6 para o C++). Tendo esse problema algumas vezes, e lendo sobre todo o hype em cima das ditas linguagens dinâmicas, resolvi estudar se haviam realmente linguagens de script com mais poder de fogo.

    Depois de uma pequena procura cheguei a conclusão que haviam dois candidatos: Python e Ruby. Estudando a linguagem pura e simplesmente, achei o Ruby melhor. Comecei a estudar Ruby enquanto lia mais sobre Python. Mas uma coisa muito importante me fez decidir pelo Python: suporte, literatura e bibliotecas. Apesar do Ruby estar crescendo devido ao Ruby On Rails (e ao ótimo marketing do seu criador), o Python já era usado pelo Google, NASA, e por muita gente no Source Forge. Então, por razões mais mercadológicas que puristas, escolhi o Python.

    A melhor forma de se estudar uma linguagem é determinar um projeto e trabalhar nele. Essa história de ficar lendo tutorial e fazendo exercícios simples não é lá de muita valia. Foi então que eu decidi migram um script VBScript que eu usava para compilar meus projetos Visual C++ para Python. Esse script encontrava o número de revisão do Subversion, mudava o arquivo de resource para colocar a revisão no FILEVERSION e compilava o projeto. Resultado: 120 linhas de código VBS se transformaram em 20 linhas de código Python. E o código ficou mais claro e mais eficiente. Percebi que fiz uma boa escolha, e isso me animou a migrar outros scripts para Python e a passar o dia com o console Python aberto para automatizar tarefas simples.

    continua...

    Em 20/02/2007 12:30 - Comentários (13)

    Artigos sobre Unmanaged Vista

    Bons artigos sobre o Windows Vista, em raros casos onde o termo .NET não aparece:

    Em 23/02/2007 14:47 - Comentários (0)

    Link para o download do Visual C++ Toolkit 2003

    A Microsoft (tá bom, a área de vendas da Microsoft) tem a PÉSSIMA E ESTÚPIDA mania de achar que quando é lançada uma versão de um software, as versões antigas não são mais necessárias, são péssimas e cheias de defeitos. O próprio Bill Gates falou mal do Office 2000 para tentar vender o 2003, por exemplo.

    Tratando-se de compiladores C++, é necessário alguns cuidados para carregar em um EXE uma DLL compilada com uma versão diferente. Muitos projetos open source exigem uma versão específica do compilador. Por mais que o Visual C++ Express 2005 seja melhor do que o Visual C++ Toolkit 2003, algumas pessoas precisam dessa versão específica. Que, é claro, é impossível de achar no site da Microsoft.

    Ainda bem que alguma boa alma disponibiliza isso. Download do Visual C++ Toolkit 2003, eu testei, funciona.

    Em 24/02/2007 01:00 - Comentários (7)

    Meu caso com o Python, parte 2

    continuação da parte 1

    Com essa minha experiência com Python cheguei à uma conclusão interessante. Sou um apaixonado por programação e suas linguagens, não necessariamente apaixonado por C++. A parte do meu cérebro que responde pelo C++ se sente um pouco traída, já que a exclusividade acaba. Mas a parte que cuida do bom senso fica cada dia mais feliz. Não sendo eu somente um fanático programador C++, passo cada dia mais a confiar na minha (possível) imparcialidade e bom senso quando o assunto é programação. E minha parte C++ fica mais feliz ainda por isso ser uma evidência de que escolhi o C++ não porque é algo complicado somente para nerds e escovadores de bit, e sim porque tem suas inúmeras qualidades e é, indiscutívelmente, a melhor ferramenta disponível para determinados tipos de projetos. Eu sempre escrevi sobre usar a melhor ferramenta disponível para sua tarefa. Mas talvez minha relutância em usar algo que não C++ falava contra mim, apesar de fazer todo o sentido. Tudo resolvido agora. :-)

    Ouvi uma história uma vez que resume tudo:

    Imagine um carro de Formula 1. O volante é muito sensível. Quando você pisa no acelerador, ele arranca e você cola no banco. Se você não freia com cuidado, é quase arremessado para fora do carro. Ele é desconfortável, apertado, faz bastante barulho. Só que ele é o mais rápido que existe. Coisa para profissionais. Assim é o C++.

    Coisa para profissionais. Quando você treina bastante, é o melhor carro do mundo. Mas usar um Formula 1 para fazer compras, no trânsito de São Paulo, não é viável. É muito legal, é muito divertido, mas pouco viável.

    Lembre-se: não é lei do mínimo esforço. É a lei de o esforço necessário, de não desperdiçar o esforço. É sobre isso a frase "usar a ferramenta certa para o problema certo". Note que não estou dizendo que só percebi isso graças ao Python, mas digamos que o Python me parece o melhor carro de passeio que existe para fazer compras no supermercado... :-)

    Algumas coisas que eu gostei muito no Python em relação ao C++ e aos wannabes (C#/Java):

    • O ciclo while(1){arruma-compila-debuga} quase desaparece. Você vai escrevendo código no console e testando, em runtime ou não. Funcionou? Copia do console e cola no fontes, um trecho de código já testado.
    • A linguagem é muito legível e simples. Em alguns casos, é uma especificação que roda e funciona. Por isso que existe bastante gente escrevendo protótipos em Python e depois refazendo tudo em C++, depois que o conceito está testado. Eu sou um deles :-)
    • Batteries Included. Quase tudo que você precisa vem na distribuição padrão (como o .NET Framework e as bibliotecas Java, só que com um pouco a mais). Precisa fazer profiling? A distribuição padrão tem 3. Unit Test? Sim, na distribuição padrão. Web Server em Python? Sim também. Regex? Threads? SMTP? XML-RPC? Sockets? Criptografia? XML? Biblioteca de Log? HTTP com tratamento de Cookies? Sim.

    E, claro, encontrei também defeitos:

    • Um código feito em Python é bem legível, o que faz com que muito programadores Python relevem a importância dos comentários no código. Minha opinião sobre isso não muda, independente da linguagem.
    • Não existe livro avançado de Python. Estou acostumado com C++, onde existe muitos livros avançados. Os de Python ou são tutoriais ou cookbooks.
    • Sim, é lento em runtime (geralmente mais do que C# ou Java, mas nem sempre). Mas extremamente produtivo (muito mais do que C# ou Java). Ou seja, é bem usado quando não se precisa de performance ou quando o tempo de I/O ou rede é tão grande em relação ao resto do processo que o tempo da linguagem se torna quase desprezível.

    Minha opinião pode não valer muito em termos de convencer as pessoas sobre o que o Python pode oferecer. Mas acredito que a opinião do Google, Nasa, ILM, YouTube e outros deve valer um pouco mais. :-)

    Pronto, confessei, eu tenho outra (linguagem). Nem foi tão difícil quanto eu pensava...

    Em 07/03/2007 17:48 - Comentários (16)

    Como ser um programador, outra opinião
    Advice on How to Become a Programmer. Não é muito diferente do que eu já escrevi, mas ele leva mais para o enfoque prático. Vale a leitura.

    Em 22/03/2007 18:54 - Comentários (0)

    Tutorial de STL, parte 6: Functors

    Functors (ou function objects) são objetos que podem ser chamados como funções. No C++, você pode sobrecarregar o operador de chamada de função - operator() - e permitir que um objeto se comporte de forma bem parecida com uma função. A grande vantagem de um objeto em relação a uma função é que o primeiro pode guardar estados diferentes usando instâncias diferentes. Um função pode guardar estado, mas esse estado é único para todos que chamam essa função. Tudo fica mais simples com um exemplo de código:

    class FunctorString
    {
      std::string m_str;
    public:
      FunctorString(const std::string& str) : m_str(str)
      {}
      bool operator ()(const std::string& str)
      {
        return m_str == str;
      }
    };
    
     
    int main()
    {
      FunctorString comparaComLaranja("laranja"), comparaComBanana("banana");
     
      //
      // todos chamados como função, comparando com
      // coisas diferentes
      //
    
      std::cout << comparaComLaranja("maçã") << std::endl; // não
      std::cout << comparaComLaranja("banana") << std::endl; // também não
      std::cout << comparaComLaranja("laranja") << std::endl; // agora sim
    
    
      std::cout << comparaComBanana("maçã") << std::endl; // não
       std::cout << comparaComBanana("pera") << std::endl; // também não
       std::cout << comparaComBanana("banana") << std::endl; // agora sim
    
    }

    Note que o FunctorString pode ser chamado exatamente como uma função, mas criando diferentes instâncias você pode criar comportamentos diferentes. Isso é muito interessante para quando uma função que deve ser rodada sobre uma seqüência STL não é trivial. Exemplo: o algoritmo for_each chama uma função (ou functor) para cada item da sequência. Caso queiramos elevar todos os itens a uma determinada potência x, não podemos usar uma função, pois para cada potência diferente seria necessário criar outra função (esqueça a gambiarra da variável global). Usando um Functor isso fica fácil, é só criar diversas instâncias com a potência diferente.

    Functors funcionam bem também para containers com objetos, onde geralmente as comparações ou ações sobre eles não é trivial, quase sempre exigem manipulação de acesso à itens. Além disso, é intessante notar que os algoritmos da STL não aceitam ponteiros de função, mas sim parâmetros cujos tipos são dados de acordo com um template. Ou seja, qualquer coisa que puder ser chamada como uma função - seja um função isolada, função estática de uma classe ou um functor - serve. É o que chamamos de duck typing: se anda como um pato e faz quack como um pato, deve ser um pato.

    Segue um código grande, que usa Functors e tem também um exemplo de uso de templates para substituir os Functors por templates no caso do estado do nosso suposto functor ser um tipo:

     
    #include "stdafx.h"
    #include <assert.h>
    #include <vector>
    #include <string>
    
    #include <iostream>
     
    //
    // Código longo mas interessante para demonstrar 
    // programação genérica. Pretendo usar essa hierarquia
    // para futuras explanações
    //
     
    struct ProgrammingLanguage
    {
      std::string name;
      std::string creator;
      // preciso disso para o dynamic_cast...
      virtual ~ProgrammingLanguage(){} 
    };
    
     
    struct CompiledProgrammingLanguage : public ProgrammingLanguage
    {
      struct Compiler
      {
        std::string name;
        bool isOpenSource;
      };
     
      std::vector<Compiler> availableCompilers;
    };
    
     
    struct NativeCompiledProgrammingLanguage  : public CompiledProgrammingLanguage
    {
      bool hasOptimizer;
    };
     
    enum JitModel
    {
      Everything,
      HotFunctions
    };
     
    struct ByteCodeCompiledProgrammingLanguage : public CompiledProgrammingLanguage
    {
      bool hasJit;
      JitModel jitModel;
    };
    
     
    struct InterpretedLanguage : public ProgrammingLanguage
    {
      bool hasConsole;
    };
     
     
    void LoadLanguages(std::vector<ProgrammingLanguage*>* pLanguages)
    {
      //
    
      // Alerta de didática:
      // EU SOU CONTRA alocar memória em ponteiro raw, sem
      // gerenciamento. A primeira versão desse código usava
      // boost:shared_ptr, mas tornava os functors mais 
      // complicados por causa de adaptors e coisas assim.
      //
     
      //
      // Python
    
      //
      InterpretedLanguage* python = new InterpretedLanguage();
      python->name = "Python" ;
      python->creator = "Guido van Rossum";
      python->hasConsole = true;
      pLanguages->push_back(python);
     
      //
    
      // VBScript
      //
      InterpretedLanguage* vbScript = new InterpretedLanguage();
      vbScript->name = "VBScript" ;
      vbScript->creator = "??";
      vbScript->hasConsole = false;
      pLanguages->push_back(vbScript);
     
    
      //
      // JavaScript
      //
      InterpretedLanguage* javaScript = new InterpretedLanguage();
      javaScript->name = "JavaScript";
      javaScript->creator = "??";
      javaScript->hasConsole = false;
      pLanguages->push_back(javaScript);
    
     
      //
      // Java
      //
      ByteCodeCompiledProgrammingLanguage* java = new ByteCodeCompiledProgrammingLanguage();
      java->name = "Java" ;
      java->creator = "James Gosling";
      java->hasJit = true;
      java->jitModel = HotFunctions; // Me corrijam se estou errado
    
     
      CompiledProgrammingLanguage::Compiler javaCompiler;
      javaCompiler.isOpenSource = true; // Já foi liberado, né?
      javaCompiler.name = "javac";
      java->availableCompilers.push_back(javaCompiler);
     
      pLanguages->push_back(java);
     
      //
      // C#
    
      //
      ByteCodeCompiledProgrammingLanguage* cSharp = new ByteCodeCompiledProgrammingLanguage();
      cSharp->name = "C#"; 
      cSharp->creator = "Anders Hejlsberg";
      cSharp->hasJit = true;
      cSharp->jitModel = Everything;
     
      CompiledProgrammingLanguage::Compiler microsoftCsCompiler;
      microsoftCsCompiler.isOpenSource = false;
      microsoftCsCompiler.name = "csc";
      cSharp->availableCompilers.push_back(microsoftCsCompiler);
    
     
      pLanguages->push_back(cSharp);
     
      //
      // C++
      //
      NativeCompiledProgrammingLanguage* cPlusPlus = new NativeCompiledProgrammingLanguage();
      cPlusPlus->name = "C++"; 
      cPlusPlus->creator = "Bjarne Stroustrup";
    
     
      CompiledProgrammingLanguage::Compiler gcc;
      gcc.isOpenSource = true;
      gcc.name = "GNU C++";
      cPlusPlus->availableCompilers.push_back(gcc);
     
      CompiledProgrammingLanguage::Compiler vc;
      vc.isOpenSource = false;
      vc.name = "Visual C++";
      cPlusPlus->availableCompilers.push_back(vc);
     
      pLanguages->push_back(cPlusPlus);
    
     
      //
      // Se sua linguagem preferida não está aqui
      // não fique triste, nada pessoal..
      //
    }
     
     
    //
    // Isso não precisa ser um functor, porque
    // como vamos filtrar de acordo com o tipo,
    // um template já guardar estado do tipo com
    // o qual ele foi instânciado
    
    //
    template<typename T>
    bool IsLanguageType(ProgrammingLanguage* p)
    {
      //
      // isso não tem efeito em runtime, mas garante
      // que o tipo que você está usando herda de
      // ProgrammingLanguage
      //
    
      static_cast<ProgrammingLanguage*>((T*)NULL); 
     
      return dynamic_cast<const T*>(p) != NULL;
    }
     
    template<typename TDump, typename TIterator>
    
    void DumpLanguagesByType(TIterator first, TIterator last, std::string message)
    {
      std::cout << std::endl
        << message << std::endl 
        << std::string(message.size(), '=') << std::endl;
     
      for( ; ; )
      {
        //
    
        // acha o próximo item com esse 
        // determinado tipo
        //
        first = std::find_if(
          first, 
          last, 
          &IsLanguageType<TDump>);
     
        if(first == last)
          break; // cabô
    
     
        // só pra garantir...
        assert(dynamic_cast<TDump*>((*first)));
     
        // dump
        std::cout << (*first)->name << std::endl;
     
    
        ++first;
      }
     
      return;
    }
     
    //
    // Isso é um functor, um objeto que pode ser
    // chamado como uma função. A vantagem é que um 
    // objeto guarda estado, o que nos permite salvar
    // o nome que deve ser procurado nas chamadas 
    // seguintes
    //
    class FindLanguageByName
    {
      std::string m_name;
    public:
      FindLanguageByName(const std::string& name) : m_name(name)
      {}
    
     
      bool operator()(ProgrammingLanguage* p)
      {
        return p->name == m_name;
      }
    };
     
    void FreeLanguage(ProgrammingLanguage* p)
    {
      //
      // caso tenhamos mais membros a serem liberados,
      // centralizamos o código de limpeza aqui
    
      //
      delete p;
    }
     
    int main()
    {
      typedef std::vector<ProgrammingLanguage*> LanguagesVector;
      LanguagesVector languages;
     
      LoadLanguages(&languages);
    
     
      //
      // encontra as linguagens interpretadas. Nessa caso não usamos
      // functors porque nossa função é um template. Note que como
      // vamos diferenciar pelo tipo, usaremos um tipo como parâmetro.
      // tipo como parâmetro = template
      //
      std::cout << "Linguagens interpretadas" << std::endl 
    << "========================" << std::endl;
    
     
      for(LanguagesVector::iterator iLanguage = languages.begin() ; ; )
      {
        iLanguage = std::find_if(
          iLanguage, 
          languages.end(), 
          &IsLanguageType<InterpretedLanguage>);
     
        if(iLanguage == languages.end())
          break; // cabô
     
        std::cout << (*iLanguage)->name << std::endl;
    
     
        ++iLanguage;
      }
     
      //
      // encapusulei o código acima em uma função template.
      // Agora vou fazer isso com o resto dos tipos.
      //
      DumpLanguagesByType<NativeCompiledProgrammingLanguage>(
        languages.begin(), 
        languages.end(),
        "Linguagens compiladas para o native code do processador");
    
     
      DumpLanguagesByType<ByteCodeCompiledProgrammingLanguage>(
        languages.begin(), 
        languages.end(),
        "Linguagens compiladas para byte code");
     
      //
      // agora vou procurar a linguagem por nome usando um functor.
      // Note que a vantagem é que classe FindLanguageByName guarda
      // a string "Python" para ser usada toda ver que o find_if
      // chamá-la para fazer a comparação
    
      //
      LanguagesVector::iterator iLanguage = 
        std::find_if(languages.begin(), languages.end(), FindLanguageByName("Python"));
     
      std::cout << std::endl << 
        "Python" << (iLanguage != languages.end() ? 
    "" : " não") << " encontrado." << std::endl;
    
     
      //
      // desalocando tudo
      //
      for_each(languages.begin(), languages.end(), &FreeLanguage);
     
      languages.clear();
     
      return 0;
    }

    Em 27/03/2007 17:03 - Comentários (2)

    Administrando: Substituindo um arquivo que está sendo usado

    Um problema muito comum quando precisamos atualizar um arquivo em um servidor (geralmente DLLs) é o seguinte:

    Temos duas soluções óbvias nesse caso:

    • Dar um boot no servidor e trocar o arquivo na volta do boot. Além de não funcionar algumas vezes (como na existência de um serviço que sobe no boot e usa o arquivo), em outras vezes não é possível (dar boot em um servidor de produção 3 da tarde?).
    • Renomear o arquivo atual e copiar o novo. Apesar de funcionar, não faz com que os programas que usam o arquivo o recarreguem.

    O que causa esse problema é que muitas vezes não sabermos quem está usando o arquivo. Caso nosso arquivo seja uma DLL de um componente COM in process (DLL que é carregada no processo usuário) é praticamente impossível saber quem o carrega. Ou melhor, é impossível descobrir isso sem a ferramenta certa.

    Caso seja um componente COM+ configurado como Server Application (ou seja, sua DLL é carregada em um dllhost.exe), é só mandar um shutdown no pacote e trocar a DLL. Mas apesar dessa facilidade, nem todos podem usá-la, já que isso aumenta o overhead do marshalling entre o processo usuário e o dllhost.exe. Além, é claro, de todas aquelas configurações de segurança.

    E a solução do nosso problema, como sempre, passa por www.sysinternals.com. Baixe o Process Explorer, que é um substituto vitaminado para o Task Manager (Gerenciador de Tarefas) que existe no Windows. Dentre todas as informações que ele mostra sobre os processos, está também a lista de handles abertos (os de arquivos inclusive) e DLLs carregadas:

    Apesar de bastante interessante, esse recurso não ajuda muito já que você precisa olhar em todos os processos. Até que fala mais alto seu instinto de usuário Microsoft e você tecla CTRL+F. Tudo resolvido:

    Resolvido. Agora você sabe quais são os processos que estão usando o arquivo que você precisa trocar. Você pode até dar um kill no processo sem sair do Process Explorer :-)

    PS: Quem acha que seria interessante se eu escrevesse sobre COM e COM+ levante a mão.

    Em 11/04/2007 18:44 - Comentários (15)

    Se você tem domínio registrado, cuidado com a fraude do NICREGISTRO

    Existe uma empresa paulista que está enviando boletos de cobrança para todos que tem domínios registrados no Brasil. NÃO PAGUE, ele não são o Registro.br. Eles na realidade oferecem um serviço de hospedagem e registro de um domínio .com, mas colocam isso em letras pequenas e fazem tudo parecer o pagamento do domínio brasileiro. Além disso, eles mandam o boleto com "A/C Departamento Financeiro" para reduzir a possibilidade de um usuário técnico detectar a fraude antes do pagamento.

    Não se esqueça, isso é fraude, mesmo que você precise do registro do domínio .com e hospedagem, não dê dinheiro para fraudadores estelionatários. O próprio Registro.br já soltou uma nota com esse alerta, e você pode ter mais informações no Google.

    Em 20/04/2007 20:27 - Comentários (2)

    Como contratar um bom programador

    Uma reclamação constante entre programadores é a (falta de) qualidade dos processos de seleção das empresas. Outra reclamação constante é das empresas, sobre a dificuldade de contratar bons programadores. Tentei separar aqui alguns pontos que eu acredito que podem melhorar um processo de seleção de programadores, tanto para empresa quanto para o candidato:

    Pague um salário justo. É lógico que reduzir os custos é uma das metas de toda empresa, mas essa é mais uma tentativa de economizar que custa caro no médio prazo. A área de programação tem empregos à vontade para quem tem o mínimo de qualificação - sim, o mínimo é suficiente. Se você paga uma salário baixo, 10% de aumento é um aumento ridículo em termos reais, o que faz com que uma empresa concorrente possa tirar um funcionário seu em uma fase crítica de um projeto pagando R$ 250,00 a mais. Normalmente as empresas pagam quanto o funcionário quer receber quando ele pede menos que o valor de mercado, e vêem nisso uma grande oportunidade - afinal, foi ele que pediu para ganhar esse salário. Com a internet, sua empresa não conseguirá esconder por muito tempo que o salário dele é baixo.

    Não minta sobre as condições de trabalho. Ele pode descobrir isso rápido, enquanto ele ainda tem outras entrevistas em aberto. Você corre o risco de perder seu novo funcionário em algumas semanas, e ter que começar o processo de seleção novamente, pedindo pelamordedeus para que os candidatos que não foram escolhidos antes reconsiderem e façam uma nova entrevista ou negociação.

    Mostre ao candidato que existe um canal aberto para negociações. Na maioria das vezes a forma mais fácil de ganhar um aumento é arrumar outro emprego e pedir mais. Se não der certo, pelo menos você não brigou com ninguém no seu emprego atual e tudo continua como estava. O risco é menor. Mostre que se ele pedir o dobro do salário e você falar não, isso não vai torná-lo um renegado, só será uma resposta negativa.

    Evite testes teóricos longos. Isso é extremamente irritante e demorado, além de você arriscar perder o candidato. A última vez que resolvi mudar de emprego, eu estava fazendo entrevistas em duas empresas, uma na área de segurança da informação e outra na área financeira. Eu tinha preferência pela empresa de segurança, pois era a área que eu mais gostava e tinha mais experiência. Só que o processo seletivo da empresa de segurança era tão horrível (RH, dinâmica de grupo, lentidão, etc), que eu fechei com a empresa da área financeira. A entrevista na empresa financeira foi rápida, focada na parte técnica e com a pessoa que seria meu gerente. Dei sorte, estou nessa empresa até hoje, e o processo de seleção tem vários dos pontos que eu sugiro aqui. Já a empresa de segurança perdeu um funcionário especializado numa área onde não é fácil encontrar profissionais.

    Faça uma entrevista mais técnica. Reduza o contato dele com a equipe de RH ao mínimo necessário. Pode parecer pedante da minha parte (afinal, sou um programador), mas se o candidato quisesse lidar com burocracia ele não seria um programador, teria outra profissão. Faça primeiro a entrevista técnica, deixe as burocracias para depois. Você só vai conseguir que o candidato fique animado com a sua empresa depois que ele puder saber exatamente o que ele vai fazer, e o pessoal do RH não vai conseguir explicar. Imagine o diálogo:

    - É framework 1.1 ou 2.0?
    - Acho que é 2.0. É o mais novo, né?
    - Sim é o mais novo. Você acessam o Oracle usando OLEDB ou usando o provider nativo?
    - É... ã... Não sei.


    O candidato vai se sentir muito mais a vontade conversando com outro programador.

    Faça o candidato escrever código na entrevista. Nem que você tenha que contratar um programador free lancer só para fazer as entrevistas. Ele não vai codificar no trabalho? Como você sabe como ele se sai fazendo isso antes de vê-lo programando? Peça para ele fazer um programa pequeno, nada que tome mais do que 1:30hs.

    Coloque um requisito não negociável no teste de codificação. Crie um requisito que não seja fazer o mais óbvio nem a melhor opção, só para descobrir o quanto o candidato é xiita e o quanto ele respeita o que está sendo solicitado. Exemplos:

    • Obrigue-o a usar o Visual C++ 6, mesmo que ele prefira (e possa) usar o 8
    • Obrigue-o a não usar generics em C# para manter compatibilidade com o .NET Framework 1.1
    • Proiba-o de criar procedures e obrigue-o a colocar as consultas diretamente na aplicação

    Você descobrirá logo a quantidade de chilique que você terá que aguentar quando pedir para ele fazer algo que ele não acredita ser o melhor, mas que é requisito do sistema por algum motivo de força maior. Um programador experiente explicaria que não é a melhor forma mas faria o que você pediu.

    Faça a primeira entrevista por telefone. Uma primeira entrevista técnica por telefone ajuda muito para filtrar os candidatos não qualificados, com um custo mais baixo. Faça algumas perguntas técnicas básicas e peça para que ele resolva algum problema simples, que não envolva escrever código diretamente. Exemplos:

    • Como você organizaria uma rotina de backup e quais softwares usaria?
    • Que containers da STL você usaria para controlar os usuários conectados em um servidor?
    • Como você faria comunicação entre duas threads?
    • O que é necessário para um objeto COM+ suportar pooling?
    • Que módulos você precisaria para fazer um sistema de pedidos em C# usando MSMQ?
    • Quais tabelas seriam necessárias para controlar o saldo e as operações de uma conta bancária?
    • Se você trabalhasse em uma fábrica de canetas, como testaria uma caneta no controle de qualidade?

    Não acredite cegamente em certificações. Faça o candidato escrever código e pergunte dos projetos que ele já participou e qual o papel dele nesses projetos. Pergunte como ele resolveu os problemas que apareceram durante os projetos. Uma certificação ajuda, mas não resolve, conheço um monte de programadores que têm certificação e são bons programadores, mas também conheço vários que têm certificação mas não conseguem fazer um sistema. Não limite uma vaga somente a profissionais com certificação, a não ser que a Microsoft/Sun/IBM/etc pare de te dar dinheiro por causa disso. Existem MUITOS profissionais bons que não tiraram certificação. E antes que perguntem, eu tenho 3 certificações (C++, C# e VB.NET) e elas são ótimas para encher lingüiça no meu curriculum, da parte técnica não me acrescentaram nada (lembrem-se: minha opinião, meu caso específico, muita gente aprende muita coisa estudando para tirar certificação).

    Nada de grandes testes psicológicos ou dinâmica de grupo. Você não está contratando vendedores. Independente da discussão sobre a valia desses testes, você pode espantar bons candidatos. Um programador precisa saber se comunicar como qualquer profissional, mas ele muitas vezes nem terá contato com os clientes. Muitos bons programadores não são bons oradores ou negociadores, não é um requisito da profissão. Se você faz testes psicológicos para reduzir as possibilidades de contratar alguém que desestabilize a equipe e atrapalhe, mude sua estratégia. Contrate todos os bons técnicos que sejam sociáveis somente o suficiente para trabalhar. E comece a demitir mais. Isso mesmo, contrate o candidato, se ele atrapalhar ou for incompetente, demita-o. É mais fácil e sua equipe ficará muito feliz em saber que caso alguém entre para desestabilizar e atrapalhar, a empresa resolverá esse problema rapidamente. Acredito que muita gente já tenha trabalhado em alguma empresa onde alguém atrapalha o andamento dos projetos e ninguém toma uma atitude, ninguém demite o infeliz. O gerente dorme com a consciência mais tranquila (como se omissão fosse um motivo para isso) enquanto todo o resto da equipe é obrigada a suportar o stress de trabalhar com alguém desse tipo.

    Proporcione o básico: paz pra programar, um micro bom e um salário justo. É só isso que um programador quer. Muitos bons programadores não estão preocupados com planos de carreira ou possibilidade de cargos de gerência (um dos argumentos preferido das "meninas do RH").

    Não prometa nada que não possa cumprir, não use velhos truques. Talvez você consiga contratar um bom programador com promessas falsas, mas não vai conseguir mantê-lo por muito tempo.

    Em 04/05/2007 21:05 - Comentários (34)

    Curso de programação de drivers em São Paulo

    Por causa do menu notório atraso em divulgar algumas coisas de sumária importância para os programadores C++ de plantão, vou resolver tudo nesse post.

    Primeiramente, meu prezado (amigo && ex companheiro de trabalho && sócio) está escrevendo um blog sobre desenvolvimento de drivers para Windows - www.driverentry.com.br - que tem muito mais conteúdo do que eu cheguei a escrever um dia. Muito conteúdo e de ótima qualidade, além de ser o único brasileiro a escrever sobre drivers.

    E "segundamente", esse mesmo (amigo && ex companheiro de trabalho && sócio) estará ministrando um curso sobre desenvolvimento de drivers para Windows. O curso será em São Paulo, pela Univerdade Gama Filho, e iniciará em 26 de maio. O conteúdo do curso engloba desde os módulos que foram o kernel do Windows até programação de drivers para hardware (coisa que eu nunca fiz...).

    Em 11/05/2007 16:14 - Comentários (0)

    Palestra: Mercado de trabalho na área de desenvolvimento

    No dia 19 de maio, vou ministrar a palestra "Mercado de trabalho na área de desenvolvimento" no evento "Codificando 2007", que será realizado na UNIP do Tatuapé.

    **Apesar do Alfred aparecer como palestrante junto comigo (originalmente ele era o único), ele vai dar o cano por motivos de força maior e só o escovador de bits que vos fala estará lá na frente.

    Em 18/05/2007 14:37 - Comentários (6)

    Material adicional da palestra do evento Codificando 2007

    Como prometido na palestra de sábado, seguem os links de algumas coisas que já escrevi sobre o assunto:

    Em 21/05/2007 20:21 - Comentários (0)

    Lançada a versão 0.40 do projeto OmniObjects

    O OmniObjects, meu projeto open source de estimação, ganhou mais uma versão. Modificações:

    • Melhoria na tratamento de erro no OmniIdl e mais detalhes da fonte do erro quando ele ocorre na fase de geração de código para proxy/stub.
    • Suporte ao atributo IDL [size_is], que permite a passagem de buffers e arrays C entre as estações. Isso permite o funcionamento de funções tipo GraveAlgoNoMeuBuffer(szBuffer, iTamanhoDoBufferQueEuAloquei).
    • Suporte ao atributo IDL [string], para que strings C possam ser passadas como parâmetros (além da BSTR que já é suportada desde a primeira versão).

    Como de costume, mais detalhes na página do projeto no SourceForge.

    Em 21/05/2007 21:07 - Comentários (5)

    Tutorial informal de Python, parte 1

    Esse tutorial informal é voltado para programadores que querem conhecer melhor o Python. Meu intuito é ajudar esses programadores a decidirem se será de alguma valia estudar Python e se existe algum projeto onde a linguagem será útil.

    expliquei porque tenho usado Python. E não se esqueçam, não foi por causa da performance (já expliquei isso). O próprio C# é mais rápido que Python na maioria das vezes, mas meu foco nesse ponto é produtividade. O Python tem vários defeitos inerentes à sua arquitetura (fonte sempre aberto, bugs que aparecem em runtime por não existir tipagem, entre outros) mas é o preço que foi pago pelas qualidades.

    Caso você queria começar a programar em Python, você só precisará da distribuição padrão baixada do python.org. Se você usar o Windows, baixar o PyWin32 também ajuda, já que com ele vem uma IDE com code completion, debug integrado e mais algumas facilidades. Se você precisa saber mais detalhes sobre o funcionamento do Python, o Google e a Wikipedia são seus amigos. Não pretendo reproduzir nessa série esses detalhes, quero me ater ao código e ao uso prático do Python o máximo possível.

    Uma grande vantagem do Python é o console, onde você escreve e testa código sem o ciclo mudar-compilar-debugar. Em alguns exemplos de código, quando possível, vou usar somente o console. Isso será claro pelo aparecimento do prompt do Python (">>>") no início da linha, como nesse exemplo:

    >>> a = 10
    >>> print a
    10
    >>> print b
    Traceback (most recent call last):
      File "", line 1, in 
    NameError: name 'b' is not defined
    >>> 
    

    Uma das características marcantes do Python é que os blocos são determinado pela identação, não existe nada como END IF ou }. Bom (mantém o código legível) ou ruim (espaço e tab não são visuais), é assim que é. Nesse exemplo não usarei o console do Python, o que pode ser notado pela falta do prompt:

    a = 1
    b = 5
     
    while b != 0:
       b -= 1
       for x in range(5):
          print x, '-',
          a+= 1
          if a == b:
             print 'sim, é igual'
          else:
             print 'não é igual, a = %d e b =%d' % (a,b)
     
    print 'fim, isso não está dentro do for ou do while. O valor de b é %d' % b

    Vamos aproveitar o trecho de código acima para explicar com as coisas básicas funcionam:

    • As variáveis não têm tipo definido (mas o valor que referenciam tem), mas precisam ser atribuídas antes de serem usadas.
    • Não existe operador de pós ou pré incremento, a++ não funciona.
    • Instruções de fluxo como if, for e while não necessitam de parênteses nas expressões sendo testadas, mas precisam de dois pontos (':') no final.
    • print é uma instrução (isso será mudado no Python 3000).
    • Formatação de strings à la printf é um recurso nativo, uma expressão str % (param0, param1, ...) faz o serviço. Isso não é um recurso do print, str = 'número %d' % 10 também funciona. Como nesse exemplo, os parênteses pode ser omitidos no caso de um parâmetro somente
    • Atribuição ('=') é diferente de comparação ('==').
    • Não existe for da mesma forma que as linguagens derivadas do C, o for é usado somente para enumeração. A função range retorna uma lista (mais sobre isso depois) com números seqüenciais para simular esse comportamento. for x in range(10) é mais simples do que for(x = 0 ; x < 10 ; ++x). Para contadores que não são simplesmente somados, o range suporta mais parâmetros e existem outros recursos igualmente simples para isso.

    Em 31/05/2007 21:14 - Comentários (3)

    Minha política para manutenção de programas

    Ouço muitos programadores dizendo coisas como "esse código está horrível, vou refazer" ou "não consigo entender nada do que aquele cara escreveu, acho mais fácil fazer de novo". Quanto mais experiência e mais tempo de mercado eu tenho, mais essas frases me soam infantis e pouco profissionais. Eu já disse essas coisas várias vezes e ainda digo as vezes, mas não muda minha visão sobre o assunto.

    Quando não se entende o código, na maioria das vezes o programador que tenta entender já faz o serviço com má vontade, e acaba encontrando alguma desculpa para justificar sua proposta: gastar tempo refazendo algo que está pronto e funciona, só porque ele diz que não consegue entender o que foi feito. Nem vou discutir a duvidosa capacidade do programador, mas uma coisa me vem: se você não entende o que está feito, como pode refazer? Você pode até conhecer quais os valores de entrada produzem quais saídas, mas sem entender o que está feito é muito difícil manter a compatibilidade e o comportamento do código anterior. É importante lembrar: o usuário não vê código fonte. Se você gastar um tempão refazendo uma parte do sistema e ela não ficar exatamente igual ou melhor do que o que estava funcionando anteriormente, como você justifica? Agora o fonte está muito melhor e mais legível, mas não funciona como o usuário espera. Não é para ele que o sistema foi feito? Uma coisa que faz muitos programadores suarem frio é, por exemplo, dar manutenção em um código cujo padrão de nomenclatura ele não concorda e não gosta. Concordo que é horrível, mas faz parte do trabalho e é necessário um pouco de profissionalismo nessa hora.

    Refazer código não é algo necessariamente ruim, apesar de ser ruim na maioria das vezes (o próprio Joel já falou sobre isso). Você precisa ter um motivo muito bom para mudar algo que está funcionando. O fonte está feio? Não se esqueça que seu estilo de codificação pode ser feio para outras pessoas. Refazer uma parte do programa é uma tarefa custosa em termos financeiros e em termos de estabilidade, já que você troca um coisa feia, horrível e porca mas que funciona por algo lindo, maravilhoso e moderno que nunca foi testado. Eu tenho até um metáfora para isso: Um sistema remendado é como um barco antigo cheio de durepox nos buracos. Ele pode ser um pouco lento e pesado, mas depois de tapar tantos furos, ele navega. Um sistema novo é como um barco novo e bonito. Pode ter seus buracos da mesma forma, mas como nunca foi usado, ele pode ainda rachar no meio a qualquer momento.

    Algumas vezes refazer é necessário, mas a parte refeita do sistema é como uma parte nova, precisa ser muito bem testada. Por isso que hoje em dia se fala tanto em refactoring e unit testing, que prega algo como "não jogue fora o que você tem, reorganize e melhore de forma progressiva e garantindo que as melhorias feitas não criarão problemas ou incompatibilidades".

    Eu sei que com esse post parece que estou saindo do lado técnico e mudando para o lado gerencial, mas não é isso. Como técnico me compete manter o sistema funcionando, e tudo isso que eu escrevi é sobre análise de risco e sobre a prioridade de manter tudo funcionando. Qualquer mudança no sistema exige uma boa análise de risco, e uma parte refeita mais ainda. Quando você cria um recurso novo no sistema, muitas vezes o usuário não sabe o que esperar, ele sabe que algumas adaptações serão necessárias. Quando você refaz alguma coisa, ele sabe exatamente o que esperar, ele usa isso no dia a dia. Mudando isso você cria aquela situação extremamente irritante de "isso estava funcionando, por que você foi mexer?".

    Se você, como todo programador normal, tem vontade de experimentar tecnologias em versão alfa, coisas novíssimas e moderníssimas e refazer coisas, comece um projeto open source. Vai ser melhor para você (liberdade total e talvez mais usuários do que o sistema que você faz na empresa) e para seu projeto (estabilidade).

    Em 27/06/2007 19:06 - Comentários (5)

    Tutorial informal de Python, parte 2
    >>> # Essa é a segunda parte do tutorial informal de Python.
    >>> # Só que dessa vez, o próprio tutorial é escrito em Python. 
    >>> # Para acompanhar, é só abrir o console do Python e digitar.
    >>>
    >>> # As variáveis em python não tem tipo definido, elas são
    >>> # somente referências para objetos
    >>> a = 10
    >>> type(a)
    <type 'int'>
    >>> b = 20
    >>> type(b)
    <type 'int'>
    >>> c = 2.0
    >>> type(c)
    <type 'float'>
    >>> d = 12345678901234567890123456789012345678901234567890123456789
    0123456789012345678901234567890
    >>> type(d)
    <type 'long'>
    >>> # como visto, o tipo é inferido na declaração. Outro recurso interessante é que o nosso número
    >>> # gigantesco foi abrigado sem problemas pela variável d.
    >>> # Operações entre tipos numéricos diferentes são permitidos:
    >>> d + a
    123456789012345678901234567890123456789012345678901234567890123456789
    012345678901234567900L
    >>> c + d
    1.2345678901234568e+089
    >>> # caso você já tenha esquecido das variáveis (afinal, a, b, c, d são nomes realmente intuitivos),
    >>> # vai aqui um lembrete:
    >>> a,b,c,d
    (10, 20, 2.0, 12345678901234567890123456789012345678901234567890123456789012345
    6789012345678901234567890L)
    >>> # mas tipos não relacionados não são misturados (para não perder a piada: isso não é VB6 :-))
    >>> s = 'python, a linguagem que não te atrapalha'
    >>> type(s)
    <type 'str'>
    >>> s + a
    Traceback (most recent call last):
      File "<interactive input>", line 1, in <module>
    TypeError: cannot concatenate 'str' and 'int' objects
    >>> s + d
    Traceback (most recent call last):
      File "<interactive input>", line 1, in <module>
    TypeError: cannot concatenate 'str' and 'long' objects
    >>> # Eu acho desnecessário falar sobre expressões matemáticas em Python, é bem parecido com as outras
    >>> # linguagens. E como esse projeto de tutorial é voltado para programadores, não vou ficar chateando
    >>> # ninguém.
    >>>
    >>> # Vamos trabalhar um pouco com manipulação de strings.
    >>> s
    'python, a linguagem que n\xe3o te atrapalha'
    >>> type(s)
    <type 'str'>
    >>> # Você deve ter notado que 'não' virou 'n\xe3o', porque o padrão é avaliar a expressão como UTF8.
    >>> # Você resolve esse problema usando o comando print:
    >>> print s
    python, a linguagem que não te atrapalha
    >>> # String (como quase tudo em Python) é um objeto. Vamos ver quais os métodos ele suporta:
    >>> dir(s)
    ['__add__', '__class__', '__contains__',
     '__delattr__', '__doc__', '__eq__', '__ge__', '__getattribute__', 
    '__getitem__', '__getnewargs__', '__getslice__', 
    '__gt__', '__hash__', '__init__', '__le__', '__len__', 
    '__lt__', '__mod__', '__mul__', 
    '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__',
    '__rmul__', '__setattr__', '__str__', 
    'capitalize', 'center', 'count', 'decode', 'encode', 'endswith', 
    'expandtabs', 'find', 'index', 
    'isalnum', 'isalpha', 'isdigit', 'islower', 'isspace', 'istitle', 'isupper', 
    'join', 'ljust', 'lower', 
    'lstrip', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 
    'rstrip', 'split', 'splitlines', 
    'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
    >>> # Eu também gostei BASTANTE desse recurso. Tem outro muito bom. Veja isso:
    >>> s.islower.__doc__
    'S.islower() -> bool\n\nReturn True if all cased characters in S are lowercase and there is\nat least one 
    cased character in S, False otherwise.'
    >>> # é bom não esquecer do print...
    >>> print s.islower.__doc__
    S.islower() -> bool
     
    Return True if all cased characters in S are lowercase and there is
    at least one cased character in S, False otherwise.
    >>> # todos os métodos, propriedades ou funções que começam e terminam com __ são 'mágicos'.
    >>> # Você pode ver alguns deles na lista de atributos do objeto s:
    >>> s.__class__
    <type 'str'>
    >>>
    >>> # Vamos à algumas funcionalidades que o tipo string fornece.
    >>> s1 = 'python'
    >>> s2 = "C++"
    >>> # Aspas simples e duplas são aceitas, não faz diferença
    >>> s3 = s1 + '\n' + s2
    >>> print s3
    python
    C++
    >>> # a barra invertida é escape igual ao C/C++
    >>> print '\tteste\n\n\t\tteste\n'
        teste
    
     
            teste
     
    >>> # se você quer usar a barra invertida literalmente, coloque o prefixo 'r'na string
    >>> print r'c:\python25\python.exe'
    c:\python25\python.exe
    >>> # Algumas funções úteis:
    >>> '1,2,3,4,5,6,7,8,9,0'.split(',')
    ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0']
    >>> # O retorno disso é uma lista, veremos mais sobre isso depois.
    >>> # Mas o recurso mais interessante com certeza é 'indexing and slicing'. Veja isso:
    >>> print s3
    python
    C++
    >>> s3[0] # indexação começando pelo zero, como em C/C++
    'p'
    >>> s3[-1] # usando números negativos como índice, começamos pelo final
    '+'
    >>> s3.find('\n') # onde está a quebra?
    6
    >>> s3[s3.find('\n')]
    '\n'
    >>> # Vamos agora ao fatiamento de strings:
    >>> s3[0:6]
    'python'
    >>> # Mas como começamos do início, o primeiro número é opcional:
    >>> s3[:6]
    'python'
    >>> # Lembra o índice negativo? Também ajuda aqui:
    >>> s3[-3:]
    'C++'
    >>> # Vamos separar as palavras 'python' e 'C++' pela quebras de linhas
    >>> pos = s3.find('\n')
    >>> print s3[:pos], '-',  s3[pos+1:]
    python - C++
    

    Em 16/07/2007 23:07 - Comentários (2)

    Um melhor programador nos meses que passaram e nos meses que virão

    Esse post é uma resposta a um post do Wanderley Caloni - ele chamou isso de tagging, mas quanto mais o tempo passa mais meu cérebro sente dificuldade em absorver novos buzzwords. A pergunta é "Como ser um melhor desenvolvedor nos próximos seis meses?".

    Resposta curta: Tudo que eu pretendo fazer para ser um melhor programador nos próximos seis meses é continuar fazendo o que eu tenho feito nos seis meses que se passsaram. Detalhes do que eu tenho feito na resposta longa, logo abaixo:

    A coisa que mais tem me ajudado a me tornar um melhor programador é me livrar dos preconceitos e preguiças e conhecer coisas novas. Eu nasci na plataforma Microsoft, meu primeiro contato com programação foi com o QBasic, e de lá eu segui para o Clipper (não-Microsoft, mas para mim sem uma "cultura" como existe em torno dos produtos MS), depois VB 3, 4, 5, 6, ASP, SQL, etc. Até que finalmente eu cheguei ao C++. Apesar do C++ ser a "menos Microsoft" das linguagens listadas, usá-lo envolve coisas bem Microsoft como Win32 API e COM. O C++ é multiplataforma, mas as APIs não o são. Você fica envolvido por essa cultura, e seu instinto básico é achar muito estranho (e até errado) a forma que as pessoas de outras culturas resolvem seus problemas.

    Você cria uma cultura de programação quando você é obrigado a usar um ecossistema de aplicações para fazer um projeto. Quando eu programei em Turbo C++ ou Clipper eu não me senti inserido em uma cultura, em uma comunidade de desenvolvedores. Eu programava sozinho na minha casa, e sem Internet era eu e meus livros. Tudo começou a mudar quando eu comecei a programar profissionalmente. Eu não usava mais uma linguagem, eu usava um ecossistema completo. Não era só o VB, uma aplicação era feitas em VB6, ASP, IIS e SQL Server. A integração esses produtos cria uma cultura, um costume difícil de se livrar - a estratégia básica da Microsoft personificada pela MSDN Magazine, onde um Hello World usa SQL Server, BizTalk, Exchange e a versão beta do IIS que só roda em um sistema operacional que ainda não foi lançado. A dificuldade em mudar de cultura é que a nova linguagem não se integra tão bem com o SQL Server que você tanto está acostumado. Não adianta mais só aprender outra linguagem. É preciso aprender uma nova linguagem, um novo banco de dados, um novo servidor Web. Eu fiz isso (esse site é feito em LAMP: Linux, Apache, MySql e PHP), mas é muita coisa para aprender. Quanto mais tempo você tem de programação, menos confortável você se sente com essas coisas. O raciocínio é simples: Pra que eu vou me matar para aprender 5 softwares novos se eu resolvo isso em plataforma Microsoft em 5 minutos? A mesma coisa vale para quem está acostumado a programar Mainframes ou Unix/Linux.

    E tudo se resolve quando você aceita sair da sua zona de conforto. Quando você começa a aprender um pouco mais e se anima, seu espírito de moleque fuçador volta a tona e te faz estudar cada vez mais. Foi graças a isso que hoje eu me considero um usuário/administrador/programador Linux. E é isso que me faz um melhor programador hoje: eu conheço várias visões de mundo sobre desenvolvimento de software. Além disso eu tenho estudado MUITO sobre mainframes. Acho que todo mundo sabe que eu trabalho na área financeira programando serviços críticos em C++. Nossas aplicações (rodam todas em Windows) param de vez em quanto, seja por bug, limite de portas de RPC, problema no COM+, problema no MSMQ, problema no Active Direcotry, fix da Microsoft que muda a implementação do critical section no kernel do Windows Server 2003 (santo Windbg...) ou coisas assim. Enquanto isso, as aplicações que rodam em UNIX, plataformas não Intel e no mainframe quase nunca param. Por que? Tenho minha teoria para isso, mas no contexto desse post não vem ao caso. O que importa é que eu estou estudando isso e muitas outras coisas. O que importa é que eu estou começando a entender a cultura IBM, a cultura UNIX, a cultura Linux e até a cultura dos administradores de rede. E eles tem as razões deles para fazer as coisas da forma que fazem.

    Eu tenho programado muito em Python e esse post foi escrito usando o emacs. Eu não consigo trabalhar sem o Cygwin e tenho lido TUDO sobre Unix e Linux que aparece na minha frente. Tenho estudado muito sobre mainframes e estudei LISP esses tempos para personalizar o emacs e entender de uma vez por todas como funciona uma linguagem funcional. Tudo fora do mundinho confortável que eu estou acostumado.

    E resumindo a resposta longa (eu sei, a resposta curta deveria fazer isso): Eu pretendo continuar descobrindo as diferentes visões de mundo sobre software. Se fechar em só uma plataforma é confortável mas limitante. Eu só continuo seguindo a filosofia de usar a melhor ferramenta para cada tipo de problema, e para isso eu preciso conhecer o maior número de ferramentas. Aquela metáfora que diz que "quando a sua única ferramenta é um martelo todos os problemas se parecem com um prego" é perfeita. Vocês não imaginam a quantidade de casos de péssimo uso para um SQL Server que eu tenho para contar...

    Em 18/07/2007 21:37 - Comentários (11)

    Tutorial informal de Python, parte 3
    >>> # Nessa terceira parte do nosso tutorial vamos ver como fazer funções
    >>> # e conhecer alguns tipos de dados que fazem do Python o que ele é
    >>> 
    >>> # As funções são definidas usando-se a palavra chave def:
    >>> def potencia(n, exp):
    ...   ret = 1
    ...   for x in range(exp):
    ...     ret *= n
    ...   return ret
    ... 
    >>> potencia
    <function potencia at 0x012DCB70>
    >>> potencia(2, 2)
    4
    >>> potencia(2,10)
    1024
    >>> potencia(2,50)
    1125899906842624L
    >>> potencia(2,100)
    1267650600228229401496703205376L
    >>> potencia(2,0)
    1
    >>> f = potencia
    >>> f
    <function potencia at 0x012DCB70>
    >>> f()
    Traceback (most recent call last):
      File "<interactive input>", line 1, in <module>
    TypeError: potencia() takes exactly 2 arguments (0 given)
    >>> f(3,10)
    59049
    >>> # Já vimos funções, agora vamos aos tipos de dados.
    >>> # Primeiro a tupla, que é um agrupamento de variáveis,
    >>> # funciona de forma parecida com um array:
    >>> t = (1,2,3,4,5,6,7,8,9)
    >>> t
    (1, 2, 3, 4, 5, 6, 7, 8, 9)
    >>> t[0]
    1
    >>> t[9]
    Traceback (most recent call last):
      File "<interactive input>", line 1, in <module>
    IndexError: tuple index out of range
    >>> # ooops
    >>> t[8]
    9
    >>> # Os recursos de índice negativo e de slicing que funcionam
    >>> # com o tipo string também funcionam com tuplas, assim como
    >>> # com qualquer tipo que siga o conceito "sequence"
    >>> t[-1]
    9
    >>> t[len(t)/2:]
    (5, 6, 7, 8, 9)
    >>> # outro recurso interessante das tuplas é o "desempacotamento automático":
    >>> t = (1,2,3) 
    >>> t
    (1, 2, 3)
    >>> a,b,c = t
    >>> a
    1
    >>> b
    2
    >>> c
    3
    >>>
    >>> # Agora vamos ao list, que funciona de forma parecida com a tupla
    >>> l = [1,2,3,4,5,6,7,8]
    >>> l
    [1, 2, 3, 4, 5, 6, 7, 8]
    >>> type(t)
    <type 'tuple'>
    >>> type(l)
    <type 'list'>
    >>> # Note a diferença na declaração, tuplas com () e listas com []
    >>> # Como o parênteses é também usado para agrupar expressões, uma
    >>> # sintaxe especial é necessária para tuplas com 1 elemento:
    >>> type([1])
    <type 'list'>
    >>> type((1))
    <type 'int'>
    >>> type((1,))
    <type 'tuple'>
    >>> # Você já deve ter reparado que o "for" no Python é equivalente
    >>> # ao "foreach" de outras linguagens:
    >>> for x in [0,1,2,3]:
    ...   print x
    ...   
    0
    1
    2
    3
    >>> # Para reporduzir o comportamento do "for" com contador numérico
    >>> # usamos a função range, que gera uma lista com os números a serem 
    >>> # percorridos:
    >>> range(10)
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    >>> for x in range(10):
    ...   print x,
    ... 
     0 1 2 3 4 5 6 7 8 9
    >>> # Antes que você pergunte, a mágica de fazer o print exibir os
    >>> # números todos na mesma linha foi feita colocando uma vírgula
    >>> # no fim da linha do print
    >>> 
    >>> # A diferença entre a tupla e a lista é que a tupla é imutável.
    >>> t
     
    (1, 2, 3, 4, 5, 6, 7, 8, 9)
    >>> t[2] = 10
    Traceback (most recent call last):
      File "<interactive input>", line 1, in <module>
    TypeError: 'tuple' object does not support item assignment
    >>> l[2] = 20
    >>> l
    [1, 2, 20, 4, 5, 6, 7, 8]
    >>> # Além disso, a lista pode ser usada como array, como fila e
    >>> # como pilha
    >>> l = []
    >>> l
    []
    >>> l.append(5)
    >>> l.append(10)
    >>> l
    [5, 10]
    >>> del l[0]
    >>> l
    [10]
    >>> l = [] # vamos usar uma lista como pilha
    >>> l.append(1)
    >>> l.append(2)
    >>> l.append(3)
    >>> l
    [1, 2, 3]
    >>> l.pop()
    3
    >>> l.pop()
    2
    >>> l.pop()
    1
    >>> l.pop()
    Traceback (most recent call last):
      File "<interactive input>", line 1, in <module>
    IndexError: pop from empty list
    >>> l
    []
    >>> range(10, 20, 2) # inicio (inclusive), fim (exclusive), passo
    [10, 12, 14, 16, 18]
    >>> l = range(10, 20, 2)
    >>> l
    [10, 12, 14, 16, 18]
    >>> l.index(12) #procura um item e retorna qual seu índice
    1
    >>> del l[l.index(12)] # removendo o item
    >>> l
    [10, 14, 16, 18]
    >>> t
    (1, 2, 3, 4, 5, 6, 7, 8, 9)
    >>> l.extend(t) # extendendo a lista com outra sequência (lista ou tupla)
    >>> l
    [10, 14, 16, 18, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    >>> l = []
    >>> l.append(1)
    >>> l.append(2)
    >>> l.append(3)
    >>> l
    [1, 2, 3]
    >>> l.pop(0) # dessa forma eu retiro o primeiro item da lista. Agora eu tenho uma fila
    1
    >>> l.pop(0)
    2
    >>> l.pop(0)
    3
    >>> l.pop(0)
    Traceback (most recent call last):
      File "<interactive input>", line 1, in <module>
    IndexError: pop from empty list
    >>> l
    []
    >>> l = range(10)
    >>> l
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    >>> l.reverse()
    >>> l
    [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
    >>> l.sort()
    >>> l
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    >>> l.sort(reverse=True)
    >>> l
    [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
    >>>

    Em 23/08/2007 22:42 - Comentários (0)

    PyConBrasil: eu estarei lá

    Nessa quinta, sexta e sábado (30 e 31 de agosto, e 1º de setembro) eu estarei em Joinville participando da PyConBrasil, a terceira versão da convenção brasileira sobre Python. Acho que todo mundo que lê esse site sabe do meu recente interesse por essa linguagem.

    No meio onde eu trabalho é difícil conhecer pessoas que usam Python, e quando eu vejo alguém falando de Python em algum site brasileiro é quase sempre sobre programação Web. Acredito que essa é uma ótima oportunidade para tentar conhecer alguém que use Python para algo que não seja fazer sites...

    Se alguém for e quiser marcar para tomarmos uma (cerveja|guaraná|água mineral) e conversar sobre Python me avise. :-)

    Em 28/08/2007 14:53 - Comentários (2)

    Estou me mudando para Porto Alegre

    Emprego novo, mas nada do clichê de "novos desafios". Estou me mudando para Porto Alegre para voltar aos velhos desafios: segurança da informação, drivers, filtros de file system, telas azuis e outras escovações de bit. Vou trabalhar na Axur, e pelo pouco que conheci deles vejo que estarei em boas mãos. Além da escovação de bits pura e simples, terei uma dose maior de "alta performance e alta disponibilidade" do que tive nos tempos da Scua e nesses quase 3 anos de Macsys e BM&F. Parece que lá terei finalmente a oportunidade de montar um cluster ativo/ativo para fins computacionais. Cluster ativo/passivo só pra fins de alta disponibilidade já virou carne de vaca... ;-)

    Como é impossível coordenar a agenda de todos os amigos e colegas para uma pseudo-despedida (PoA é 1:20hs de avião de SP), tenho uma proposta melhor. Durante todas as terças feiras enquanto eu ainda estiver em São Paulo estarei no Bar Barão (perto do metrô Luz) a partir das 18:30hs esperando amigos e colegas para tomar o melhor chopp que eu conheço nessa cidade (sim, eu já conheço o Bar do Leo, Juarez, All Black, Outback, etc). Estão todos convidados. Só lembrem-se que eu ganhei um emprego novo, não ganhei na loteria. Sendo assim, na hora de pagar a conta é cada um por si... :-D

    Eu devo levar umas 3 semanas para ir, então temos pelo menos 3 terças. Terça foi o dia escolhido porque é dia da promoção da Brahma onde você toma 2 chopps e paga 1. E se você não bebe, a comida lá é MUITO boa, ou seja, não tem desculpa para não aparecer...

    Em 11/09/2007 03:16 - Comentários (10)

    Palestra sobre IronPython no evento TechRaptors 2008

    Dia 29 de setembro (sábado), eu estarei no evento TechRaptors 2008, falando sobre IronPython, a implementação da linguagem Python para .NET. A linguagem Python está ganhando cada vez mais visibilidade na plataforma .NET. Além do suporte oficial por parte da Microsoft (a Microsoft contratou o programador que criou o IronPython), o Python é uma das linguagens suportadas logo na primeira versão do Silverlight.

    Espero vocês lá.

    Em 13/09/2007 16:13 - Comentários (4)

    Slides da palestra sobre IronPython

    Como prometido, os slides que usei na palestra de IronPython podem ser acessados em http://docs.google.com/Present?docid=dg9kbfzn_28gwqfhn&fs=true. Assim que essa mudança aqui para Porto Alegre terminar voltaremos a ter posts com mais de um parágrafo... :-)

    Em 05/10/2007 15:40 - Comentários (2)

    Acessando a API Win32 usando Python

    Como muitos devem saber, eu voltei a trabalhar com drivers. Meu projeto específico aqui na Axur é o AMS, um software que (simplificadamente) monitora o acesso à file servers. Durante a caçada à um bug eu precisei fazer um programa de testes para reproduzir um cenário descrito no nosso controle de bugs. Nesse cenário, eu precisa acessar certos arquivos com flags específicas da função CreateFile da API Win32.

    Fácil. É só abrir o Visual C++ e escrever algumas linhas. Mas Python é mais indicado para programinhas pequenos de teste (não preciso de performance e o fato do código fonte ser aberto não é um problema). Então decidi fazer em Python, e vou mostrar aqui as duas opções que eu conheço para isso.

    A primeira delas é usar o ctypes, que vem na distribuição padrão do Python 2.5. "Batteries included", lembra? Essa biblioteca provê um acesso MUITO fácil à código C (DLLs, APIs, etc) usando Python. Sinceramente, nunca vi nada tão fácil e prático, nem em VB ou C#. O código fica simples assim:

    >>> import ctypes
    >>> CreateFile = ctypes.windll.kernel32.CreateFile
    Traceback (most recent call last):
      File "<interactive input>", line 1, in <module>
      File "C:\Python25\lib\ctypes\__init__.py", line 353, in __getattr__
        func = self.__getitem__(name)
      File "C:\Python25\lib\ctypes\__init__.py", line 358, in __getitem__
        func = self._FuncPtr((name_or_ordinal, self))
    AttributeError: function 'CreateFile' not found
    
    >>> # ooops. Lembra das funções ANSI e UNICODE? Já falei detalhadamente sobre isso
    >>> # nos tutoriais de WinDbg (que pretendo retomar agora que voltei a fazer drivers)
    >>> CreateFile = ctypes.windll.kernel32.CreateFileA
    >>> CreateFile
    <_FuncPtr object at 0x012DF288>
    >>>

    Simples assim. A variável CreateFile já é um ponteiro para a função CreateFile da API. Como exemplo vamos ler o arquivo boot.ini:

    >>> # ** CONTINUAÇÃO**
    >>> # uma ajudinha. Copiei isso do winnt.h. Como são defines, 
    >>> # não dá pra "importar" como as funções
    >>> GENERIC_READ = 0x80000000
    >>> GENERIC_WRITE = 0x40000000
    >>> FILE_SHARE_READ = 0x00000001
    >>> OPEN_EXISTING = 3
    >>> h = CreateFile(r'c:\boot.ini', GENERIC_READ, FILE_SHARE_READ,
         None, OPEN_EXISTING, None, None)
    >>> # se for erro, retorna INVALID_HANDLE_VALUE, que é 0xFFFFFFFF
    >>> h
    532
    >>> # tudo ok. vamos ler
    >>> ReadFile = ctypes.windll.kernel32.ReadFile
    >>> # preciso de um buffer para ler o arquivo.
    >>> buffer = ctypes.create_string_buffer(4096)
    >>> buffer
    <ctypes.c_char_Array_4096 object at 0x012DDE40>
    >>> #preciso também passar o ponteiro de um LONG (int) para saber o quanto foi lido
    >>> BytesRead = ctypes.c_long()
    >>> BytesRead
    c_long(0)
    >>> ret = ReadFile(h, buffer, len(buffer), ctypes.byref(BytesRead), None)
    >>> ret
    1
    >>> # funcionou
    >>> print buffer.value
    [boot loader]
    timeout=30
    default=multi(0)disk(0)rdisk(0)partition(1)\WINDOWS
    [operating systems]
    multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft Windows XP Professional" 
    /noexecute=optin /fastdetect
     
    >>> #bytes lidos:
    >>> print BytesRead.value
    211
    >>> # Não preciso pegar o ponteiro de função em uma variável,
    >>> # posso chamar diretamente
    >>> ctypes.windll.kernel32.CloseHandle(h)
    1
    >>>
    

    A segunda opção é usar o pacote PyWin32, que é um wrapper para a API Win32 feito em Python. Ele encpasula praticamente toda a API Win32, toda a runtime COM e a MFC. Esse pacote vem com uma IDE para Python muito boa, chamada PythonWin. Ela é feita em Python usando MFC.

    Talk is cheap, show me the code:

    >>> import win32file
    >>> h = win32file.CreateFile(r'c:\boot.ini', win32file.GENERIC_READ, 
    win32file.FILE_SHARE_READ, None, win32file.OPEN_EXISTING, 0, 0)
    >>> h
    <PyHANDLE:504>
    >>> err, str = win32file.ReadFile(h, 1024)
    >>> err
    0
    
    >>> print str
    [boot loader]
    timeout=30
    default=multi(0)disk(0)rdisk(0)partition(1)\WINDOWS
    [operating systems]
    multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft Windows XP Professional" 
    /noexecute=optin /fastdetect
    >>> Não precisamos fechar o handle, o tipo PyHANDLE cuida disso
    >>> Use "del h" se quiser fechar na hora
    

    Algumas coisas são mais fáceis usando PyWin32, mas temos também algumas desvantagens. As assinaturas das funções não são exatamente as mesmas da API e esse pacote vem separado da distribuição padrão do Python.

    Em 20/10/2007 23:56 - Comentários (1)

    Palestra na BorCon: Gerenciamento de memória do Windows

    Dia 29 de outubro (segunda feira), estarei de volta à minha querida (ex) terra da garoa ministrando a palestra "Por Dentro do Windows: Gerenciamento de Memória" na Borland Conference Brasil 2007, o evento para desenvolvedores Borland.

    As demonstrações serão feitas usando o C++ Builder 2007, a única ferramenta realmente RAD para C++ que existe :-) (Não, C++/CLI não conta, não é C++)

    Em 26/10/2007 03:02 - Comentários (0)

    Voltando do Python para o C++ com bastante coisa na bagagem

    Como visto por todos, passei um bom tempo programando e estudando Python. Gostei muito do que vi, mas no meu dia-a-dia (serviços, drivers, etc) ainda há mais espaço para C++ do que para Python. Durante esses meses eu cheguei a implementar um pequeno roteador de mensagens em Python usando Twisted e gostei muito do estilo de programação assíncrona, inclusive usando coroutines. Os tipos de dados do Python me ajudaram muito e tornaram meu trabalho muito mais produtivo que antes.

    Bom, depois de tudo isso, eu voltei a estudar C++. Eu já estava a muito tempo querendo estudar Boost com mais calma, e foi isso que eu fiz. Gostei muito de tudo que eu vi, e tive uma ótima surpresa: muitos dos recursos e tipos de dados existentes no Python estão disponíveis para C++ por meio do Boost (tudo bem tipado e especificado, é claro). Estou usando muita coisa do Boost em um novo projeto e a produtividade e possibilidade de abstração aumentaram bastante. Antes eu usava basicamente os smart pointers, regex e string algorithms. Agora minha filosofia é procurar no Boost antes de fazer qualquer biblioteca para qualquer coisa. Além disso, para quem faz software multiplataforma, o Boost vale ouro.

    Aos que esperavam por isso, esse é sinal de que voltarei a escrever sobre C++. :-)

    Em 22/11/2007 19:55 - Comentários (0)

    Programando melhor: padrão de nomenclatura

    Padrão de nomenclatura é basicamente definir como serão nomeadas as funções, variáveis, classes e como formatar o código fonte. Isso não muda absolutamente nada no código de máquina gerado pelo compilador, mas faz toda a diferença para os programadores que fazem o software. Essa é uma discussão calorosa, com muita gente tentando explicar porque seu padrão/estilo é o melhor. E, como em quase todas as discussões calorosas, ninguém está certo. Nenhum padrão de nomenclatura é melhor do que o outro. Um padrão é um padrão, ele só precisa existir e ser funcional. Dito isso, algumas coisas devem ser consideradas.

    É necessário ter um padrão de nomenclatura. Não importa se é camelCase ou FluFFersCasE, é importante definir uma padrão, nem que seja informal. Discuta entre os desenvolvedores e defina. Quase todas as linguagens tem um padrão recomendado por quem faz a linguagem, e é recomendável (mas não obrigatório) usá-lo para que o seu código siga o mesmo estilo das bibliotecas padrão. C# e Java são exemplos de linguagens com padrões bem definidos, enquanto C e C++ têm um padrão mais informal. Na Wikipedia existe uma lista boa sobre o que deve ser padronizado. Para C++ eu costumo usar o seguinte:

    
    #define MACRO_DE_DEBUG printf
    
     //
     // comentários explicativos e com uma linha em branco em cima
     // e outra embaixo. Não usar comentários em bloco: /*  */
     // Isso dificulta comentar código já que não pode haver um 
     // comentário dentro do outro
     // 
    class UmaClasse
    {
      int m_variavelDeClasse;
      char* m_ponteiro;
      ClasseComConstrutor m_c1, m_c2;
    
      enum UmEnum
      { Item1, Item2, Item3, Item4 };
    
    public:
    
     UmaClasse() :
       m_c1("abc"),
       m_c2("def")
     {}
    
     void MetodoDeClasse(int a, int b, int c)
     {
       int variavelLocal = 10;
     }
    
     //
     // sem espaço antes ou depois dos parênteses ou chaves
     // e UM espaço depois das vírgulas
     // espaço entre os operadores matemáticos
     // chaves embaixo
     // 
     void OutroMetodoDeClasse(int a, int b, int c)
     {
       int a;
       a = 10;
       a += 5;
       ++a;
       a = b + c;
    
       for(int x = 0 ; x < 10 ; ++x)
       {
         if(x == 5)
           break;
          // 
          // nada de colocar tudo na mesma linha:
          // if(x == 5)break;
          //
       }
    
       int i = FuncaoComUmMonteDeParametros(
         "parametro1",
         "parametro2",
         "parametro3",
         "parametro4",
         "parametro5");
    
       MetodoDeClasse(a, b, c);
     }
    
     void SetVariavelDeClasse(int i)
     {
       m_variavelDeClasse = i
     }
    
     //
     // ainda estou pensando em abolir o Get, tipo
     // int variavelDeClasse();
     // mas ainda estou só pensando
     //
     int GetVariavelDeClasse()
     {
       return m_variavelDeClasse;
     }
    };
    
    

    Quando der manutenção em um programa, use o mesmo estilo de nomenclatura. Não importa se o padrão é porco, se você não gosta. É um padrão. Mude o projeto inteiro se tiver tempo para isso, mas não use dois padrões para o mesmo projeto. Isso torna o código mais porco ainda. Eu sei, é difícil de ler, difícil de entender, mas é assim que está feito. Sempre que pegar um código novo para modificar, tente entender qual é o padrão de nomenclatura e siga-o.

    Use nomes claros e explicativos para as funções e variáveis. A função ApagaClientePendentes é melhor do que DelCliPen ou del_cliente_p. Os códigos fontes são feitos para seres humanos entenderem, não para que o computador entenda. Quem gera o que o computador entende é o compilador. E não se esqueça: se alguém não gostar dos nomes gigantescos, uma manhã de find/replace resolve o problema.

    Não economize nos comentários. Já falei bastante sobre isso. O argumento de que os comentários podem ficar fora de sincronia e complicar mais a sua vida é totalmente furado. É o mesmo que dizer que não vai fazer uma parte do software porque ela pode ter bugs.

    Em 27/11/2007 15:48 - Comentários (7)

    Como sincronizar um e-mail POP3 ou Exchange com o Gmail usando o Outlook

    Esse blog continua sendo voltado para programadores, apesar dessa dica útil, porém dummy. Eu assumo que você sabe o que é POP3 e IMAP (e sabe como conseguir a RFC que especifica esses protocolos), além de possivelmente já ter feito um software C/C++ implementando algum desses protocolos.

    É extremamente irritante ter que procurar um e-mail em vários lugares diferentes, ou chegar em casa e descobrir que o número de telefone que você precisa está no Outlook do trabalho. Ou, pior ainda, quando sua empresa te dá uma caixa de e-mail de 50MBs, mesmo quando um HD de 500GB é mais barato do que o tempo que você gasta procurando quais e-mails podem ser apagados (conseqüentemente, tempo sem programar). Com o suporte a IMAP do GMail você pode resolver esse problema com alguns clicks.

    Eu já tentei configurar o Outlook para fazer um forward dos e-mails via POP3, mas nunca consegui fazer isso funcionar. Por isso, a solução que segue usa IMAP:

    Pronto. Agora todos os seus e-mails estão em um lugar só, e você usa o search do Google ao invés do search totalmente-meia-boca do Outlook. Além disso, você pode apagar seus emails do POP3/Exchange logo depois de ler, pois fica tudo arquivado no GMail.

    Se sua empresa tem um proxy HTTP, você não vai conseguir conectar no servidor IMAP do GMail. Procure no Google sobre "ssh tunnel" e "http tunnel" e burle o proxy da sua empresa por sua conta e risco.

    Em 30/11/2007 16:37 - Comentários (1)

    Vai usar esse computador? Tem Carteira de Habilitação?
    http://www.leo.org/information/freizeit/fun/cars.html

    Em 05/12/2007 15:06 - Comentários (0)

    Programando melhor: evite criticar o código alheio

    Uma das situações comuns na vida de um escovador de bits é a necessidade de editar código escrito por outras pessoas. E essa é uma situação com a qual muitos programadores não estão preparados para lidar, pois envolve muito mais o lado pessoal do que o lado técnico. Mas, tecnicamente, verdade seja dita: é difícil ler e entender código dos outros. Pelo simples fato da linha de raciocínio ser diferente. É difícil, mas algo que precisamos aprender a lidar.

    Todo programador segue uma linha de raciocínio para implementar uma funcionalidade em um software, linha essa que foi moldada com anos de programação. Independente de design patterns ou padrão de codificação, a forma de resolver problemas é algo muito pessoal. Tudo que o programador lê ou aprende sobre algoritmos é absorvido e empregado de formas diferentes. Patterns e standards ajudam a minimizar isso, mas não modificam a forma como as coisas são.

    Criticar código dos outros, principalmente para seus colegas de trabalho, é chato e pode te causar problemas. A pessoa que escreveu o código pode ser quem senta do seu lado, e talvez ele não goste muito de ser criticado (alguém gosta?). Além disso, todo código escrito tem uma história, e no contexto dessa história tudo tem um bom motivo para ter sido feito daquela forma. Você não pode julgar cegamente algo só por aquilo que você está vendo.

    Um bom motivo para um código aparentemente ruim é a inexperiência de quem escreveu. Você não pode exigir de um programador iniciante a mesma qualidade de algoritmo de alguém com dez anos de experiência. Você já foi iniciante um dia, e acredito que quando lê um código que você escreveu a alguns anos atrás chega a sentir vergonha. Mas é assim que funciona, as pessoas aprendem, ninguém começa sabendo. Além disso, se um programador iniciante foi colocado para fazer uma parte crítica do sistema, a culpa é de quem o alocou para essa tarefa.

    Você nunca sabe a situação que levou à criação de um certo código. Talvez o código não tenha sido feito da melhor maneira possível porque era véspera de feriado e o programador queria ir embora. Justificável ou não, é um bom motivo. Se o programador ficou enrolando a semana inteira e resolveu fazer uma gambiarra às 17:30 de sexta é um problema dele com o gerente dele, não é um problema seu. Talvez o gerente inventou um prazo maluco sem consultar os programadores, o que é a coisa mais comum desse planeta. O fato é: você não sabe.

    Nós somos pagos para resolver problemas, certo? Resolva os problemas e evite criar outros. Criticar código alheio só cria problemas, raramente resolve alguma coisa. Já conheci gente com bom conhecimento técnico mas que ninguém suportava, tudo devido à esse costume de criticar todo o código que aparecia pela frente. Espero que todo mundo saiba que só conhecimento técnico não resolve.

    Na frase "código feio que está funcionando em produção", a parte que importa é a "...está funcionando em produção...". Pense bastante antes de refazer algo que está funcionando, e não critique o código. Fale em "reestruturação para facilitar a manutenção" e não em "refazer aquele troço nojento que eu não entendo". Afinal, como você pode refazer algo que não entende?

    Em 06/12/2007 16:50 - Comentários (8)

    Tutorial informal de Python, parte 4
    >>> # Mais uma parte do nosso tutorial informal. Hoje vamos
    >>> # começar entendendo o que são iterators e suas aplicações
    >>> # Existem duas formas básicas para gerar uma sequência para
    >>> # ser enumerada em python. Primeiro temos o nosso já conhecido
    >>> # range:
    >>> for x in range(10):
    ...   print x,
    ...   
    0 1 2 3 4 5 6 7 8 9
    >>> # E temos o xrange:
    >>> for x in xrange(10):
    ...   print x,
    ...   
     0 1 2 3 4 5 6 7 8 9
    >>> # apesar do efeito ser o mesmo e o protótipo das duas funções
    >>> # serem iguais, existe uma diferença grande na implementação:
    >>> range(10)
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    >>> xrange(10)
    xrange(10)
    >>> # enquanto o range gera uma lista (ou seja, aloca todos os números), o xrange
    >>> # retorna um objeto xrange que gera o número quando ele é solicitado. Isso pode
    >>> # reduzir o consumo de memória e ser bastante eficiente caso você não percorra
    >>> # todos os itens. Agora vamos ver por que eles são compatíveis.
    >>> # Como já vimos antes, todos os métodos e propriedades que começam e terminam
    >>> # com dois underlines (como o __dict__ que já vimos) são métodos que tem algum
    >>> # significado especial para o interpretador Python.
    >>> # Um desses métodos é o __iter__, que retorna um objeto iterator:
    >>> l
    xrange(10)
    >>> i = l.__iter__()
    >>> i
    <rangeiterator object at 0x00ACA818>
    >>> dir(i)
    ['__class__', '__delattr__', '__doc__', '__getattribute__', 
    '__hash__', '__init__', '__iter__', '__length_hint__', 
    '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', 
    'next']
    >>> # o método que nos interessa é o next. Todo loop em Python é o equivalente a:
    >>> i = l.__iter__()
    >>> while 1:
    ...   try:
    ...     x = i.next()
    ...   except StopIteration:
    ...     break # passamos do último item
    ...   print x,
    ...   
    0 1 2 3 4 5 6 7 8 9
    >>> 
    >>> # Como Python usa duck typing (lembra? Se parece um pato e anda como um pato,
    >>> # é um pato), não é necessário que o iterator implemente uma interface
    >>> # especial. Tendo o método next() e disparando a exceção StopIteration quando
    >>> # os itens acabarem, tudo certo.
    >>> 
    >>> # Outra coisa que pode ser feita, é usar o iterator de uma classe nativa
    >>> # para suportar iteração no seu objeto. Exemplo:
    >>> class MyList:
    ...   def __init__(self):
    ...     self.l = []
    ...   def __iter__(self):
    ...     return self.l.__iter__()
    ...   def Append(self, x):
    ...     self.l.append(x)
    ...     
    >>> l = MyList()
    >>> l.Append('a')
    >>> l.Append('b')
    >>> l.Append('c')
    >>> for x in l:
    ...   print x
    ...   
    a
    b
    c
    >>> # Resumindo: para suportar iteração seu objeto só precisa ter um método __iter__
    >>> # que retorne um objeto que tenha um método next e que dispare StopIteration quando acabar
    

    Em 17/12/2007 20:31 - Comentários (0)

    Como montar um servidor iSCSI Linux

    Um dos problemas que eu sempre tive desenvolvendo coisas para o Windows Server Cluster foi a disponibilidade de servidores de testes. O problema é que é necessário ter um storage compartilhado entre os nós do cluster, e como um storage não é lá algo muito barato, já dá para imaginar qual é o problema...

    Uma solução para esse problema é o iSCSI, o SCSI sobre IP. Ou seja, ao invés de precisar de um storage com fiber channel ou algo assim, qualquer coisa que fale o protocolo iSCSI resolve. Isso facilita MUITO a vida de quem precisa de algo simples para testes ou usos não-produção - você não precisa criar disks, volumes, aggregates, RAIDs e eteceteras, é só exportar um disco.

    Uma solução fácil é montar um servidor Linux dedicado para iSCSI - e você pode usar o VMWare Server ou o ESX para isso. Se você não tem ainda um Linux instalado, eu sugiro o Ubuntu Server. É bem fácil de instalar, e desmarcando todas as opções de serviços no setup (LAMP Server, Samba Server, etc), com 400MB de HD você já tem um server rodando. Mesmo que você seja um usuário Windows e não entenda nada de Linux, um google por "bash tutorial", a receita de bolo que eu vou passar, e uma procurada no How-To Forge resolve seu problema. Nem que seja até você encontrar um iSCSI Target para Windows - eu não achei nenhum grátis ou open source.

    Na parte client (Windows) tudo que você precisa fazer é instalar o iSCSI Initiator da Microsoft. Eu testei no Windows Server 2003. O Initiator já vem junto com o Vista (e acredito que deva vir junto com o Windows Server 2008 também).

    Para o server Linux, usaremos o iSCSI Enterprise Target (ietd), uma implementação open source do protocolo iSCSI. Para garantir que teremos sempre a última versão, compilaremos os fontes diretamente do Subversion do SourgeForge ao invés de usar os pacotes pré compilados da sua distribuição favorita do Linux.

    Depois de ter seu Linux instalado e rodando, siga a seguinte receita de bolo:

    • Certifique-se que você é root (faça login como tal, ou use "su -" ou "sudo -i").
    • Vamos precisar do client do Subversion, OpenSSL, GCC e uns header do kernel do Linux. No Ubuntu/Debian, um "apt-get install subversion linux-headers-2.6.22-14-server libssl-dev linux-libc-dev gcc make" resolve.
    • Baixe os fontes diretamente do Subversion do pessoal do ietd. "svn checkout svn://svn.berlios.de/iscsitarget/trunk".
    • Agora é só compilar e instalar. Entre no diretório trunk e "make all install install-initd".

    Agora que tudo está instalado, é só editar o arquivo de configuração, que fica em /etc/ietd.conf. Ele é bem fácil de entender, e já tem um exemplo comentado lá dentro. Como ilustração, segue o arquivo de configuração que eu montei para meus clusters de teste. São 6 discos, 3 para cada cluster:

    #
    # developers
    #
    Target iqn.2007-12.net.axur:ams.dev.storage1
      Lun 0 Path=/storage/ams.dev.storage1,Type=fileio
    
    Target iqn.2007-12.net.axur:ams.dev.storage2
      Lun 1 Path=/storage/ams.dev.storage2,Type=fileio
    
    Target iqn.2007-12.net.axur:ams.dev.storage3
      Lun 2 Path=/storage/ams.dev.storage3,Type=fileio
    
    
    #
    # testers
    #
    Target iqn.2007-12.net.axur:ams.test.storage1
      Lun 3 Path=/storage/ams.test.storage1,Type=fileio
    
    Target iqn.2007-12.net.axur:ams.test.storage2
      Lun 4 Path=/storage/ams.test.storage2,Type=fileio
    
    Target iqn.2007-12.net.axur:ams.test.storage3
      Lun 5 Path=/storage/ams.test.storage3,Type=fileio
    

    Eu criei uma pasta /storage, e lá criei um arquivo para cada disco. Como é necessário criar os arquivos já com o tamanho do disco, é só usar o comando dd. "dd if=/dev/zero of=storage2 bs=1M count=400", por exemplo, cria um arquivo storage2 com 400 MB. Para entender como funcionam os identificadores dos disco, veja o artigo sobre iSCSI da Wikipedia. Você pode exportar um disco físico inteiro se quiser, veja na documentação do ietd.

    Depois disso é só reinic... Ops, desculpa, essa é a parte Linux :-). Um "/etc/init.d/iscsi-target start" e tudo está funcionando. Feito isso, você precisa adicionar o servidor Linux como "Target Portal" no iSCSI Initiator do Windows, e na aba "Targets", fazer Log On para o Windows disponibilizar os discos. Depois disso, vá ao "Computer Management" >> "Disk Management" e inicialize/particione/formate seus novos discos como quiser.

    Outra opção é usar o OpenFiler ou o FreeNas, que são distribuições Linux e BSD respectivamente, que além de exportar iSCSI funcionam como storage server. Eles têm serviços para CIFS (protocolo usado pelo Windows), NFS, FTP, etc, etc. Mas se você precisa só de iSCSI, um Linux e os passos que eu descrevi é tudo que você precisa.

    Em 02/01/2008 19:19 - Comentários (3)

    Menos metodologia e mais escovadores de bit

    Parece que a cada ano que passa mais as empresas se preocupam em implementar metodologias. Algumas são metódicas e adotam algo como RUP (sigla para Gerenciamento De Peão De Obra) sem mudar uma vírgula sobre o que está no manual. Mas a grande maioria mistura um monte de coisas e chama de "Metodologia de Desenvolvimento EmpresaX". A adoção de metodologias pode trazer muitos benefícios, mas o que muita gente não percebe é que uma metodologia de desenvolvimento não salva uma equipe com programadores inexperientes ou simplesmente incompetentes.

    Não adianta nada ter um livro de 300 páginas com toda a especificação e documentação do sistema se os programadores não têm competência suficiente para implementar. A análise é importante, espero que ninguém entenda que estou dizendo o contrário. Mas um software com uma ótima análise e uma péssima implementação ainda é um péssimo software. O usuário não vê especificação (e muitas vezes nem a documentação), vê um software sendo executado. Se ele é lento ou dá pau, não adianta falar que seu analista funcional é pós graduado em sei-lá-onde. Você precisa de mais escovadores de bits, não de mais gente que passa o dia no Project e no Word.

    Programadores bons custam caro. É claro, tudo que é bom custa caro. Economizar dinheiro na contratação de programadores é um erro que sempre (sempre mesmo) aparece no final do projeto. Já vi muitas empresas (muitas mesmo) que tentam melhorar a qualidade do software implantando mais metodologias e mais burocracias - inclusive, contratando mais gente para gerenciar isso. Todo programador com mais de 5 anos de experiência conhece as histórias de equipes inteiras pedindo demissão no prazo de um mês. Eu conheço umas 3 empresas onde esse ciclo de renovar mais de 80% da equipe é de dois anos. E uma grande equipe de analistas e gerentes não programadores é quase sempre um dos fatores que causa isso. Alguns dias atrás conversei com um amigo - excelente programador - que está mudando de emprego porque cansou de tentar explicar o que ele faz para um bando de gerentes que não entendem nada de programação (aos chatos de plantão: eu sei, existem bons gerentes não técnicos, mas essa é a exceção, não a regra).

    Existem empresas que, quando a coisa começou a desandar, contrataram programadores experientes. Na esmagadora maioria das vezes os problemas são resolvidos e a carga de trabalho de todo mundo diminui. As diferentes visões dos programadores experientes ajudam a empresa a resolver os problemas de formas muito mais simples. E é muito melhor e recompensador trabalhar com gente experiente, das quais se pode aprender alguma coisa, do que rodeado de um monte de estagiários e novatos. É importante ter novatos e estagiários, mas eles devem ser minoria na equipe.

    E o motivo disso não é só pela parte técnica. Programadores experientes são mais práticos e calmos sob pressão, afinal, enquanto um programador inexperiente (ou pior, um estagiário) diz que o mundo acabou, o programador experiente lembra de coisas básicas como "já reiniciaram o servidor?" ou "já verificou se localhost está apontando mesmo para máquina local?". Com o tempo você aprende a ignorar ao máximo a pressão e focar em fazer o seu trabalho e resolver o problema. Palavra de alguém que trabalhou vários anos no mercado financeiro...

    Resumindo a fórmula nem-tão-mágica: as metodologias são boas e válidas, mas não substituem bons programadores. Contrate programadores bons e experientes e deixe que ELES decidam qual metodologia usar. Coloque um gerente técnico com eles, que saiba deixar a pressão longe para eles trabalharem em paz. Espere um tempo e veja software de qualidade começar a ser produzido.

    Em 08/01/2008 20:10 - Comentários (11)

    Chega a dar vontade de desinstalar...

    Por que colocar gente tão feia no setup do Visual Studio 2008? Alguma insinuação?




    Em 10/01/2008 20:00 - Comentários (19)

    Usando o vi pela primeira vez
    http://bash.org/?795779?

    Em 17/01/2008 22:45 - Comentários (8)

    Win32: O começo de tudo

    Começa nesse momento uma série de posts falando sobre programação Win32. Além de falar das APIs, eu também explicarei os componentes do Windows dos quais essas APIs dependem. É quase uma aula de Sistemas Operacionais, mas com coisas práticas ao invés de ficar escutando alguém repetir o livro do Tanenbaum :-). Aos que não sabem o que é Win32, já falei sobre isso, e sobre os motivos para estudar Win32.

    Para acompanhar a série, você precisa somente de um compilador C++ e o Microsoft Platform SDK. As opções que eu conheço são:

    • Visual Studio Professional: Ele já tem tudo que você precisa.
    • Visual C++ Express: Você precisa dele e do Platform SDK. Na página de download do Express existe um link para download do SDK.
    • Outros: Existem várias outras opções, como o Borland C++ Builder (ou o novo Turbo C++), Dev-C++, Ultimate++, CodeBlocks, etc. Se você quer só o compilador e gosta mesmo é do Bloco de Notas, o MinGW também resolve o seu problema. Só não se esqueça de configurar sua IDE/makefile/sei-lá-o-que para procurar os headers e as libs no Platform SDK.

    Se você conseguir compilar o código abaixo é porque tudo está funcionando:

    
    #define WIN32_LEAN_AND_MEAN
    #include <windows.h>
     
    //
    // programa Win32 tem como entry point (função inicial) a função WinMain
    //
    int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,int nCmdShow)
    {
      MessageBox(NULL, "Start Me Up!", "Win32", MB_OK);
      return 0;
    }
    

    Em 24/01/2008 17:06 - Comentários (9)

    Win32: Processo

    Um processo é, simplificadamente, uma instância de um programa que está sendo executado em um determinado momento. Por exemplo, quando abrimos três instâncias do Bloco de Notas, temos três processos independentes.

    Vale lembrar que apesar de ser comum o termo "executar" um processo, o que executa não é o processo, e sim uma ou mais threads que fazem parte desse processo (mais sobre threads depois).

    No exemplo acima vimos que 3 instâncias do Bloco de Notas são três processos diferentes, que têm em comum o fato do código executável ser carregado do mesmo arquivo, o notepad.exe. Um processo é um container para diversas estruturas, entre elas:

    • Espaço de endereçamento de memória. Cada processo tem seu espaço de endereçamento isolado.
    • Handles de arquivos, sockets, dispositivos, chaves de registros, etc.
    • Tokens de segurança e accesso
    • Threads, que são as linhas de execução que rodam o código do processo. Todo processo é constituído de uma ou mais threads, criadas pelo próprio processo ou por componentes do Windows.

    Em Win32, a função usada para criar processos é a (surpresa!) CreateProcess:

    #include <windows.h>
    #include <stdio.h>
     
    void main()
    {
        STARTUPINFO si;
        PROCESS_INFORMATION p;
     
        //
        // dica: muitas estruturas no Windows têm uma variável membro chamada
        // cb ou cbSize. Esse membro deve ser preenchido com o sizeof()
        // da estrutura ANTES de chamar a função. Se você não fizer isso a função 
        // retornará um erro. Esse é um erro comum de iniciantes (eu apanhei muito...)
        //
        ZeroMemory(&si, sizeof(si));
        si.cb = sizeof(si);
     
        ZeroMemory(&p, sizeof(p));
     
        //
        // Abrindo o Bloco de Notas
        //
        if(CreateProcess( NULL,   // Sem módulo 
            "C:\\WINDOWS\\notepad.exe", // linha de comando para o executável 
            NULL,
            NULL,
            FALSE,            // O processo criado não herdará os handles
            0,
            NULL,
            NULL,
            &si,              // Ponteiro para STARTUPINFO
            &p ) == FALSE) // Ponteiro para PROCESS_INFORMATION  
        {
            printf( "Erro em CreateProcess: 0x%X", GetLastError() );
            return;
        }
     
        //
        // O CreateProcess preencheu as estruturas e colocou handles nelas
        // como não vamos usar os handles, precisamos fechá-los...
        //
        CloseHandle(p.hProcess);
        CloseHandle(p.hThread);
    }

    Sobre preencher o membro cb com o sizeof da estrutura, isso é usado para versionamento. Afinal, quando uma versão nova do Windows precisa de mais parâmetros para uma função, ao invés de criar uma nova versão (isso é C, não existe overload), mais membros são adicionados à estrutura, aumentando seu tamanho.

    Esse foi um pequeno trecho de código, mas de bom tamanho para quem está começando com Win32, e é bom para entender como as coisas funcionam. Além disso, pretendo manter os posts pequenos para facilitar a leitura de todos, afinal, eu não sou o único aqui que tem um software para fazer...

    Em 27/01/2008 04:38 - Comentários (5)

    chuck_norris* cn = NULL;

    http://lwn.net/Articles/262570/

    http://reddit.com/r/programming/info/675jj/comments/

    (comentários com piadas de chuck norris serão sumariamente eliminados)

    Em 31/01/2008 05:05 - Comentários (3)

    Win32: Mais sobre processos

    Uma coisa muito importante deve ser notada pelos (linux|unix|bsd) boys: não existe fork no Windows. (outra coisa muito importante deve ser notada pelos (chatos|acha-pelo-em-ovo) boys: sim, existe no Unix Subsystem, mas é preciso uma boa dose de drogas bem pesadas para chamar o Unix Subsystem de usável). Quando novos processo são criados, tudo é novo, não só o address space. É possível que um processo herde os handles do processo pai (veja a referência do CreateFile na MSDN), mas um compartilhamento de memória ou variáveis exige um esforço extra de implementação.

    Outra coisa importante é que, além de não existir fork, não existe a "cultura" fork e o Windows não ajuda. Um processo por usuário conectado ou por job/work não é usado e não recomendado. O custo da criação de um processo no Windows é bem mais alto do que um fork, porque um fork pode usar e abusar do recurso copy-on-write de memória. Quando um fork é executado no Linux, por exemplo, a maioria das páginas de memória física do processo atual e do novo são simplesmente compartilhadas. A mágica toda é um flag que marca a página de memória como read only. Quando um dos processos resultados do fork grava uma página, o processador dispara uma exceção, que faz com que o Linux crie uma página de memória exclusiva para o processo e faça a gravação. Ou seja, as páginas exclusivas só são criadas quando é realmente necessário. O Memory Manager do Windows também usa esse recurso, mas em outras ocasiões (como para as páginas de memória que contém o código executável dos arquivos .exe e .dll).

    Além de tudo isso, ainda tem o problema do Desktop Heap Size (mais informações em um Google perto de você). Depois de um certo ponto, você não consegue mais criar processos - em um teste que eu fiz não consegui criar mais do que 180 processos sem ajustar o Desktop Heap Size, que requer mexer no registro e um reboot. Esse (defeito|tradeoff|particularidade) é compensado pelo fato do conceito de threads ser nativo do Windows NT desde a primeira versão, ao contrário do mundo *nix. Ou seja, se você faz um fork por (usuário|job|work) no Unix, no Windows isso precisa ser adaptado para uma thread por (usuário|job|work). Só que aí você precisa sincronizar o acesso às variáveis, coisa que veremos depois...

    Em 08/02/2008 22:00 - Comentários (4)

    Algumas dicas simples para palestrantes

    Agora que os encontros de C++ se transformaram em algo mais profissional ao invés de encontros de boteco, vários escovadores de bit estão tendo a oportunidade de sair da frente do seu compilador predileto e compartilhar seu conhecimento com o mundo. Acho isso ótimo, pois ajuda a melhorar *muito* o panorama dos eventos com palestras no Brasil. Eu não pude comparecer ao primeiro evento com palestras, mas conheço (ou pessoalmente ou via nossa lista C & C++ Brasil) os palestrantes, e o nível de conhecimento deles é **absurdamente** alto. É difícil ver eventos com palestrantes desse nível, até mesmo lá na gringa.

    Fazer palestras não é algo muito complicado, mas falar para um público de mais de 50 pessoas deixa algumas pessoas nervosas. Quem me conhece pessoalmente sabe que não sou lá um cara muito tímido, mas eu sempre fico um pouco nervoso nos primeiros 10 minutos de palestra ou na primeira aula de uma turma. Não sou o super-palestrante, mas já ministrei algumas palestras e alguns cursos, e tenho umas dicas interessantes para quem nunca palestrou. Lá vai:

    • Saiba do que você está falando: Já existem inúmeros casos de palestrantes que não conhecem bem o assunto mas são bons oradores. Eu prefiro ser um orador mediano mas saber bem do que estou falando. Prefira falar sobre assuntos que são ligados ao seu dia-a-dia e que você tem experiência e um bom conhecimento para transmitir. O foco de fazer uma palestra é passar conhecimento para um grande número de pessoas em um curto período de tempo. Esse conhecimento deve ser útil e rapidamente aplicável, de preferência, algo que os espectadores possam testar ao chegar em casa.
    • Entretanto, você não precisa ser uma autoridade: É impossível saber tudo sobre um assunto. Sim, você pode fazer uma palestra sobre um assunto que o seu colega de trabalho conhece mais do você, mas você precisa saber o suficiente para responder perguntas de nível médio e ter idéia do que se trata as perguntas difíceis. É completamente normal alguém fazer uma pergunta que você não sabe responder. Diga que você precisa pensar com mais calma e que vocês podem conversar no final da palestra ou durante o coffee break.
    • Seja educado, mas firme com os chatos: Existem pessoas que adoram testar palestrantes e professores, não se pode fugir deles. Outras só gostam de fazer mais de 50 perguntas em uma palestra de uma hora. No caso de perguntas fora do assunto ou propositalmente muito complicadas, seja educado e amistoso, e diga que é melhor deixar para responder depois para não prejudicar o tempo da palestra. Não gaste muito tempo tentando responder perguntas complicadas, você penaliza toda a platéia por causa de uma pessoa. Pode ser uma pergunta de interesse geral, mas se impactar no tempo e no conteúdo da palestra, deixe para depois. É só convidar o "perguntante" para conversar depois.
    • Não esqueça sua garrafinha de água: Você estará falando durante mais de uma hora sem parar. O sintoma mais comum do nervosismo é boca seca, e isso pode facilmente chegar ao ponto de ficar difícil de falar.
    • Impressionar a platéia ajuda a acalmar: Prepare um pequeno truque ou um trecho de código propositalmente curto e complicado mas que faça algo interessante. Isso acrescenta, mostra que você sabe do que está falando e dá a sensação de controle da situação, o que te deixa mais calmo. Mas, é claro, não gaste mais do que 5 minutos com isso.
    • Não leia os slides, todo mundo sabe ler: Explique o slide e use-o como guia, mas é extremamente chato um palestrante que só lê os slides. Se é pra ler slides, eu prefiro fazer isso no conforto do meu lar.
    • Fale para platéia, não para seu notebook ou para o projetor: É comum ter dificuldade de olhar para algum lugar ou ficar olhando para a mesma pessoa durante a palestra toda. Você pode até ficar olhando para o extintor que fica atrás na platéia, mas olhe na direção da platéia. Para melhorar, mude a direção do olhar para os cantos da platéia algumas vezes, isso ajuda a dar a impressão que você está realmente falando com todo mundo.
    • Ensaie em casa: Dar uma ensaiada em casa ajuda bastante a pegar o tempo da palesta. Ensaie e teste principalmente as demos, que é o que mais pode dar errado. Teste todas as conexões de banco dados, servidores Web, etc. Deixe tudo pronto e configurado para não perder tempo durante a palesta. Experimente deixar para configurar um listener de banco de dados ou um Virtual PC durante uma palestra e você vai se arrepender pelo resto da vida...
    • Separe os slides essenciais dos "se sobrar tempo eu falo": É dífícil calcular o tempo necessário para uma palestra. Defina os tópicos que você gostaria de falar e enumere-os em ordem de importância e dependência (você não pode falar de sockets antes de falar sobre TCP/IP, por exemplo). Faça mais slides do que você acha necessário e coloque-os depois do slide "The End" para falar se sobrar tempo e valer a pena. Não é problema algum acabar a palestra antes do tempo, o chato é ficar pulando slides ou falando superficialmente para "caber" no horário. E evite inventar coisas na hora para preencher o tempo, geralmente não funciona, todo mundo percebe que você está enchendo lingüiça...
    • Não esqueça a "cola", escrever código na hora é armadilha: É bonito escrever código na hora, mas em uma palestra você não pode gastar 20 minutos debugando até achar que esqueceu de inicializar uma variável. Imagine 20 minutos de debug com 100 pessoas olhando e algumas dando palpite... Quantas vezes na sua vida inteira você escreveu um programa inteiro - mesmo que pequeno - sem erros de compilação e onde tudo funcionou de primeira? Percebeu quão estúpido é fazer isso? Se possível, decore o código que você vai escrever, mas mesmo assim tenha um arquivinho txt com todos os fontes que você vai precisar. Se alguma coisa der errado, é só olhar a cola. É mais imporante fazer a palesta fluir de forma agradável do que provar que você é o programador foderoso que programa rápido e sem errar na frente de dezenas de pessoas.
    • Cuidado com produtos beta, alfa ou coisas assim: Essa vai direto para os Microsoft boys. Eu já perdi a conta das vezes que assisti alguém falar "ops..." depois do Visual Studio/SQL/Exchange simplesmente travar, e depois o cara levar mais de 10 minutos pra arrumar (quando ele não desiste). Além disso, se o notebook que você está usando tem pouca RAM, ficar abrindo um monte de VMs vai tornar a palestra *muito* chata, porque cada clique que você der vai levar minutos...
    • Mais conteúdo e menos gracinhas: Contas piadas é legal, ajuda a descontrair, te faz parece um cara legal e tudo mais. Mas não se esqueça, é uma palestra técnica, não um circo.

    Em 15/02/2008 19:56 - Comentários (9)

    Programando caças a jato em C++
    http://www.research.att.com/~bs/JSF-AV-rules.pdf?

    Em 19/02/2008 06:07 - Comentários (1)

    Win32: Unicode e Ansi

    Procurando a declaração da função MessageBox (um F12 sobre o nome da função resolve isso no Visual Studio), encontramos isso:

    
    WINUSERAPI
    int
    WINAPI
    MessageBoxA(
        __in_opt HWND hWnd,
        __in_opt LPCSTR lpText,
        __in_opt LPCSTR lpCaption,
        __in UINT uType);
    WINUSERAPI
    int
    WINAPI
    MessageBoxW(
        __in_opt HWND hWnd,
        __in_opt LPCWSTR lpText,
        __in_opt LPCWSTR lpCaption,
        __in UINT uType);
    #ifdef UNICODE
    #define MessageBox  MessageBoxW
    
    #else
    #define MessageBox  MessageBoxA
    #endif // !UNICODE
    
    

    Podemos notar que não existe função MessageBox, e sim um #define MessageBox, que aponta para a função com o sufixo A ou W. A letra A é adicionada no nome das funções que suportam caracteres ANSI, e a letra W nas funções que aceitam caracteres UNICODE (o W vem de “wide char”). Como estamos usando Windows NT, a função correta é a que tem W no final.

    Os caracteres UNICODE ocupam 2 bytes, o que permite que um caractere UNICODE possa representar até 65536 letras ou símbolos diferentes (em oposição ao ANSI/ASCII, que suporta somente 255), o que permite conter confortavelmente todos os caracteres e símbolos de todas as línguas e dialetos existentes no nosso planeta. Isso acaba com aquela história de MODE CON CODEPAGE PREPARE (lembra?) para ficarmos mudando a página de caracteres dependendo do idioma escolhido. Para declarar strings UNICODE em C++ precisamos prefixá-la com um L (L"dessa forma";). Quando o Windows NT foi projetado (nos ido de 1989), foi feita a decisão pelo UNICODE, para facilitar a internacionalização do Windows e dos softwares que nele rodam. O mesmo não aconteceu com o Windows 95/98/Me, que herda muita coisa do Windows 3.1, baseado em ANSI.

    Na hora de programar em Win32, o #define UNICODE é o que vai definir ser vamos usar ANSI ou Unicode. As duas versões funcionam no Windows NT, sendo que o MessageBoxA (assim como todas as APIs ANSI) converte as strings de entrada para UNICODE, chama a função MessageBoxW, e converte as strings de saída de UNICODE para ANSI. Você pode inclusive chamar diretamente uma ou outra versão, como no exemplo abaixo:

    
    #include "stdafx.h"
     
    int main()
    {
    	//
    	// ANSI
    	//
    	MessageBoxA(NULL, "mensagem ANSI", "www.1bit.com.br", MB_OK | MB_ICONEXCLAMATION);
    
     
    	//
    	// UNICODE
    	//
    	MessageBoxW(NULL, L"mensagem UNICODE", L"www.1bit.com.br", MB_OK | MB_ICONQUESTION);
     
     
    	//
    	// TCHAR: a macro _T() automaticamente coloca o prefixo L caso
    	// estejamos compilando com UNICODE. Dessa forma é possível compilar
    	// o mesmo código para UNICODE ou ANSI. Apesar de estar entrando em
    	// desuso hoje em dia, isso era importante na era dos Windows 9x.
    	//
    	MessageBox(NULL, _T("mensagem em TCHAR"), _T("www.1bit.com.br"), MB_OK | MB_ICONERROR);
    }
       
    

    Em 04/03/2008 22:22 - Comentários (6)

    Quarto Encontro de Programadores C++

    No próximo sábado, dia 29 de março, os escovadores de bits estarão reunidos para o "4º Encontro de Programadores C e C++", que acontecerá em São Paulo. Eu farei uma palestra sobre programação TCP/IP usando Boost.Asio, e ainda teremos palestras sobre Qt, programação de drivers para Windows e programação para microcontroladores.

    O evento durará o dia todo, e custa a bagatela de R$ 15,00 se você pagar até quarta-feira. Faça sua inscrição no site da Tempo Real Eventos, e veja mais informações no wiki da nossa comunidade C++.

    Em 24/03/2008 22:45 - Comentários (2)

    Resultado do Quarto Encontro de Programador C & C++

    Mais um encontro de programadores C++ foi realizado, outra ótima oportunidade para trocar idéias e conhecer outros profissionais e outras visões. Como meu amigo Wanderley Caloni fez um reporte detalhado, vou me abster.

    Aqui estão a apresentação e o vídeo da minha palestra sobre Boost.Asio:

         

    Gostaria também de deixar registrado meu sincero agradecimento ao nosso amigo Alberto Fabiano, que foi o membro da comunidade responsável por fazer com que esse encontro acontecesse. Foi ele quem fez todos acordarem para o fato de que um evento de verdade, com palestra, comida de graça e sorteio de brindes é possível mesmo para um banco de escovadores de bits como nós.

    Em 08/04/2008 05:50 - Comentários (7)

    Win32: threads

    Uma thread é basicamente uma linha de execução independente, contida dentro de um processo. Ou seja, uma thread permite que um processo "faça varias coisas de forma simultânea", já que um processo pode conter várias threads. Todas as threads que fazem parte de um mesmo processo compartilham vários recursos, como o espaçamento de memória e os handles. Ou seja, elas acessam as mesmas variávies e podem usar os mesmos handles ao mesmo tempo.

    Quando um processo é criado no Windows, é criado junto com ele a thread principal, que é thread que roda a função main ou WinMain. A partir daí é possível criar novas threads, como no exemplo abaixo:

    #include "stdafx.h"
    #include <windows.h>
    #include <iostream>
     
     
    using namespace std;
    
     
    //
    // WINAPI é um #define para __stdcall
    //
    DWORD WINAPI ThreadProc(void* lpv)
    {
      //
      // vamos esperar até 500 ms com o nosso random() de pobre
      //
      Sleep(GetTickCount() % 500);
      return 0;
    }
     
    int main()
    {
      DWORD dwThreadID;
      static const DWORD THREAD_COUNT = 20;
      HANDLE hThreads[THREAD_COUNT];
     
      //
      // vamos criar nossas threads
      //
      for(DWORD a = 0 ; a < THREAD_COUNT ; a++)
      {
        hThreads[a] = 
          CreateThread(NULL, // segurança, vamos deixar o default
          NULL,        // tamanho da pilha. O default é 1MB
          &ThreadProc,     // função da thread
          NULL,        // parâmetro da thread
          NULL,        // flag de criação. Podemos usá-lo para criar um thread suspensa
          &dwThreadID);
     
        //
        // O Sleep(0) diz ao scheduler do Windows que ele
        // pode agendar a próxima thread em espera, e nós
        // vamos para o fim da fila. 
        //
        Sleep(0);
      }
     
      //
      // coloque um breakpoint aqui, vá no menu Debug >> Windows >> Threads
      // e veja as threads que criamos na lista
      //
      __asm nop; // instrução x86 que não faz absolutamente nada
     
      //
      // Para esperar uma thread parar (o que os linux boys conhecem como 'join')
      // usamos WaitForSingleObject ou WaitForMultipleObjects. Essas funções
      // funcionam com handles de processos, threads, mutexes e outras coisas mais
      // que veremos depois
      //
      DWORD dw = WaitForSingleObject(
        hThreads[0], // handle
        100); // vamos esperar 100 ms
     
      if(dw == WAIT_TIMEOUT)
      {
        //
        // não retornou em 100ms... Vamos esperar para sempre
        //
        dw = WaitForSingleObject(hThreads[0], INFINITE);
      }
      else if(dw == WAIT_OBJECT_0)
      {
        //
        // esperamos menos de 100 ms
        //
      }
     
      //
      // agora vamos ver qual thread para primeiro
      //
     
      dw = WaitForMultipleObjects(
        THREAD_COUNT - 1, // quantos handles na array
        hThreads + 1,     // array de threads + 1, porque a thread 0 já acabou
        FALSE,            // não quero esperar por todas, se algum thread acabar está bom
        INFINITE);        // senta e espera
     
      DWORD qualThread = dw - WAIT_OBJECT_0;
     
      cout << "a thread mais rápida foi a " << qualThread << endl;
     
      //
      // agora vamos esperar todas de uma vez --------------|
      //                                                    v
      dw = WaitForMultipleObjects(THREAD_COUNT, hThreads, TRUE, INFINITE);
     
      //
      // Como a API é C, vamos fechar os handles. Lembrando que
      // o Windows faz isso automaticamente quando o processo acaba
      //
      for(DWORD a = 0 ; a < THREAD_COUNT ; a++)
        CloseHandle(hThreads[a]);
     
      return 0;
    }

    A única diferença entre as threads criadas posteriormente e a thread principal de um processo é que quando a thread principal acaba, todas as outras threads são forçosamente terminadas e o processo acaba - caso contrário uma thread esquecida poderia deixar o processo em um estado de limbo. Em um computador rodando Windows, em uma determinada hora, temos várias threads rodando, e a thread é única entidade que faz uso do processador. No Windows, um processo não "roda" nem é executado. O que "roda" é uma thread (que por sua vez pertence à um processo). Dessa forma, o scheduler (agendador) do Windows trabalha compartilhando e dividindo o uso do processador entre as threads do sistema, independente do processo que contém a thread.

    Em uma de suas atuações, o scheduler do Windows (e de qualquer SO) funciona basicamente respondendo à uma interrupção de timer que acontece em intervalos de milissegundos. Quando essa interrupção ocorre, a thread atual é interrompida, e o scheduler toma seu lugar no processador para verificar se é necessário trocar de thread. Caso o quantum (tempo de cpu determinado para que a thread rode antes de outra thread assumir o processador) da thread atual tenha terminado, o scheduler salva os registradores que definem o estado atual da thread no thread context (veja a estrutura _CONTEXT no winnt.h) e carrega os registradores da próxima thread a ser executada. Com a nova thread pronta para utilizar o processador, o scheduler coloca a thread atual no fim da fila. Falando em código, o ponteiro da estrutura ETHREAD que representa a thread é colocada no fim de uma lista duplamente ligada que controla a fila de threads em estado ready.

    Quando só existe um processador ou core no computador, o scheduler dá a impressão de que as threads estão sendo executadas simultaneamente, alternando entre elas dessa forma. Quando mais de um processador ou core estão disponíves, várias threads podem ser executadas simultaneamente, o que faz com que o scheduler precise distribuir as threads entre os N processadores disponíveis.

    O tempo de interrupção citado é de 20 à 120ms, dependendo do processador e da versão do Windows. Nas versões Home, Professional e Business, os intervalos são de 20ms, para que o sistema seja mais responsivo ao usuário interativo. O intervalo de 120ms é usado nas versões Server, pois quanto maior o quantum, maior a probabilidade da thread conseguir atender a requisição em um quantum só, aumentando a vazão do servidor.

    Outra situação onde o scheduler aparece é quando uma thread deve esperar uma operação de I/O. Nesse caso a thread perde o controle do processador antes que seu quantum acabe, e ela só será colocada novamente na lista de threads ready (prontas para serem executadas) quando essa requisição de I/O for respondida.

    Como sempre, mais informações podem ser encontradas na MSDN (sobre as funções CreateThread e WaitForXXX), Wikipedia (sobre conceitos do sistemas operacionais, como quantum e thread) e no livro "Windows Internals" (sobre a implementação dos conceitos no kernel do Windows).

    Em 15/04/2008 20:46 - Comentários (4)

    Win32: Handles

    Como visto nos artigos anteriores, tudo no Windows é controlado por handles. Arquivos, sockets, objetos de sincronização, etc. O tipo HANDLE é na verdade um void* opaco, e somente o Windows sabe o que tem lá dentro (diga "veja mamãe, sem os fontes do sistema operacional!"). A única coisa a saber é: quem abre o HANDLE é o responsáve por fechá-lo usando a função CloseHandle. Fechar um handle mais de uma vez é um bug não-muito-grave, já que você não vai ganhar um GPF ou algo assim. Mas um GetLastError() retornará um ERROR_INVALID_HANDLE.

    Acho que eu já disse isso algumas vezes, mas vou repetir. Todos os handles são fechados automaticamente quando o processo termina. É *impossível* que um programa termine e deixe um arquivo aberto, por exemplo. Impossível.

    Dentro do kernel do Windows, todo processo tem uma tabela de handles referenciada pela estrutura de controle do processo, EPROCESS. Apesar do tipo do handle ser void*, ele é na verdade um offset para a tabela de handles, onde fica um ponteiro para o OBJECT_HEADER dentro do kernel. É tentador acessar a tabela para ver as informações sobre o objeto, mas isso não é possível... Como os objetos são gerenciados pelo Object Manager em kernel mode, os ponteiros são todos para a memória do kernel (endereço maior que 0x80000000). Se você quiser enumerar e ver os handles, terá que fazer um driver ou usar o Process Explorer:

    Em 24/04/2008 22:56 - Comentários (5)

    A volta dos que foram mas voltaram logo

    Algumas notícias importantes para movimentar meu quase abandonado site enquanto eu fico pensando em uma boa desculpa para essa situação:

    • Voltei para São Paulo, e conseqüentemente, estou quase toda terça degustando um chopp no Bar Barão. Sim, isso é um convite. Para todos.
    • O seminário da Tempo Real sobre Portabilidade e Performance foi muito bom, muito bom mesmo. Parabéns a todos nós do "C/C++ Brasil" por fazermos eventos com qualidade técnica tão alta, coisa que eu nunca vi nessas bandas. Fico feliz por poder compartilhar o que eu aprendi em incontáveis horas programando, dezenas de livros e outras dezenas de sites e artigos lidos. E fico muito mais feliz em poder aprender com pessoas que percorreram esse mesmo caminho, muitos que estudaram e leram muito mais do que eu. Eu sei que sou bom nesse negócio de programação (modéstia é um defeito que eu não tenho), mas minha vontade de aprender ainda é bem maior que meu ego. É **muito** bom conhecer e compartilhar conhecimento com gente que é melhor do que eu. Parabéns e obrigado a todos.
    • Depois de mais de um mês offline (sim, essa é a desculpa), segue aqui a apresentação usada na minha palestra "Portabilidade via STL e Boost", onde eu fiz pelo STL e Boost o que os palestrantes da Microsoft/Sun/IBM/etc costumam fazer pelas empresas que pagam seus salários:



    Como um trecho de código vale mais do que uma apresentação cheia de piadinhas não-tão-engraçadas, segue o dito cujo que usei durante a demo da palestra:

    
    #define _CRT_SECURE_NO_WARNINGS
    #define _SCL_SECURE_NO_WARNINGS
     
    #include <vector>
    #include <string>
    
    #include <sstream>
     
    #include <iostream>
     
     
    #include <boost/thread.hpp>
    #include <boost/bind.hpp>
    #include <boost/asio.hpp>
    #include <boost/function.hpp>
    #include <boost/array.hpp>
    #include <boost/lexical_cast.hpp>
    #include <boost/shared_array.hpp>
    #include <boost/enable_shared_from_this.hpp>
    #include <boost/ptr_container/ptr_map.hpp>
    #include <boost/ptr_container/ptr_set.hpp>
    #include <boost/algorithm/string.hpp>
    #include <boost/algorithm/string/join.hpp>
    #include <boost/format.hpp>
    #include <boost/date_time/gregorian/gregorian.hpp>
     
    #include <boost/program_options.hpp>
     
    #include <boost/tuple/tuple.hpp>
     
    #include <boost/foreach.hpp>
     
    #include "boost/filesystem.hpp" 
    #include <iostream>          
     
    using namespace std;
    using boost::lexical_cast;
    using boost::format;
    using boost::shared_ptr;
     
    boost::mutex log_mutex;
     
    void Log(int level, const string& str)
    {
    	boost::mutex::scoped_lock l(log_mutex);
     
    	cout << 
    		boost::format("%d - %s") % level % str
    		<< endl;
    }
     
     
    void f(int level, string s1, string s2)
    {
    	Log(level, s1 + string(" ") + s2);
    }
     
    typedef vector<shared_ptr<boost::thread> > UmVectorDeThreads;
     
    UmVectorDeThreads Test_Threads()
    {
    #if 0
    	boost::array<boost::thread, 32> threads;
    	threads.at(2) = boost::thread(boost::bind(&f, "Eu programa aos sábados.", "E daí"));
    
    #endif
     
    	boost::array< shared_ptr<boost::thread>, 32> threads;
     
    	for(unsigned int i = 0 ; i < threads.size() ; i++)
    	{
    		threads[i] = shared_ptr<boost::thread>(
    			new boost::thread(
    			boost::bind(&f, 2, "Eu programo aos sabados.", 
    			lexical_cast<string>(i))));
    	}
     
    	//
    	// ai...
    	//
    	for(boost::array< shared_ptr<boost::thread>, 32>::iterator i 
    		= threads.begin() ; i != threads.end() ; ++i)
    	{
     
    	}
     
    	BOOST_FOREACH(shared_ptr<boost::thread> t, threads)
    	{
     
    	}
     
    	boost::thread t;
     
     
    	UmVectorDeThreads threads2(threads.size());
     
    	std::copy(threads.begin(), threads.end(), threads2.begin());
     
    	return threads2;
    }
     
    void VamosEsperarAsThreads(UmVectorDeThreads& threads)
    {
    	BOOST_FOREACH(shared_ptr<boost::thread> t, threads)
    	{
    		t->join();
    	}
    }
     
    void Test_FileSystem()
    {
    	using namespace boost::filesystem;          
    	path p("c:\\temp\\abobrinha");
     
    	for(path::iterator i = p.begin() ; i != p.end() ; ++i)
    		cout << *i << endl;
    }
     
     
    void Test_STL()
    {
    	vector<string> v;
    	list<string> l;
    	map<int, string> m;
     
    	for(int a = 0 ; a < 20 ; a++)
    	{
    		string nome = "Nome " + lexical_cast<string>(a);
    		v.push_back(nome);
    		m[a] = nome;
    	}
     
    	copy(v.begin(), v.end(), back_inserter(l));
     
    
    	BOOST_FOREACH(const string& nome, v)
    		cout << nome << endl;
     
    	for_each(l.begin(), l.end(), boost::bind(&Log, 1, _1));
    }
     
    int main()
    {
    	UmVectorDeThreads threads = Test_Threads();
     
    	Test_STL();
    	Test_FileSystem();
     
    	for_each(threads.begin(), threads.end(), 
    		boost::bind(&boost::thread::join, _1));
     
    	//
    	// olha mamãe, sem desalocar memória
    	//
    	return 0;
    }

    Em 21/06/2008 21:42 - Comentários (7)

    Win32: sincronização

    Em um post do passado eu falei sobre o maravilhoso mundo das threads e de todos problemas que as threads resolvem. Agora é a hora de começarmos a ver os inúmeros problemas que as threads criam. Pensou que ia ser fácil? Pois é, já começou errado...

    Como um trecho de código vale mais que e = m * pow(c, 2);, vamos começar com um exemplo clássico de problema que acontece quando temos várias threads compartilhando um recurso:

     
     
    #include "stdafx.h"
     
    /*
    conteúdo do stdafx:
     
    #ifndef _WIN32_WINNT		
    #define _WIN32_WINNT 0x0501
    #endif						
     
    #include <Windows.h>
     
    #include <stdio.h>
    #include <tchar.h>
     
    #include <iostream>
    #include <vector>
     
    #include <assert.h>
    #define ASSERT assert
    */
     
    using std::cout;
    using std::endl;
    using std::vector;
     
    //
    // classe simples (e incompleta) que implementa uma lista
    // duplamente ligada. Se você disse "hã?", leia o artigo
    // sobre "Linked list" da wikipedia.
    //
    template<typename T>
    class LinkedList
    {
      struct NODE
      {
        NODE* previous;
        NODE* next;
        T data;
      };
     
      NODE rootNode_;
      unsigned int count_;
     
    public:
     
      LinkedList()
      {
        count_ = 0;
        rootNode_.previous = rootNode_.next = NULL;
      }
     
      void AddAfter(NODE* node, T data)
      {
        NODE* newNode = new NODE();
     
        //
        // primeiro item?
        //
        if(node->next == NULL)
        {
          ASSERT(node->previous == NULL && node == &rootNode_);
          node->next = node->previous = node;
          node->data = data;
          return;
        }
        else
        {
          //
          // refaz todos os links, para que o nó inserido
          // fique entre o nó passado como parâmetro
          // e o próximo
          //
          newNode->next = node->next;
          node->next = newNode;
          newNode->previous = node;
          newNode->next->previous = newNode;
     
          newNode->data = data;
        }
     
        count_++;
      }
     
      void Dump()
      {
        cout << "== DUMP START ==" << endl;
     
        if(rootNode_.next == NULL)
        {
          ASSERT(rootNode_.previous == NULL);
          cout << "== DUMP END ==" << endl;
          return;
        }
     
        cout << rootNode_.data << endl;
    
     
        for(NODE* node = rootNode_.next ; node != &rootNode_ ; node = node->next)
          cout << node->data << endl;
     
        cout << "== DUMP END ==" << endl;
      }
    
     
      void Add(T data)
      {
        AddAfter(&rootNode_, data);
      }
     
      unsigned int Count()
      {
        if(rootNode_.next == NULL)
        {
          ASSERT(rootNode_.previous == NULL);
          return 0;
        }
    
     
        unsigned int count = 1;
     
        for(NODE* node = rootNode_.next ; node != &rootNode_ ; node = node->next)
          count++;
     
        return count;
      }
    
     
    };
     
    //
    // estrutura para informar a nossa worker thread o que ela
    // deve fazer
    //
    struct WORKER_THREAD_INFO 
    {
      LinkedList<int>* list;
      unsigned int addCount;
    };
    
     
    //
    // nossa "linha trabalhadora" (aposto que algum livro traduzido
    // deve usar esse termo, aposto). Ela adicionará itens na 
    // nossa lista ligada
    //
    DWORD WINAPI WorkerThread(void* lpv)
    {
      WORKER_THREAD_INFO* info = (WORKER_THREAD_INFO*)lpv;
     
      for(DWORD a = 0 ; a < info->addCount; a++)
      {
        info->list->Add(a);
     
        //
        // dormir é uma boa forma de gastar um tempo
        // e simular trabalho. Pena que a Win32 API
        // não tem um função SleepInTheBathroom();
        //
        Sleep(10);
      }
     
      return 0;
    }
     
    int main()
    {
      LinkedList<int> list;
    
     
      static const DWORD threadCount = 100;
      DWORD dwThreadID;
      vector<HANDLE> threads;
     
      //
      // todas as threads vão adicionar a mesma quantidade,
      // para fins de teste e demonstração está mais que bom.
      //
      WORKER_THREAD_INFO info;
      info.list = &list;
      info.addCount = 100;
     
      //
      // criando todas as threads
      //
      for(DWORD a = 0 ; a < threadCount ; a++)
        threads.push_back(CreateThread(NULL, NULL, &WorkerThread, &info, NULL, &dwThreadID));
     
      //
      // esperando TODAS as threads retornarem
      //             |___________________________________
      //                                                 |
      //                                                 v
      //
      WaitForMultipleObjects(threadCount, &threads[0], TRUE, INFINITE);
     
    
      list.Dump();
     
      //
      // isso deve mostrar 10.000.
      //
      cout << "count: " << list.Count();
     
    
      //
      // vou deixar os handles abertos, como o programa vai 
      // morrer agora o Windows se vira com isso...
      //
     
      return 0;
    }

    Bom, 100 threads criando 100 registros na nossa lista ligada devem criar 10.000 registros, certo? Faça alguns testes: rode primeiro em Debug. Depois rode em Release. Depois brinque com o número do Sleep. Depois repita os testes em diferentes máquinas, com diferentes clocks e quantidades de cores. Olhe algumas saídas que eu obtive durante os testes, usando um Core 2 Duo (2 cores):

    == Versão Debug ==
    count: 92
    count: 99
    count: 98
    count: 99
    == Versão Release ==
    count: 40
    count: 99
    count: 97
    

    Ué, não deveria ser 10.000? Mudando o tempo do Sleep e a quantidades de threads eu consegui outros números, completamente diferentes. Isso é o chamado race condition, um problema ou bug que depende de timming e coisas fora do seu controle, um problema típico de programação multithread. Como a manifestação do bug depende de diversos fatores fora do seu controle - velocidade do processador, carga de processamento da máquina, etc - esse tipo de problema é sempre muito difícil de reproduzir e debugar.

    O principal problema desse código está no trecho de código que adiciona um item na lista:

          newNode->next = node->next;
          node->next = newNode;
          newNode->previous = node;
          newNode->next->previous = newNode;
          newNode->data = data;
    

    Esse trecho de código deve ser executado de forma atômica para que a lista ligada continue válida, com todos os ponteiros apontando para seus próximos e anteriores de forma correta. Depois da execução da segunda linha, o estado da lista permanece inválido até que a última linha desse trecho seja executada. Como uma thread pode ser interropida a qualquer hora (inclusive entre essas linhas), a manipulação dos ponteiros é feitas de forma incorreta e desordenada. É grande a probabilidade (isso depende do scheduler) de que a última thread que estava "mexendo" na lista a tenha deixado em um estado inválido. Isso pode gerar um erro de lógica (nosso caso) ou um GPF difícil de achar, que cada hora acontece em um lugar do código.

    No próximo post começaremos a ver os recursos da API do Windows para resolver esse problema e permitir que várias threads manipulem um mesmo recurso ao mesmo tempo.

    Em 13/07/2008 06:49 - Comentários (1)

    Todos os fontes do meu site pelo Google Code

    Simplesmente muito legal mesmo:

    Putz!

    Em 15/07/2008 03:38 - Comentários (0)

    Meus bugs fazem as pessoas perderem bastante dinheiro. Ainda bem.

    http://en.wikipedia.org/wiki/Therac-25

    Em 22/07/2008 03:14 - Comentários (0)

    Desafios aos Caçadores de Bugs

    Enquanto escrevia a parte 2 sobre sincronização, encontrei um bug na utilização das funções Win32 na parte 1. Qual é o bug?

    Em 13/09/2008 06:08 - Comentários (6)

    E o bug é....

    O bug está no uso incorreto da função WaitForMultipleObjects:

    DWORD WINAPI WaitForMultipleObjects(
      __in  DWORD nCount,
      __in  const HANDLE *lpHandles,
      __in  BOOL bWaitAll,
      __in  DWORD dwMilliseconds
    );
    

    Parameters:

    nCount [in]:The number of object handles in the array pointed to by lpHandles. The maximum number of object handles is MAXIMUM_WAIT_OBJECTS.

    Vendo o valor de MAXIMUM_WAIT_OBJECTS em winnt.h:

    #define MAXIMUM_WAIT_OBJECTS 64     // Maximum number of wait objects
    

    E no meu post:

    
     static const DWORD threadCount = 100;
    
    ...
    
    
    //
    // esperando TODAS as threads retornarem
    //             |___________________________________
    //                                                 |
    //                                                 v
    //
    WaitForMultipleObjects(threadCount, &threads[0], TRUE, INFINITE);
    
    
    

    Não podemos esperar 100 threads de uma vez só. Agora sabemos que o programa não espera todas as threads terminarem antes de mostrar o resultado.

    Em 24/09/2008 01:23 - Comentários (1)

    Palestra do Quinto Encontro: Process Explorer

    Aos que não acompanham a lista de discussão e o site do nosso glorioso http://www.ccppbrasil.org, foi realizado no sábado passado o Quinto Encontro de Programadores C e C++, no espaço cedido pela Microsoft Brasil. Para fazer as honras ao anfitrião e saciar minha vontade de falar sobre os softwares que me salvam a pele todos os dias, falei sobre as ferramentas de apoio à programação, debug e diagnóstico na plataforma Windows.

    Como uma palestra de 1:15hs (um pouco menos se consideramos o problema no som...) não é nem de longe suficiente para explicar todas as ferramentas, vou explicá-las melhor aqui no meu recentemente abandonado blog. Aos que esperam o prosseguimento da série de Win32, vamos esperar a Apple devolver meu carregador que foi para garantia e nunca mais voltou, assim eu consigo recuperar o fonte dos próximos posts. Bom chega de choramingar, daqui a pouco vou começar os posts com "meu querido diário"...

    A primeira ferramenta que eu mostrei, e de longe a mais útil é o Process Explorer, o Task Manager vitaminado da Sysinternals:

    Principais usos:

    • Visualizar processos em árvore: Isso possibilita saber facilmente qual processo iniciou outro processo. CTRL+T
    • Finalizar uma árvore de processos: Muitos programas (setups geralmentes) criam vários processos filhos que fazem o mesmo, ficando difícil finalizar todos os processos criados. Um SHIFT+DEL no processo pai resolve esse problema, matando sem remorso o processo pai e toda sua família.
    • Mostrar indicadores detalhados de memória: A pergunta "quanto de memória esse processo está usando?" é vaga e subjetiva. Clicando com o botão direito no header da lista com os processos aparece a opção "Select Columns...". Na aba "Process Memory" encontramos os indicadores detalhados de memória, como "Private Bytes" e "Working Set". Mais detalhes sobre isso no material da minha palestra "Por Dentro do Windows: Gerenciamento de Memória" (que eu já ministrei 3 vezes e se me convidarem eu faço de novo, gosto bastante desse assunto)
    • Saber qual processo está mantendo um determinado arquivo aberto: CTRL+F, digite o nome do arquivo. Você verá uma lista dos processos que mantém handles abertos para esse arquivo.
    • Visualizar quais conexões TCP/IP o processo mantém: Duplo clique no processo, depois aba TCP/IP.
    • Resumo do status do sistema (consumo de processador, memória, I/O, etc): CTRL+I ou menu View >> System Information. Apesar de parecer com a janela do Task Manager, nessa janela temos informações também sobre o I/O total do sistema, além de mais um monte de coisas.
    • Descobrindo qual processo criou uma determinada janela: Arraste o último botão da barra de ferramentas (um alvo) para cima da janela. O processo dono será selecionado. Muito útil para descobrir qual dos programas inúteis do fabricante do seu notebook estão ativando aqueles insuportáveis popups. Ou qual das 15 instâncias do seu programa deve ser debugada depois de mostrar um MessageBox(NULL, "Passou por aqui!!!", "DEBUG", MB_OK);.
    • Descobrir quais serviços rodam em uma instância do svchost.exe: Muitos serviços nativos do Windows são implementados como DLLs que rodam dentro de um dos inúmeros processos do svchost.exe. Abra as propriedades de um processo (duplo clique ou botão direito e "Properties"), você verá uma aba "Services" com a lista.
    • Descobrir quais processo são do usuário XYZ: Na tela de seleção de colunas, existe uma coluna "User name" na aba "Process Information".
    • Lista de todos os handles (arquivos, sockets, chaves de registro mutexes, events, etc) de um processo: CTRL+H.
    • Listar DLLs carregadas por um processo: CTRL+D. Botão direiro + opção "Properties" na DLL e você vê o caminho completo da DLL, sua versão e o nome do fabricante/fornecedor.
    • Ver os processos que mais consomem CPU: Na lista de colunas, aba "Process Performance", escolha a coluna "CPU Time", que mostra o total de tempo de processador que o processo já consumiu desde seu início. Útil para quem quer economizar bateria do notebook. Ou descobrir qual dos 19 antispywares que seu usuário instalou estão monopolizando a CPU e não deixando muito espaço para sua aplicação (que o usuário gentilmente chama de "carroça").
    • Iniciar o Process Explorer minimizado no "tray" na inicialização do Windows: Crie um atalho do Explorer para o comando "procexp.exe /t" e coloque-o em Start Menu >> Programs >> Startup.

    Mais sugestões nos comentários serão bem vindas e muito úteis para quem ler esse post no futuro. E torçam para o deus/santo/padroeiro/entidade/orixá/sei-lá-o-que da organização (deve existir um) me ajude a separar mais tempo para escrever no blog, pois eu realmente gosto de fazer isso...

    Em 09/10/2008 05:55 - Comentários (5)

    Ferramentas de apoio Win32: psexec

    Achei melhor "desamarrar" esses posts de ferramentas da palestra do quinto encontro, pois assim eu posso escrever sobre mais ferramentas do que eu falei ou planejei falar.

    O psexec é mais uma ferramenta Sysinternals, e permite que você execute um processo remotamente sem fazer login interativo (no popular, "sentar na máquina"). A sintaxe é simples, algo como "psexec \\maquina-remota executavel.exe". O que é mais interessante é que o psexec redireciona a saída (stdout) do programa para sua máquina, assim como redireciona seu stdin para a máquina remota. Isso permite que você faça de qualquer máquina um servidor de telnet chamando "psexec \\servidor cmd.exe". Use sua criatividade para usar isso para diagnóstico, resolução de problemas em servidores, execução de um servidor para debug remoto, rodar scripts, etc. Ah, antes que você ache o mundo um lugar maravilhoso demais, devo lembrá-lo que você precisa ser administrador da máquina remota. Só espero não ter afetado o estado emocional de ninguém subitamente.

    Ele instala um serviço na máquina remota, e esse serviço cria os processos e redireciona o input/output:

    (você também consegue instalar serviços em uma máquina remota usando a API CreateService ou usando o comando sc do Windows).

    Uma coisa que deve ser notada é que o psexec executa o processo remoto com impersonate - simplificadamente, muda o token processo para um token do seu usuário - e o processo criado não tem acesso à rede. Para conseguir acesso à recursos remotos, use "psexec -u dominio\usuario \\nome-da-maquina-remota comando". Ele vai pedir para você digitar a senha e a enviará sem criptografia via um Named Pipe, que acaba indo via socket (para ver os Named Pipes do psexecsvc, use o Process Explorer. No post anterior eu expliquei como ver os handles de um processo).

    (Sim, é possível rodar "psexec \\maquina-remota wmplayer.exe fart.mp3" em uma máquina remota se você for admin. Mas eu não disse nada)

    Em 17/10/2008 06:19 - Comentários (3)

    Ferramentas de apoio Win32: pskill

    O pskill é mais um ferramenta Sysinternals sem a qual a vida fica muito difícil. A única coisa que ele faz - e bem feito - é matar um processo. Parece simples, mas quando você precisa matar 7 processos toda vez que seu servidor dá pau, uma bat cheia de chamadas ao pskill pode salvar a sua vida.

    Alguns usos:

    • Ela aceita tanto o nome do processo quanto o PID: você pode chamar pskill notepad.exe ou pskill 4576.
    • Quando você usa o nome, ele mata TODOS os processos com o mesmo nome: Ou seja, um pskill notepad.exe vai matar TODAS as instância do notepad. Se você quer matar uma instância específica, use o PID. Se precisar saber o PID do processo em command line, use o pslist, ferramenta que veremos em mais detalhes depois.
    • Matar um processo e todos criados por ele: pskill -t meu-servico.exe. Mata o processo pai e toda sua família.
    • Funciona remotamente: você pode chamar pskill \\outra-maquina notepad.exe ou pskill \\outra-maquina 4576. Como sempre, você precisa ter permissão para isso na máquina remota

    Informação muito importante: pskill \\maquina-remota winlogon.exe causa um BugCheck (tela azul) instantâneamente na máquina remota. Use com moderação.

    Em 31/10/2008 18:55 - Comentários (4)

    Win32: sincronização parte 2

    Preparando esse post eu percebi que o post de sincronização parte 1 não explica nada sobre sincronização, só apresenta o problema que a sincronização resolve. Estou parecendo aqueles vendedores de empresas de software que gastam mais tempo tentando provar que você tem um problema do que realmente mostrando a solução. Agora chegou a hora de mostrar a solução. :-)

    Como vimos na primeira parte, o maior problema em programação multithread é o compartilhamento de dados entre as threads. Existem duas solução para isso. A primeira e mais usada é sincronizar o acesso aos dados, de forma que somente uma thread por vez manipule os dados em questão. Ou seja, a solução envolve "eliminar" (notou as aspas?) o multithread da parte que ele atrapalha. A segunda e pouco usada opção é eliminar o compartilhamento dos dados, usando filas, controle restrito de ownership e trabalhando com cópias do dados. Esse é o enfoque usado pelo Erlang, uma nova linguagem de programação.

    A forma mais básica para sincronização em vários SOs é o critical section. Esse recurso permite definir um trecho de código que é executado por várias threads, mas que precisa ser sincronizado entre elas. Sincronizado == somente uma thread pode executar esse trecho de código por vez.

    Revisando a nossa classe de lista ligada, vamos ver quais são os dados da classe:

    template<typename T>
    
    class LinkedList
    {
      struct NODE
      {
        NODE* previous;
        NODE* next;
        T data;
      };
     
      NODE rootNode_;
      unsigned int count_;
    
     ...
    

    A princípio parece que precisamos sincronizar o rootNode_ e o count_. Apesar disso estar correto, não é simples assim, não esqueça que todos os nós apontados pelo rootNode_ são dados controlados pela classe, que também precisam ser sincronizados.

    Olhando o fonte dessa classe, todos os métodos manipulam ou lêem essas duas variáveis. A forma mais fácil e óbvia seria cercar com um critical section o corpo de todas as funções da classe.

    Em Win32, um critical section é definido pelo objeto (surpresa!) CRITICAL_SECTION. Esse objeto deve ser inicializado e terminado, dessa forma:

    CRITICAL_SECTION cs;
    
    //
    // adivinha?
    //
    InitializeCriticalSection(&cs);
    
    //
    // aqui entramos no critical section. Quando uma thread está "dentro" de
    // um critical section, se alguma outra thread tentar entrar ficará
    // travada dentro da funcão EnterCriticalSection até que a primeira
    // thread chame LeaveCriticalSection. Isso garante que somente uma thread
    // executará esse trecho de código em um dado momento.
    //
    EnterCriticalSection(&cs);
    
    //
    // Saímos do critical section. Isso faz com que a próxima thread que estiver
    // esperando no ponto do EnterCriticalSection seja liberada. Note que somente
    // uma thread é liberada por vez. Caso mais de uma thread esteja esperando, elas
    // são liberadas na ordem que chegaram "na portinha" do critical section
    //
    LeaveCriticalSection(&cs);
    
    //
    // análogo ao CloseHandle
    //
    DeleteCriticalSection(&cs);
    
    

    Resumindo: o Critical Section cria um "gargalo", onde só passa uma thread de cada vez. Ah, existe uma coisa interessante a ser acrescentada à explicação do post sobre threads: as threads permitem que um processo execute várias linhas de execução ao mesmo tempo, mas essas linhas de execução podem rodar o mesmo código, que está no mesmo lugar na memória. Ou seja, elas compartilham dados e código executável.

    No próximo post iremos para o exemplo prático.

    Em 30/01/2009 16:17 - Comentários (8)

    Lendo e medindo o tempo em C e C++

    Existem vários motivos para um programa consultar o "horário da máquina". Os mais comuns são registrar a data e hora de um acontecimento ou ação do usuário (data de acesso à um arquivo, por exemplo) ou medir o intervalo entre dois momentos para saber o tempo que algo levou.

    Em C e C++ existem diversas maneiras de ler uma representação numérica de um determinado instante no tempo, e cada uma tem seu uso e característica. Essa salada de funções e APIs existem porque usando C e/ou C++ você tem acesso à todas as camadas de abstração (ou não-abstração), desde o relógio da placa mãe, passando pela BIOS, pelas APIs do sistema operacional até as funções da runtime do C ou do boost::datetime. Nessa série de posts vamos ver as características de cada forma de ler o "horário da máquina".

    Toda representação numérica de um instante no tempo é dada pela diferença entre o instante da medição e um instante tido como referência. Quando usamos uma data e hora no nosso calendário gregoriano, tomamos como referência a "data tradicional da encarnação de Jesus Cristo". Isso é uma referência. 26 de maio de 1980 é tradução de (instance referência) + (1980 anos) + (5 meses) + (26 dias). Passando essa data para qualquer pessoa/programa que use a mesma refererência, ela consigirá saber de que instante no tempo você está falando. É importante saber esse conceito porque em programação a referência muitas vezes é diferente da referência do calendário gregoriano (por motivos técnicos e não religiosos. Sem teorias da conspiracão, pelamordedeus).

    Quando usamos uma data e hora "normal" (ou seja, do calendário), ainda temos outra variável na verificação do horário: o fuso e o horário de verão. O horário de Brasília (fuso de São Paulo, onde eu moro) é de -3 horas em relação ao horário GMT, que é tido como a referência mundial para os fusos (GMT é Greenwich Mean Time, horário de Greenwich na Inglaterra). O horário de verão não muda o fuso do país, mas adianta o horário em uma hora. Na prática, é como se São Paulo virasse GMT-2. O fuso é uma informação controlada pelo SO, e mudar o fuso pode causar mais problemas ainda. Os SOs tratam o horário de verão explicitamente. Quem cuida de sistemas de produção onde a data e hora é algo importante sabe o inferno que é quando começa ou quando termina o horário de verão.

    Saindo da história e voltando para a programação, existem alguns fatores importantes para escolher qual sistema de medição usar. Um fator importante é se o instante referência é fixo ou não (calendário gregoriano x quantidade de segundos desde que a máquina foi ligada). O outro é a precisão da medição, geralmente em segundos ou menos. Se você precisa medir o tempo que leva para concatenar uma string, medir em segundos não vai adiantar nada (isso geralmente leva nanosegundos).

    Resumindo o que precisamos saber para escolher que função ou API de tempo usar:

    • Referência: se é uma referência fixa (início do calendário gregoriano) ou uma referência relativa (hora de início da máquina ou de inicialização do sistema operacional)
    • Precisão do retorno. Não adiantar usar uma função que retorna a hora com precisão de segundos se vou fazer um cálculo de período que leva microsegundos.
    • Precisão de leitura. Existem funções que retornam em milisegundos, mas a precisão é menor que isso. A função GetTickCount da API Win32 retorna a quantidade de millisegundos desde o início do sistema, mas sempre em intervalos maiores do que 15ms. Ou seja, ele não consegue medir algo que leva menos do que 15ms
    • Performance. Algumas funções de leitura de hora são mais rápidas que outras. Se vocês faz isso dentro de um loop, isso faz diferença

    Por exemplo, uma função com precisão baixa pode ser usada para medir o tempo de abandono do meu blog, já que faz muuuito tempo que eu não atualizo isso aqui :-)

    O próximo post terá código. Não se preocupem, estou mais ansioso por isso do que vocês.

    Em 17/11/2009 23:32 - Comentários (4)

    Meu Twitter

    Também é sobre programação e afins (com poucos avisos úteis tipo "acabou a luz aqui, catso, perdi meus fontes #apagão"). Não espere saber se eu acordei de bom humor ou se o filme xpto é bom ou não.

    http://www.twitter.com/rodrigostrauss

    Em 18/11/2009 15:55 - Comentários (0)

    WebCast sobre DLR no .NET Framework 4.0

    Amanhã 12:00hs estarei apresentando o "MSDN Webcast: Trabalhando com o DLR no .NET Framework 4. Falarei da versão nova da DLR (Dynamic Language Runtime) que fará parte do .NET 4.0, de IronPython e sobre como integrar scripts Python no C#.

    Parece que ainda consta o nome do Alfred Myers como apresentador, mas eles devem arrumar isso logo

    Em 18/11/2009 23:04 - Comentários (1)

    A Liga dos Programadores Independentes

    Eu tenho certeza que não sou o único programador com ideias mirabolantes para dominar o mundo. Seja um projeto de software para ficar bilionário ou um simples serviço online para ajudar o ego (com Google Adwords para ajudar mais ainda o ego), já ouvi várias boas ideias de várias pessoas com quem trabalhei e convivi. O grande problema é que ninguém tem a organização e foco necessários para tocar um projeto desses. Eu também não, mas estou tentando chegar lá. E depois de aproximadamente 17 candidatos a sócio (sócio-programador, não o financeiro), eu desisti de tentar apoio nas minhas empreitadas. Sou agora um programador independente.

    Lendo o Twitter do Rafael Beckel vi que ele também estava com ideias para dominar o mundo, então liguei pra ele para marcarmos um chopp e conversar. Nenhum chopp depois (acabamos indo para o McDonalds) pensamos em montar um grupo de programadores malucos com ideias malucas, para termos com quem discutir nossas maluquices. Não é algo para discutir como montar um plano de negócio ou como conseguir dinheiro para financiar uma empresa. É para conversar sobre software. E sobre como manter o foco em um projeto quando você trabalha nele sozinho. E, claro, para ter pessoas para discutir suas ideias. O Rafael também escreveu sobre a conversa que tivemos.

    Como eu já disse, o foco é software. Pode ser indelicadeza da minha parte (sendo "indelicadeza" um puta de um eufemismo), mas pessoas que não sabem programar e não são da área de software não seriam bem vindas nesse suposto grupo (não queremos montar um sebrae). O foco é software, o publico alvo são pessoas que sabem fazer software. Com as próprias mãos. Pessoas que querem discutir organização e método para terminar seu software, já que não adianta ter um plano de negócio ou zilhares de pesquisas de sei-lá-o-que se você não tem software para o usuário interagir.

    Minha idéia é um primeiro encontro de buteco para discutirmos o formato do grupo. Dia 2 de dezembro, quarta-feira, no O'Malleys que fica perto da Paulista. Além de ser um lugar onde é possível conversar, a comanda é individual e não tem 10%. Quem não quiser me acompanhar me acompanhar no pint de Guinness pode tomar um refrigerante e não dividir a conta com os malditos programadores bêbados. Comentem sobre a data e hora e farei um outro post semana que vem confirmando o horário e local.

    Em 25/11/2009 13:22 - Comentários (27)

    Liga dos Programadores Independentes: primeira reunião

    Como temos quorum suficiente, está confirmada a primeira reunião para dia 2 de dezembro, quarta-feira, no O'Malleys, a partir das 19 horas. Com não sei exatamente quantas pessoas irão aparecer, vou reservar mesas para 10 pessoas. Como eu já tinha dito, lá as comandas são individuais e cada um paga sua própria conta. Chegando pergunte onde ficam as mesas e tenha certeza que um monte de programadores reunidos é algo bem fácil de achar.

    Só para esclarecer o debate do post anterior: você faz parte do público alvo do encontro se você é programador e tem uma idéia (implementada ou não) para fazer um software (produto ou serviço) para vender e ganhar dinheiro, não importa o motivo. Você pode estar puto com seu chefe e odiar trabalhar das 9 às 6 ou simplesmente ter vontade de abrir sua própria empresa. O encontro é para que programadores solitários e independentes tenham pessoas para discutir a viabilidade das suas idéias, e para discutir também métodos para colocá-las em prática. Para discutir métodos de desenvolvimento para transformar suas idéias em produtos e serviços e viver disso.

    Você não faz parte do público alvo se você tem a idéia para um software mas não consegue escrever o software sozinho. Esse encontro especificamente não é um bom lugar para encontrar um programador para implementar a sua idéia. Para isso tente a apinfo. Talvez isso mude se o grupo crescer e se organizar, seria ótimo um encontro para juntar pessoas com boas idéias com pessoas com conhecimento técnico para executá-las. Não é (preconceito|elitismo|frescura|complexo de superioridade), é só questão de foco e redução de pauta para tentar fazer desse encontro o primeiro de muitos.

    Em 30/11/2009 22:32 - Comentários (8)

    Lendo e medindo o tempo em C e C++: função time()

    Bom, chega de teoria e encheção de lingüiça e vamos para o código de uma vez por todas. A função mais conhecida para pegar a data e hora é a função time() da C runtime, que retorna quantidade de segundos desde o "Unix Epoch", que é meia noite (00:00:00) de 1 de Janeiro de 1970.

    Características da função time():

    • Retorna a quantidade de segundos desde 1 de Janeiro de 1970. Ou seja, a sua precisão máxima é de um segundo. Ou seja, não serve para medir performance. (ou seja, vou explicar mais sobre isso depois)
    • O horário retornado é GMT, para pegar o horário local é necessário fazer manualmente o cálculo para ajuste de fuso horário. Para usar o horário local é necessário usar a função get_timezone (ou _tzset no Visual C++) para ler a diferença do fuso que você usará para fazer o cálculo depois
    • Como o retorno da função é um número inteiro, é muito fácil fazer contas com ele. Para avançar a data em dois dias, por exemplo, é só somar [60 * 60 * 24 * 2].
    • Durante o inicio dos tempos do unix o retorno era do tipo int de 32 bits. Fazendo uma conta simples com os limites de um inteiro, vemos que o limite de medição é algum dia no ano de 2038, criando uma nova e repaginada versão do bug do milênio. Nas versões mais atuais dessa função o retorno é um int64, o que empurra o limite para uma data bem longínqua, quando provavelmente não haverá mais programadores vivos no universo.
    • Por ser uma função da C runtime, está disponível em qualquer compilador C e C++ (qualquer == qualquer compilador não-exótico para plataformas não-exóticas). É provavelmente a função mais multiplataforma de todas que eu vou mostrar

    Como um trecho de código vale mais do que 0xFFFFFFFFFFFFFFFF palavras, here we go:

    #include <stdio.h>
    #include <tchar.h>
    #include <time.h>
    #include <iostream>
    #include <iomanip>
    #include <string>
    
    using namespace std;
    
    void print_time(time_t t)
    {
      tm* formatted_time;
    
      //
      // essa função "quebra" essa quantidade de segundos e dia, mês, ano, etc
      //
      formatted_time = gmtime(&t);
    
      //
      // isso vai mostrar algo como "2009/12/07 14:42:57"
      // note que é necessário somar 1900 na data para pegar o ano corrente
      //
      cout << setfill('0') << 
        setw(4) << formatted_time->tm_year+1900 << "/" <<
        setw(2) << formatted_time->tm_mon+1 << "/" <<
        setw(2) << formatted_time->tm_mday << " " <<
        setw(2) << formatted_time->tm_hour << ":" << 
        setw(2) << formatted_time->tm_min << ":" << 
        setw(2) << formatted_time->tm_sec << endl;
    }
    
    
    int main()
    {
      time_t t;
    
      //
      // pega o número de segundos desde 1970
      //
      t = time(NULL);
    
      //
      // mostra na tela, formatado
      //
      print_time(t);
    
      //
      // pega o fuso horário da máquina para pegar o horário local
      // ao invés de GMT. 
      //
      long timezone;
    
      _tzset(); // carrega as configurações de fuso
    
      _get_timezone(&timezone); // lê a diferença do fuso
    
      cout << timezone << endl;
    
      //
      // ajusta o horário pelo fuso
      //
      t -= timezone;
    
      print_time(t);
    
      //
      // avança da data em 2 dias
      //
      t += 48 * 60 * 60;
    
      //
      // mostra novamente
      //
      print_time(t);
    
      return 0;
    }
    
    
    
    

    Esse código foi feito no Visual C++ (Windows). Existem pequenas diferenças nos nomes das funções no Linux, mas o conceito é o mesmo. Consulte sua documentação preferida para detalhes específicos.

    Em 16/12/2009 22:09 - Comentários (1)

    Vou fazer uma palestra sobre Python na Campus Party
    • Título: Seja dinâmico com Python
    • Descrição: Vou falar sobre quase todas as gambiarras e coisas maravilhosas que um programador pode fazer com os recursos "dinâmicos" do Python. Criar membros e métodos em runtime, usar o reflection mais simples do planeta, responder por métodos que não existem na hora em que são chamados. Métodos __MAGICOS__ e seus correlatos. Como transformar um registro de um banco de dados em um objeto (quase um ORM) com duas linhas de código (ou três). E as piadas quase-engraçadas de sempre
    • Data e hora: 26 de janeiro (terça-feira), 09:00hs da matina. Para quem vai acampar lá é uma boa maneira de começar a semana :-)
    • Links: Campus Party, agenda do evento, meu twitter

    Além disso, devo ficar lá o dia inteiro. Essa palestra está sendo organizada pelo GruPy-SP, o Grupo de Usuários Python do Estado de São Paulo.

    Em 22/01/2010 21:59 - Comentários (1)

    Apresentação do Tio dia 15 de maio no nosqlbr

    Esse sábado acontecerá o primeiro evento brasileiro sobre nosql, organizado pelo @porcelli do OpenSpotlight. Eu farei a palestra "Tio: um nosql Made in Brasil". Tio? Ãh?

    Eu ando parindo um nosql já faz mais de um ano, depois de passar mais de um ano pensando no conceito. Aproveitando esse evento e a modinha do nosql, acho que agora é a hora de publicar os fontes e divulgar. Os fontes e a documentação estão em http://code.google.com/p/tio (apesar da insistência do @porcelli para que eu colocasse no github :-))

    Para o pessoal de C++, o Tio nada mais é do que containers STL em um servidor remoto, mas tudo construído sobre um base publish/subscribe. Olha como fica simples construir alguns softwares server side com esse conceito:

    • Servidor de filas: criar uma lista, o producer manda as coisas fazendo push_back na lista e o consumer usa pop_front. Ou o commando wnp_next, que facilitar a contrução de clusters computacionais.
    • Servidor de chat: criar uma lista de mensagem para cada usuário. O usuário assina as modificações da própria lista e quem quiser mandar uma mensagem pra ele faz um push_back na lista. Você pode criar um map com os IDs dos usuários, onde o valor é o nome da lista de mensagens. Se você assinar esse mapa, ainda será notificado automagicamente sobre todos os usuários criados ou apagados

    Minha idéia é: todo software server mantém estado e notifica clientes das modificações nesses estados. Eu faço isso bastante usando C++, e é sempre isso. Um monte de containers STL para controlar o estado da aplicação, e código para enviar notificações via TCP para os interessados. O Tio faz quase todo esse serviço sozinho. Muitos protótipos de aplicação podem ser feito com zero linhas de código server.

    Os fontes e a documentação estão lá no site do projeto Tio. Para compilar no Windows, tem um projeto do Visual Studio 2008 (não testei no Express, mas deve funcionar). No Linux, você vai precisa do CMake ("cmake . && make" faz o serviço). Você vai precisar do Boost devidamente instalado, compilado e configurado, o Tio é feito usando Boost Asio. O fonte é bem simples e direto, deve ser um bom ponto de aprendizado para quem está estudando Boost Asio.

    Criei duas listas de discussão: uma em inglês, e uma em português. Note que o site do Tio é todo em inglês (e os comentários do Wiki devem ser em inglês). Está nos planos ter documentação em português também. Mas eu sou um só :-)

    Nos fontes você vai achar um client para C++ e um client Python. Por falta de tempo, o client Python está mais refinado do que o client C++. Em C++ tudo é feito usando algo como

    shared_ptr<TioContainer>
    e
    container->PushBack(TIONULL, "abcdef")
    Em Python você usa um container remoto como se fosse local, com coisas tipo
    container["rodrigostrauss"] = "messages/rodrigostrauss"

    Para maiores explicações e o chopp/happyhour/networking de sempre, nos encontramos no evento de nosql esse sábado. Traga suas perguntas e comparações com outros nosql.

    Em 11/05/2010 15:11 - Comentários (1)

    Mais Tio

    Aos que foram à palestra e lançamento do Tio no nosqlbr, algumas coisas que eu esqueci de falar:

    • O Tio funciona com 3 campos: key, value e metadata. Esses campos podem ter o tipo string, int, double e raw (blob).
    • As chaves dos maps só podem ser strings. Mas você pode acessar por índice numérico para ler o n-ésimo item do map
    • Todo container tem suporte à propriedades. Por exemplo, você pode criar uma propriedade "schema" em um container para saber como serializar ou desserializar o que está no campo value
    • Para fazer um upsert, é só fazer algo como "my_map['rodrigo'] = 'strauss'"
    • Eu quebrei o build do Linux... Quem for testar hoje, tem que compilar em Release e usar somente os containers em memória (volatile/list, volatile/list, etc). Corrijo isso essa semana :-)

    Quase tudo que eu falei está na documentação do projeto. Quem tiver mais dúvidas, o melhor canal para perguntar são as duas listas de discussão (inglês e português).

    A apresentação está em http://docs.google.com/present/view?id=dg9kbfzn_73m4c7qvc6

    Em 15/05/2010 23:29 - Comentários (0)

    Sétimo Encontro de Programadores C++

    O grupo C & C++ Brasil (ccppbrasil) tem o imenso orgulho em anunciar (agora oficialmente) o nosso sétimo encontro. Detalhes:

    Em 22/07/2010 22:09 - Comentários (1)

    Seminário "C & C++ Portabilidade e Performance"

    O grupo C & C++ Brasil (ccppbrasil) vamos realizar mais um evento, a segunda edição do Seminário de Portabilidade e Performance. Esse evento é voltado para as qualidades mais apreciadas das nossas linguagens preferidas: a portabilidade e a possibilidade de criar um código extremamente rápido.

    Eu falarei sobre os dois assuntos. Na minha apresentação, "Abordagens para a construção de servidores de alta performance em C & C++", falarei sobre como usar STL e Boost para criar servidores multiplataforma e dicas para fazer esses servidores responderem solicitações usando poucos ciclos de processador. Eu já trabalho com servidores C++ faz uns 6 anos, vou tentar falar sobre coisas que me custaram várias madrugadas no WinDbg para aprender.

    Os outros palestrantes falarão mais de performance. Teremos uma palesta de Assembly com o Wanderley Caloni, sobre sincronização com o Fernando Roberto e sobre CUDA com o Fabio Gallupo. A primeira versão do evento foi muito boa, acredito que essa tem tudo para ser muito boa também.

    Inscrições e maiores detalhes no site da Tempo Real Eventos. E, claro, com certeza, teremos o chopp-pós-evento de sempre.

    Em 03/12/2010 19:50 - Comentários (2)

    O Seminário de Portabilidade de Performance é dia 11 de dezembro

    Coloquei a data errada no post sobre o seminário... A data correta é 11 de dezembro. Obrigado Vinicius Jarina pela correção.

    Em 06/12/2010 17:00 - Comentários (1)

    Vim mais amigável para usuários de Visual Studio

    Quando eu uso Linux, eu uso vim+ctags+omnicppcomplete+taglist, cmake para build e cgdb para debug. Não é integrado como o Visual Studio, mas funciona bem e tem até suas vantagens. Não tenho a pretensão de ensinar vim aqui, isso levaria muuuitos posts. Ou seja, se isso tudo é novidade para você, leia o tutorial do vim, leia a documentação do ctags, cmake, e do omnicppcomplete antes.

    Primeiro, meu vim tem algumas adaptações para ficar com a teclas de atalho do Visual Studio para compilar. Isso vai no meu .vimrc

    "
    " Visual C++ 6 like keys to build
    "
    :nmap <F7> :wa \| :make<CR> " build
    :nmap <F8> :cope \| :cnext<CR> " next error
    :nmap <F4> :TlistToggle<CR> "
    :nmap <F12> <C-]>
    :nmap <F6> :bnext<CR>
    

    Para autocomplete (Intellisense no linguajar Microsoft), eu uso ctags e omnicppcomplete. O ctags monta o "banco de dados de coisas para autocomplete", análogo ao arquivo ncb do Visual Studio. O próprio vim sabe usar um arquivo de tags, mas o omnicppcomplete torna e experiência muito melhor.

    Eu faço algumas mudanças na configuração do omnicppcomplete para melhor a experiência:

    "omnicppcomplete
    let OmniCpp_NamespaceSearch = 1
    let OmniCpp_GlobalScopeSearch = 1
    let OmniCpp_ShowAccess = 1
    let OmniCpp_ShowPrototypeInAbbr = 1 " show function parameters
    let OmniCpp_MayCompleteDot = 1 " autocomplete after .
    let OmniCpp_MayCompleteArrow = 1 " autocomplete after ->
    let OmniCpp_MayCompleteScope = 1 " autocomplete after ::
    let OmniCpp_DefaultNamespaces = ["std", "_GLIBCXX_STD"]
    

    Você pode simplesmente fazer um banco de dados de ctags com STL, Boost e seus projetos. Isso vai fazer com que seu vim pare por 20 segundo cada vez que você tentar completar alguma coisa. Para resolver isso, eu fiz um shell script que monta um arquivos tags somente com o que é usado no seu projeto. É só rodar isso na pasta do projeto. Fazer isso funcionar em um projeto com várias pastas fica como exercício para o leitor :-)

    #!/bin/bash
    
    DEPFILE="dependencies"
    
    if [ -e "$DEPFILE" ]; then
            rm "$DEPFILE"
    fi
    
    #
    # find all files dependencies
    #
    for x in *.cpp
    do
             echo dependencies for "$x"...
             gcc -E -M -H -I/usr/local/include/boost/ "$x" > /dev/null 2>> "$DEPFILE"
    done
    
    if [ -e global_dep ]; then
            rm global_dep
    fi
    
    #
    # summarize
    #
    echo summarizing...
    sed "s/^\.* \(.*\)/\1/g" "$DEPFILE" | 
    grep -v "Multiple include guards may be useful for" | sort | uniq > global_dep
    
    if [ -e tags ]; then
        rm tags
    fi
    
    echo generating tags...
    xargs --arg-file=global_dep ctags --append --c++-kinds=+p --fields=+iaS --extra=+q
    

    Funciona assim: uso o gcc -H para enumerar os headers usados por todos os cpps. Jogamos tudo isso para um arquivo dependencies e filtramos os duplicados com "sort | uniq". Depois todos os headers estarão no arquivo global_dep, e o xrags resolve o problema de gerar tags para todos os arquivos encontrados. Lembre-se que meus conhecimento de shell script são suficientes somente para subsistência e deve haver um modo melhor de fazer isso. Aceito sugestões nos comentários. :-)

    Links:

    Em 22/02/2011 15:26 - Comentários (5)

    Bye Bye C++/CLI

    Como "xingar muito no Twitter" não faz muito meu estilo, vou usar bem mais do que 140 caracteres para explicar porque C++/CLI entrou na minha lista negra de tecnologias.

    A promessa parecia maravilhosa: uma tecnologia que permite integrar código C++ nativo com código gerenciado .NET de forma quase automática. Confesso que quando o C++/CLI foi lançado eu fiquei realmente impressionado, imagino o trabalho que deve ter dado para fazer isso. Mas como na prática a teoria é outra, depois de usar C++/CLI e ter muitos problemas, resolvi jogar a toalha. Motivos para minha decisão:

    • Ao usar C++/CLI reneguei minha máxima de só usar tecnologias Microsoft que eles usam internamente. Assumo que C++/CLI não é extensivamente usando dentro da Microsoft, pelo simples fato de nunca ter lido nada sobre isso.
    • O Intellisense do Visual Studio 2010 não tem suporte a C++/CLI. O SP1 também não. Agora a Microsoft está prometendo para próxima versão. Tarde demais.
    • For fuck sake, NÃO EXISTE Application::Run em C++, isso é C++/CLI. No help do framework você pode filtrar pelas linguagens. E tem um tal de C++ em todos os tópicos do .NET Framework, mesmo que o compilador de C++ só gere código nativo. A Microsoft criou uma IMENSA confusão entre C++ e C++/CLI, e isso é notório na lista C & C++ Brasil (exemplo 1, exemplo 2 e exemplo 3).
    • Quando você ativa o suporte a C++/CLI, o compilador começa a reclamar com algumas coisas do Boost ([1], [2]). Muitas vezes o código compila mas o programa não roda. A única solução é encher tudo de #pragma managed e #pragma unmanaged. Mas a Microsoft recomenda que você não faça isso, mas sim, reestruture seu código (e note que o bug do link foi fechado como "wont fix"). Alguém pode até dizer que "Boost é incompatível com C++/CLI". Mas Boost é C++ padrão, e o C++/CLI propõe integrar código C++ padrão com .NET. #FAIL
    • Não vejo prioridade para correções nos bugs do compilador relacionados a C++/CLI
    • Quando você chama código C# a partir de um código C++/CLI, muitas vezes o debugger do Visual Studio não percebe que você mudou para código gerenciado (mesmo com o debug "managed" ativado), e você não consegue fazer debug.

    Migrei todo meu código C++/CLI para C++ comum. Tudo que é C++ fica em uma DLL, onde eu exporto tudo como funções C. Nessas funções C eu faço a ponte entre .NET e C++ de forma manual (coisa que o C++/CLI deveria fazer automaticamente). É necessário fazer código ponte na parte C# também, principalmente para fazer o marshal/unmarshal e para guardar a referência dos delegates passados como ponteiro de função. Pronto, agora tudo funciona como deveria, eu sempre consigo fazer debug, e eu sei que não vou perder uma semana inteira (isso já aconteceu) mudando flags de compilação até meu projeto funcionar.

    Já que eu já comecei a sessão reclamação, vou aproveitar para mandar mais um bullet point (ponto de bala?):

    • A IDE do Visual Studio 2010 é a pior ide que eu já vi a Microsoft colocar no mercado. Pelo simples fato de ser extremamente instável e bugada. Cai toda hora. O Intellisense para C++ continua sendo uma piada, só que agora uma piada que consome 100% de processador quase sempre através do mais novo penduricalho do Visual C++, um tal de vcpkgsrv.exe. Quando você digita isso no Google, a primeira sugestão é "vcpkgsrv.exe crash". Bom para Whole Tomato, que continua muito bem vendendo o Visual Assist.
    • Para ser justo, o compilador C++ do Visual Studio 2010 é o MELHOR COMPILADOR C++ QUE EU JÁ VI. Até hoje não vi nenhum "internal compiler error". E tem um ótimo suporte à C++0x. O único problema é a ambiguidade de coisas como ter std::shared_ptr e boost::shared_ptr, mas isso não é culpa da Microsoft.

    Prometo que o próximo post terá código (em Assembly se possível), porque, afinal, a humanidade já possui uma ferramenta oficial para reclamar de tudo e todos, o Twitter.

    Em 04/03/2011 20:04 - Comentários (26)

    Baterista-programador precisa de banda

    Cansei de ficar só em casa estudando bateria com fone e metrônomo. Já cheguei a ensaiar um tempo com uns amigos, mas não foi pra frente. Se alguém precisa de um baterista, por favor me mande uma mensagem pelo formulário de contato.

    Quando eu ensaiava a gente tocava Metallica, Millencolin, Megadeath, Green Day e Pearl Jam. Eu gosto também de coisas clássicas como Beatles, Iron Maiden, Deep Purple e Black Sabbath. Também gosto bastante de rock anos 90, tanto nacional (Legião, Paralamas, Titãs, Engenheiros do Hawaii, etc), como internacional (Pearl Jam, Nirvana, Alice in Chains, etc). Apesar de tudo tenho tocado bastante Jamiroquai no meus momentos de lazer, e, apesar de gostar bastante de metal progressivo, ainda preciso de mais estudo pra tocar coisas como Rush, Angra, Dream Theater ou Symphony X. Se sua banda só toca música própria e eu gostar do estilo, eu também topo.

    Bom, acho que isso é suficiente para você saber o que eu gosto e consigo tocar. Posso ensaiar um vez por semana, de preferência em algum estúdio na Pompéia. Só topo ensaiar em estúdio ou onde já tenha uma bateria, pois eu moro em prédio e ficar levando minha batera pra passear toda semana dá muito trabalho.

    Em 10/03/2011 22:57 - Comentários (1)

    Lendo e medindo o tempo em C e C++: GetTickCount

    GetTickCount é uma função da api Win32, portanto disponível somente no Windows. Qualquer versão do Windows desde o Windows 95 disponibiliza essa função.

    Características:

    • Retorna a quantidade de milissegundos desde a inicialização do Windows, portanto, não pode ser usada para horário, somente para medição de tempo;
    • Sua precisão é de no máximo 15ms. Ou seja, se você medir seguidamente o tempo com GetTickCount você nunca terá um intervalo entre 0 e 15 milisegundos;
    • Só funciona no Windows;
    • É a forma mais rápida de medir tempo no Windows, ou seja, a chamada desse função demora pouco em relação aos outros método. Esse valor é atualizado pelo timer do sistema, então, eu suponho, que ele seja somente uma leitura nas memória.

    Código:

    int main()
    {
      DWORD start = GetTickCount();
    
      AlgumaCoisaQueDemoraBastante();
    
      DWORD elapsed = GetTickCount - start;
    
      cout << "AlgumaCoisaQueDemoraBastante levou " << elapsed << " milissegundos para rodar" << endl;
    
    }
    

    Eu já recebi algumas planilhas com números de medição de performance onde os intervalos nunca eram menores do que 15ms, e algumas vezes o código medido levada 0ms. Esse é o problema em usar um timer sem resolução suficiente para mensurar o tempo de um trecho de código. Você pode usar o GetTickCount para medir tempo, contanto que você saiba das limitações.

    A maior vantagem do GetTickCount é ser uma função que, no Windows, leva o menor tempo para te retornar uma referência de tempo. Consequentemente, interfere bem menos no tempo que será medido.

    Veja também: Lendo e medindo o tempo em C e C++: função time()

    Referência: GetTickCount na MSDN

    Em 01/08/2011 19:29 - Comentários (2)

    Slides da palestra sobre C++11 no Oitavo Encontro de Programadores C & C++

    Apesar do material já ter sido enviado para a nossa lista, outros programadores podem achar útil, então, lá vai:

    Original PPTX

    Em 26/10/2011 13:23 - Comentários (0)

    C++11: Introdução

    C++11 é a nova versão da linguagem C++, definida pelo padrão ISO/IEC 14882:2011. O fato de ser um padrão ISO traz a vantagem de nenhum fabricante ser "dono" da linguagem. Mas isso também cria um problema comum em qualquer coisa que é decidida por comitê: é necessário um consenso entre os membros para que algo seja adicionado ou modificado, e isso torna o processo sempre mais lento. O último padrão C++, por exemplo, foi de 2003. 8 anos depois, finalmente temos uma nova versão.

    (Isso me lembra de quando fui a um Coding Dojo no Google Brasil. No final, todo mundo deve escrever as opiniões sobre o Dojo em papeizinhos, um para vantagens, outro para desvantagens. Foram inúmeras as reações de inconformismo quando leram o papel que eu escrevi com uma desvantagem: "Democracia demais".)

    Mas a espera valeu. Todos os novos recursos foram bem, digamos, "azeitados", e o padrão já nasceu com vários compiladores implementando uma parte considerável dele (isso demorou bem mais no C++03). Além da vantagem de quem vem por último: você aprende com os erros e acertos dos outros (como Java, C# && Python).

    Os principais objetivos do novo padrão, segundo quem participou das discussões de definição da linguagem e segundo eu mesmo são:

    • Facilidades de linguagens modernas: O melhor exemplo é a implementação de lambda, que, antes tarde do que nunca, chegou ao C++. Mas, por tardar e consequentemente aprender com os acertos e erros dos outros, temos em C++ a melhor implementação entre as linguagens mainstream.
    • Facilidade para ensino: Essa veio do Stroustrup (o criador da linguagem), que de uns anos pra cá voltou a dar aulas de programação para iniciantes. No último encontro de C++ me perguntaram qual seria a vantagem do C++11 para iniciantes. Não consegui dar uma resposta objetiva, além de citar o fato de que uma linguagem melhor acaba virando uma linguagem mais fácil.
    • Melhorias na biblioteca padrão: A STL já estava mesmo precisando de uma atualizada, e resolveu-se unir o útil ao agradável: com o tempo, as bibliotecas mais estáveis e bem aceitas do Boost estão sendo incorporadas à biblioteca padrão. O C++11 tem std::threads, std::shared_ptr, std::function, e inúmeras melhorias que vou detalhar em posts futuros.
    • Acabar com “gambiarras” do Boost: Não sei se esse era realmente um dos objetivos iniciais, mas eu gostei disso ter acontecido. Acho metaprogramação em C++ um negócio muito legal, mas exotérico demais. Boost Bind é algo extremamente necessário para programação assíncrona, mas 100+ erros de compilação por esquecer uma vírgula ou um _1 é de matar. E o Boost Lambda, convenhamos, é uma gambiarra que cresceu demais e aprendeu a falar. Com o recurso de lambda da linguagem se tornam desnecessários o Boost.Bind e o Boost.Lambda. Foi bom enquanto durou, mas não sentirei saudades.

    Em breve, posts detalhando cada ponto. Enquanto isso, você pode como sempre, ler a extensiva lista de novidades na Wikipedia.

    Em 03/11/2011 13:23 - Comentários (3)

    Agora eu visto a camisa da empresa

    Um post que gerou bastante polêmica foi o "Eu não visto a camisa da empresa". Em um emprego meu gerente contou que, quando um diretor resolveu me contratar sem consultá-lo ele imprimiu esse artigo, levou pra mesa dele e falou: "É esse cara aqui que você vai contratar?". O que eu acho ótimo. É mais fácil quando sabem das minhas opiniões antes de eu chegar. Economiza alguma discussões.

    Durante muito tempo como programador peão de obra eu vi muitos gerentes cometendo erros crassos. Na realidade, como todo programador já sabe, a cada emprego que você passa você aprende sobre várias coisas que não deve fazer para ter sucesso em um projeto. Aprende-se muito mais o que não fazer. Conheci gerentes muito bons e projetos bem administrados, mas são, infelizmente, a minoria.

    Depois de ver tanta besteira sendo feita comecei a me perguntar: será que é assim mesmo que as coisas funcionam e nada pode ser feito? Cheguei a cogitar que produção de software no Brasil é isso, uma grande bagunça onde a maioria dos gerentes e diretores são maus programadores que foram promovidos e não fazem a menor ideia do que estão fazendo.

    Um belo dia, depois de anos recusando, resolvi aceitar um cargo de gerente de desenvolvimento, para ver se era realmente impossível fazer as coisas de uma maneira correta. Descobri: não é impossível. Não é fácil, mas tendo disciplina, método, e principalmente interesse, é possível. O que eu descobri é que as pessoas geralmente não se importam com isso. Elas se interessam pelo cargo, não pelo trabalho. Para virar programador eu li dezenas de livros. Quantos gerentes você conhece que leram ao menos um livro sobre o assunto? Além disso, os pseudo programadores recém promovidos costumam esquecer rápido que fazer a coisa certa envolve, no final das contas, dar condições para que os programadores programem, deixando-os distantes de políticas de empresa, não enchendo o saco por causa de horário e coisas estúpidas que todos sabemos que são estúpidas. Já escrevi sobre isso bastante (aqui, aqui, aqui), não vou repetir.

    (Fim da sessão reclamação. Voltando ao assunto)

    Depois de mais de 3 anos programando nas horas vagas, eu finalmente consegui juntar forças com um sócio não-programador e abrir um empresa de desenvolvimento de software para o mercado financeiro, a Intelitrader. Nosso foco é fazer software de alto desempenho e alta disponibilidade, basicamente o que eu tenho feito nos últimos anos. Mas dessa vez eu não preciso preciso explicar porque estou usando C++11, Python e até Assembly quando necessário. :-)

    Abrir uma empresa de software é meu projeto de vida desde que tenho 15 anos (esses dias achei o adventure que eu fiz em 1995, com o "copyright" de STRAUSSoft). Aqui na Intelitrader eu tenho silêncio para programar em paz, e mesmo que eu tenha várias tarefas de dono de empresa, sou bem mais produtivo do eu era nos empregos por onde passei. E não é porque o lucro da empresa virá para o meu bolso. É porque meu foco é fazer software bom e com alta disponibilidade.

    Como eu fiz quando virei gerente de desenvolvimento, vou tentar fazer aqui. Ver se, na prática, tudo que eu sempre preguei é possível. Sem horários (e com home office), sem regras de vestimenta (estou de bermuda no escritório nesse exato momento), sem frescura. Um lugar onde os escovadores de bits podem fazer o que mais gostam. E onde o importante é fazer software bom, rápido e fácil de usar. Se eu tiver disciplina e organização suficiente (tempo todo mundo tem), vou escrevendo minhas experiências aqui.

    Pois é, agora eu visto a camisa da empresa. Por vários motivos. E o motivo principal é o fato de eu poder programar em paz por 4 horas seguidas quase todo dia. Isso não tem preço...

    Em 16/11/2011 14:23 - Comentários (16)

    C++11: auto

    A palavra chave auto já existe no C++ desde os tempos mais primórdios. Ela sempre era (sim, era) um modificador de storage de variáveis. Você podia escolher o modificador auto para variáveis de armazenamento automático e register para armazenar a variável em um registrador. Esse modificador hoje em dia é implícito: se não especificado o armazenamento o compilador decide onde armazenar. De forma automática. O modificador register, até onde sei, é grande conhecido do pessoal de embarcados, que muitas vezes trabalham com recursos limitados e precisam gerenciá-los a ponto de dizer ao compilador onde a variável x será armazenada.

    Interessante lembrar que a palavra chave register gera alguns efeitos colaterais, como a impossibilidade de pegar o endereço da variável com o operador &. E tem ainda o fato de que muitos compiladores modernos simplesmente não respeitam o register.

    1. //
    2. // Eu dizendo ao compilador que quero a variável a
    3. // *preferencialmente* armazenada em um registrador.
    4. //
    5. register int a = 10;
    6.  
    7. //
    8. // Eu dizendo para o compilador "eis minha variável b, enfie
    9. // onde bem entender"
    10. //
    11. auto int b = 42;
    12.  
    13. //
    14. // Quando não especifico nada, o "enfie onde quiser"
    15. // está subentendido
    16. //
    17. int c = 42;

    Com o padrão C++11 o significado dessa palavra chave foi modificada. Agora ela é usada para tipar uma variável de acordo com a expressão a ela atribuída. Na linha "int a = "2 + 2", o compilador sabe que a expressão "2 + 2" retorna um tipo inteiro - na realidade o compilador sempre sabe o tipo de uma expressão em uma linguagem tipada - e pode muito bem acertar a declaração de acordo.

    Esse recurso não é lá muito útil para expressões simples e pode até prejudicar a legibilidade do código, já que o leitor terá que efetuar de cabeça a mesma avaliação de expressão que o compilador fez. Ele realmente se torna útil para tipos e subtipos de classes templates, onde só o tamanho da declaração do tipo da variável pode tomar mais da metade da tela. Exemplo:

    1. //
    2. // Variável a será do tipo int
    3. //
    4. auto a = 10;
    5.  
    6. //
    7. // Variável f será do tipo double, devido à regra de promoção de tipos
    8. //
    9. auto f = 10 * 2.0;
    10.  
    11.  
    12. std::map<std::string, std::string> um_mapa;
    13.  
    14. //
    15. // Variável i será do tipo informado
    16. //
    17. std::map<std::string, std::string>::iterator i = um_mapa.begin();
    18.  
    19. //
    20. // Variável biggie_smalls terá o mesmo tipo da variável i,
    21. // mas economizando bastante espaço
    22. //
    23. auto biggie_smalls = um_mapa.begin();
    24.  
    25. //
    26. // Só pra mostrar que essa coisa toda funciona
    27. //
    28. assert(i == biggie_smalls);
    29. i = biggie_smalls;
    30.  
    31. //
    32. // ISSO NÃO FUNCIONA. É preciso haver uma atribuição para
    33. // que o compilador saiba qual o tipo
    34. //
    35. auto x; // <- error 12345: you really don't know how to use auto, do you?

    Para quem programa em coisas tipo Haskell isso pode parecer óbvio. Mas já vi trocentas pessoas achando que isso é uma tentativa de transformar C++ em uma linguagem dinâmica. Então, só pra garantir:

    A "nova" palavra chave auto NÃO TORNA O C++ UMA LINGUAGEM DINÂMICA. O tipo ainda é estático, você só não precisa informá-lo quando o compilador consegue deduzir isso sozinho.

    Ah, isso é equivalente ao var do C# e igual ao funcionamento de declaração de variáveis da linguagem Go. E *não* é igual à declaração de variável em Python, Ruby ou Perl.

    Mais um exemplo, e caso específico onde o auto é mais usado:

    1. using std::string;
    2. using std::map;
    3. using std::vector;
    4.  
    5. map<string, vector<string>> connections;
    6.  
    7. //
    8. // Como é hoje
    9. //
    10. for(map<string, vector<string>>::iterator i = connections.begin() ; i != connections.end() ; ++i)
    11. {
    12.  
    13. }
    14.  
    15. //
    16. // Muito mais fácil de ler.
    17. //
    18. for(auto i = connections.begin() ; i != connections.end() ; ++i)
    19. {
    20.  
    21. }

    Em 28/11/2011 14:39 - Comentários (5)

    Sobre minha nomeação como Microsoft MVP em Visual C++

    Como alguns de vocês devem saber, esse ano eu fui nomeado Microsoft Most Valuable Professional em Visual C++. Aos que estão fora do mundo Microsoft, MVP é um "título" que a Microsoft concede aos profissionais que colaboram com a "comunidade" difundindo conhecimento sobre um dos produtos deles. É basicamente o que eu faço na lista de discussão do grupo C & C++ Brasil, com os eventos sobre C++ que eu ajudo a organizar e com os posts sobre Visual C++ aqui no blog. Mais detalhes sobre o programa MVP no site da Microsoft.

    O único MVP de Visual C++ no Brasil era o Fabio Galuppo, que é também membro do grupo C & C++ Brasil.

    Seja lá o que for, eu recebi isso por causa do grupo C & C++ Brasil, afinal, não costumo participar dos eventos da própria Microsoft e não respondo perguntas nos newsgroups da Microsoft faz anos. Até onde sei isso não é comum, e acho bem legal ter esse reconhecimento devido à uma comunidade não-Microsoft. Na minha primeira mensagem para lista-vip-clube-de-campo dos MVPs eu falei sobre o grupo C & C++ Brasil, a lista, e a variedade de tecnologias que usamos. Afinal, o último evento do grupo teve C++11 (com Visual C++), Android+Bada+iOS, ARM e até um framework usado pelo CERN. Mas sempre falamos de Visual C++ também.

    Meu agradecimento pela nomeação (não é um prêmio) vai para os membros do grupo C & C++ Brasil, mas se extende à todos os programadores C & C++ do Brasil. Nós já vimos muitas modinhas irem e virem, mas continuamos aqui. E continuamos multiparadigma, multilinguagem e na maioria das vezes muitissistemaoperacional. (essas regras novas de gramática ainda me confundem...).

    Em 16/01/2012 04:28 - Comentários (1)

    Proposta aos favoráveis à regulamentação da profissão de Analista de Sistemas

    Li recentemente uma pesquisa que diz que a maioria dos profissionais da área são a favor da regulamentação da profissão de analista de sistemas. Eu, sendo um cara otimista e de boa fé, me acalmo pensando que esse número é grande por pura desinformação.

    De qualquer forma, ao invés de simplesmente debater com argumentos, coisa que já fiz bastante, deixo aqui uma proposta:

    Concorda que só pessoas com formação acadêmica na área de TI possam fazer sistemas?
    Comece desinstalando da sua máquina todos os programas feitos por programadores não formados.

    Fechado? Não esqueça de desinstalar o sistema operacional também.

    Argumente a vontade na área de comentários, acho que esse assunto precisa ser mais discutido. Mas lembre-se que sou formado em Ciências da Computação e acho que uma faculdade ajuda bastante na formação profissional. O resto dos argumentos já coloquei em outro post.

    Em 09/08/2012 17:15 - Comentários (17)

    11 anos depois

    Faz 11 anos do primeiro post no 1bit.com.br.

    "Eu tardo, mas não falho". Eu costumava dizer isso bastante quando eu era um adolescente que sonhava em ser hacker. Acho que essa antiga expressão vale para essa volta ao blog, que estou planejando faz anos. Espero que blogs ainda sejam alguma coisa e que alguém ainda tenha meu feed em algum leitor de RSS... :-)

    Comecei o 1bit em 2004. A ideia inicial era fazer um site para escrever artigos sobre programação junto com o Alfred Myers na época em que trabalhavamos juntos. Acho que o nome seria CodeTalk ou algo assim. O domínio estava livre até o dia em que eu tentei registrá-lo... O Alfred desanimou totalmente, eu não. Então resolvi seguir sozinho e ter um blog em uma época em que quase ninguém tinha blog de programação no Brasil. Blogs eram coisas para você falar de você e colocar fotos do seu cachorro fofinho e seus amigos.

    Esse tempo longe do blog coincide com o tempo que eu resolvi seguir outro sonho: ter minha própria empresa, onde as coisas não funcionariam como nas empresas por aí ( escrevi bastante sobre isso). Eu precisava seguir meu próprio caminho para continuar programando, já que a pressão para ser um gerente burocrático cresceu e se tornou a única forma de aumentar o salário. Eu nunca quis parar de programar. Além disso, essa palhaçada de ter horário rígido e perder mais tempo agradando diretor do que programando me embrulhava o estômago. Minhas empresas estão bem, pago minhas contas com o dinheiro delas desde 2011 e, apesar da crise, tudo vai muito bem, obrigado. Meu LinkedIn diz "sócio programador", e meu próximo cartão de visitas vai dizer o mesmo. Ah, pretendo escrever sobre empreendedorismo para programadores também.

    Aprendi bastante coisa nesse tempo que fiquei distante. Trabalhei em vários projetos, atendendo vários clientes. Dei aulas de C++ e Python, onde os alunos faziam perguntas sobre coisas impensáveis. E tudo isso aumentou bastante minha vivência com desenvolvimento de software, desde à concepção até a codificação em si. Uma das ideias é compatilhar aqui tudo que aprendi nesses anos à frente da Intelitrader e da BitForge.

    Tenho várias ideias para essa volta. Falar sobre C++ e Python, sobre gerência de projetos e engenharia de software, empreendedorismo, algoritmos e eteceteras. Estou até pensando em fazer uns vídeos sobre programação, com live coding e tudo mais. Peço que vocês me ajudem com isso, dando sugestões sobre conteúdo e formatos.

    Eu aprendi bastante nesses anos e espero compartilhar o máximo possível de conhecimento aqui. Espero que vocês gostem. Bora escovar bits.

    Em 21/07/2015 07:04 - Comentários (7)

    Sobre o cancelamento da trilha de C e C++ do TDC

    A trilha de C e C++ do TDC foi cancelada. Muita gente tem me perguntado sobre isso, então vou explicar tudo detalhadamente aqui para encerrar esse assunto de uma vez.

    Todas as informações relativas a esse assunto estão documentadas em uma thread da lista de discussão do grupo C & C++ Brasil. Thread essa que eu postei no Twitter. Mas como a thread é longa, as pessoas não estão lendo a thread inteira e tirando conclusões erradas sobre o assunto. Eu achei que o conteúdo da thread fosse suficiente para que as pessoas chegassem à mesma conclusão que eu cheguei. Mas não foi o que aconteceu, cada pessoa que me pergunta fala uma coisa diferente.

    Timeline

    • Foi criada uma thread para informar os membros da lista sobre a aprovação das palestras. (link)
    • Algumas pessoas da nossa lista usaram essa thread para falar do descontentamento com o preço do TDC (link 1, link 2, link 3)
    • Um membro da nossa lista postou uma mensagem na lista de Python perguntando se o sentimento lá era o mesmo. Nessa mensagem ele citou um possível boicote ao TDC (link)
    • Como a palavra "boicote" foi usada, várias pessoas entraram em pânico pelo Brasil inteiro (link)
    • Eu enviei uma mensagem para lista perguntando o motivo do descontentamento e dizendo que eu não achava o evento caro (link)
    • Pessoas da lista fizeram críticas ácidas sobre o TDC, expressando a opinião de que o grupo deveria sair do TDC (link)
    • Uma pessoa da organização do TDC se inscreveu na nossa lista e enviou uma mensagem nada amigável (link)
    • Um dos palestrantes, membro da lista, cancelou a participação no evento (link)
    • Eu expliquei que aquilo era uma discussão em curso, não uma posição oficial do grupo (link)
    • Mais pessoas da organização do TDC entraram na nossa lista para defender o evento. A discussão continuou, já que se trata, afinal, de uma lista de discussão (link)
    • Uma pessoa da organização postou a seguinte mensagem no Facebook, que foi apagada algumas horas depois (link) :
      "pessoal em lista de discussão de C / Python propondo boicote ao‪ TheDevConf‬ 
      por conta do preço... "Eu estou ficando enojado com esse preço 
      exagerado da TDC. Ainda mais que palestrantes não tem sequer benefício 
      a não ser uma camiseta, o que torna o evento uma máquina de lucros 
      exorbitantes pros organizadores." 
      Melhor começar a programar Java ou .Net hein. Começou a temporada do 
      Mimimi do #TheDevConf SP, terra dos reclamões senior. Ah, lembrando 
      que o sanduba do subway é frio e na dúvida entre ir e encher o saco ou 
      ficar na firma, fique na firma! Grato." 
      
    • Depois do tiroteio começou uma operação panos quentes para resolver o problema (não tem link, essa parte foi em pvt)
    • Eu fiquei extremamente puto com as mensagens da organização do TDC (link 1)
    • Houve uma tentativa de resolver a situação e pedido de desculpas por parte do pessoal do TDC (sem link, isso também foi em pvt). Respondi que as desculpas foram aceitas mas que eu não tinha mais a menor vontade de participar do evento
    • Eu fiquei na dúvida. Para resolver minha dúvida li a mensagem novamente. Ela dizia "... o sanduba do subway é frio e na dúvida entre ir e encher o saco ou ficar na firma, fique na firma!". Resolvi então ficar na firma e me retirei da organização da trilha
    • A trilha acabou sendo cancelada

    Se você quer tirar algum juízo de valor, leia a thread inteira, todas as mensagens. Tudo que é preciso saber sobre o acontecido está nessa thread, inclusive os links para as mensagens da lista de Python e o conteúdo da mensagem do Facebook. A única coisa que não está na thread são as várias tentativa de remediar a situação por parte da organização do TDC, algo que aconteceu principalmente fora da lista.

    No final todo mundo saiu perdendo, principalmente a comunidade de C e C++. O grupo C & C++ Brasil vai tentar fazer mais eventos para compensar isso. Inclusive, o 12º Encontro de Programadores C & C++ acontece no Rio de Janeiro dia 15 de Agosto de 2015.

    Em 24/07/2015 12:05 - Comentários (3)

    C++11: lambda

    Lambda é uma função inline, anônima, que você pode definir dentro de qualquer lugar no código.

    Usos:

    • Manter o código que trata o callback de uma função perto da chamada da função, para deixar o código mais legível;
    • Definir o código da função de uma thread diretamente na chamada que cria a thread, também para legibilidade;
    • Substituir os functors de forma mais simples, já que as lambdas no C++ podem guardar estado também. Ajuda na legibilidade do código, também devido à proximidade do código. Eu já expliquei functors antes;
    • Nunca mais precisar usar Boost.Bind, que gera aproximadamente 375 erros de compilação se você esquece uma vírgula ou algum _1.

    Vamos à sintaxe:

    Por partes:

    • Captura: nessa parte você determina quais varíaveis dos escopos exteriores estarão disponíveis dentro do lambda. Na maioria das linguagens (como JavaScript e C#) a captura é implícita, ou seja, é só usar a variável dentro do lambda que ela está capturada. Em C++, pra variar, você tem mais controle sobre isso, podendo especificar ainda se a captura será por valor ou por referência;
    • Parâmetros: Aqui definimos os parâmetros. Funciona da mesma forma que a declaração de parâmetros de uma função;
    • Tipo de retorno: Essa parte é opcional, não precisa colocar caso essa lambda não retorne nada. Essa é a nova sintaxe C++ para definir tipo de retorno, que fica depois dos parâmetros. Isso possibilita, entre outras coisas, o uso de decltype() em cima dos parâmetros e coisas assim. Dá pra escrever um artigo inteiro sobre isso, então vou omitir detalhes sórdidos por enquanto;
    • Corpo da função: é a parte do [insira seu código fonte aqui]. Você escreve seu código como em uma função comum, com return e tudo mais;

    Como um trecho de código vale mais que 0,14% de álcool no sangue, vamos ao que interessa:

    Em 03/08/2015 09:53 - Comentários (4)

    11 anos depois: C++
    Ah, o C++... A linguagem que desperta amor e ódio, excitação e pavor. A linguagem que nunca passa desapercebida, a linguagem sobre qual todos gostam de falar, todos gostam de opinar. Afinal, a maioria do software que as pessoas usam é feita em C++. Não dá pra não ter uma opinião.

    Só pra recordar a história, C++ foi só uma das linguagens orientadas a objetos que nasceram nos anos 80, junto com o SmallTalk. Ela venceu a batalha pela mente dos programadores por um motivo bem pragmático: nasceu compatível com a linguagem C desde o primeiro dia. As primeiras versões do C++ foram feitas usando o pré processador do C, para fazer o que era chamado de C com classes. E as mesmas características continuavam: você tinha construções de alto nível mas era possível até escrever assembly dentro do seu código fonte. Você tinha funções e um sistema de tipos mas podia manipular a memória diretamente usando um void*.

    Em 2004, ano de fundação do 1bit.com.br, nós tínhamos o C++98, um padrão ISO. A comunidade de forncedores de compiladores era uma bando de competidores e a qualidade das implementações variava bastante. O Microsoft Visual C++ tinha uma boa IDE, um suporte horrendamente péssimo ao padrão ISO mas um compilador atendia muito bem as necessidades dos usuários Windows (onde eu me incluo). A Borland tinha o C++ Builder, cujo compilador era tão cheio de bugs que até hoje o código fonte do Boost é cheio de #ifdefs para contorná-los. No mundo Linux o GCC era um bom compilador que atendia o padrão corretamente, talvez o único compilador mainstream a fazer isso (não, o Comeau não conta).

    O cenário de desenvolvimento mudou bastante nesses 11 anos. Em 2001 a Microsoft apresentou ao mundo o C#, uma linguagem inspirada no Java, que por sua vez era inspirada no C++. Em 5 anos de existência o C# evoluiu mais que todas as outras linguagens do mundo juntas. E isso foi, claro, muito bom para os usuários de C# - onde eu me incluo. Mas acabou sendo ótimo para os usuários de todas as outras linguagens, já que a rápida evolução do C# gerou uma imensa pressão no mercado. Até o Java, que já estava virando o COBOL do século 21, acabou evoluindo. O Java apresentou seu generics meia boca (que por dentro ainda usa Object e boxing/unboxing) e os responsáveis pelo C++ dentro do comitê ISO (principalmente o Herb Sutter, que foi para Microsoft depois de um tempo) resolveram acelerar o C++. É isso mesmo, se você gosta de C++11, C++14, C++17 e tudo mais agradeça à Microsoft e ao C#.

    Foi assim que o C++ evoluiu, acrescentando uma tonelada de novos recursos, mas sem quebrar a compatibilidade com os fontes antigos. Hoje em dia o C++ é uma linguagem moderna, onde gerenciamento de memória manual pode ser coisa do passado. Mas toda a bagagem de mais de 30 anos ainda está ali. Você pode deixar a linguagem trabalhar para você, mas tem a opção de continuar sendo um maloqueiro.

    (Inclusive foi essa a motivação para que o Rob Pike criasse o Go. Houve uma apresentação sobre C++11 no Google. Ele assistiu a apresentação e pensou "Sério que vocês querem resolver o C++ COLOCANDO MAIS COISAS NELE?" Ele saiu da reunião e começou a esboçar uma nova linguagem que virou o Go. Linguagem essa que eu não posso usar na Intelitrader por causa da porcaria do Garbage Collector...)

    Então veio o C++0x, onde o x seria, junto o com o zero à esquerda, o ano de lançamento do novo padrão. Atrasou (como todo projeto de software) e virou o C++11, o grande upgrade que a linguagem precisava. Auto, constexpr, lambda, modelo de memória, initializer list e uma nova sintaxe para definir classes e estruturas. A lista de coisas novas é gigantesca. Além das melhorias na linguagem, várias libs do Boost foram incorporadas ao padrão, como a shared_ptr, tuple, thread e regex. Nasceu então o tal do Modern C++.

    Hoje o C++ suporta vários paradgimas: programação procedural, orientada a objetos, genérica e até funcional. Sim, várias construções de linguagens funcionais se tornaram possíveis com auto e lambda. O Bartosz Milewski escreve bastante sobre programação funcional em C++.

    Ironicamente, enquanto o C++ evoluiu bastante nesses 11 anos, meu site continua usando PHP e MySql (que como diz meu amigo Gianni, é um bando de dados)...

    Em 13/08/2015 08:01 - Comentários (0)

    Sobre a Microsoft e o open source

    Muito se tem falado sobre esse novo movimento da Microsoft em direção ao open souce. Para muitos da velha guarda, ver a mesma empresa tida como monopolista dos anos 90 fazendo isso tem gerado muita desconfiança. Como uma empresa de software proprietário vai aderir ao movimento open source?

    Sim, a Microsoft aderiu ao modelo de código aberto. Mas as pessoas esqueceram o X da questão: código aberto é diferente de software livre. A governança open source virou algo mais eficiente que a governança fechada. Quanto mais feedback dos usuários, melhor. Quando os usuários são técnicos, como nós programadores, melhor ainda. E como adoramos código fonte, ficamos mais felizes e damos mais feedback. Com podemos ver o código fonte, dizemos onde o erro está e até mandamos a correção pronta. Qual empresa não gostaria que seus clientes e usuários ajudassem a arrumar seu próprio produto?

    Inacreditavelmente, hoje em dia muita gente gosta da Microsoft (com a exceção dos fanáticos do free software, é claro). No Hacker News, onde me parece que a maioria do pessoal é de Linux, há um sentimento geral positivo em relação à Microsoft. É irônico ver isso depois de todos esses anos... Levei bastante tempo para entender como a Microsoft funciona e como ela pensa. Já li muita coisa, já assisti muita coisa (como o Channel9) e já visitei a Microsoft em Redmond 2 vezes, uma como representante do grupo C & C++ Brasil e outra como MVP. Minha carreira sempre orbitou em volta de softwares da Microsoft e eu comecei a programar usando um software deles, o GW Basic. Tenho usado bastante Linux ultimamente, mas isso não me afastou do Windows e, principalmente, do Visual Studio.

    Apesar de gostar da Microsoft no geral, eu não concordo com tudo que eles fazem. Esse costume horrendo que eles têm de implementar parcialmente os padrões (principalmente os padrão ISO do C++) segundo a visão puramente mercadológica é uma abominação. Eles levaram tempo para entender que implementar o padrão C++ era mais importante do que implementar só a parte que eles usam. Isso só chegou esse ano, para meu total desgosto e desespero. Mas mesmo assim continuei com o Visual C++ por causa da qualidade. Eu já tentei migrar para o GCC. Mas a qualidade do ferramental da Microsoft é muito alta e acabei ficando com o ecosistema do Visual Studio. Apesar do Fora o famigerado C++/CLI...

    Mas uma coisa é inegável: a Microsoft é uma máquina extremamente eficiente de fazer software. Eles foram durante muitos anos a única empresa do mundo que sabia fazer software. Todas as empresas que competiram com eles caíram, a Netscape, a Borland, a Word Perfect, a Lotus. E não era por monopólio ou coisas assim, como gostam de dizer. Foi por competência em Engenharia de Software. O Excel venceu o Lotus 1-2-3 porque era melhor. O Word venceu o WordPerfect porque, versão após versão, ficou melhor. E o Internet Explorer ficou melhor que o Netscape.

    Eu sei o que você está pensando. Blablaba monopólio blablabla. O IE continua vindo junto com o Windows, e mesmo assim todo mundo baixa o Chrome e o Firefox. Hoje o Internet Explorer é basicamente uma ferramenta gigante que serve para baixar outros browsers. Poderia ter sido assim na época. Mas a Netscape foi estúpida o suficiente para fazer a maior estupidez que uma empresa pode fazer: jogar fora o código fonte porque ele "está feio "e comerçar tudo de novo.

    Voltando ao open source. A coisa é simples. Eles estão se adaptando ao mercado, como eles sempre fizeram. A Microsoft nunca foi inovadora, como a Apple ou mesmo o Google. Eles inovaram várias vezes (o Visual Basic foi o precursor de todos as IDEs modernas), mais o foco deles não é a inovação. É fazer software direito. E nisso eles são muito bons, é inegável.

    Eles levaram bastante tempo para entrar na Internet, tentando levar as pessoas para a MSN, que era uma rede separada, tipo a Compuserve. Depois foi uma versão do IE atrás da outra e a Netscape morreu pela própria incompetência (novamente, alguém aí usa IE só porque vem junto com o Windows?).

    Eles levaram tempo para entrar no mercado de nuvem. Hoje o Azure fatura milhões.

    Eles levaram tempo para entender o mercado de mobile. O Windows Phone... é.... mmm... está aí.

    E agora eles entenderam o modelo de negócio open source e estão se adaptando. Como sempre fizeram. E garanto que vão fazer isso melhor do que gente que está aí faz tempo, como IBM e outros, pelo simples fato de que eles entendem de software como ninguém. Até onde sei, o modelo aberto do .NET já nasceu mais aberto que o Java...

    Em 27/08/2015 08:01 - Comentários (0)

    Índice para a série de posts sobre o Itanium, por Raymond Chen

    O Raymond Chen escreveu uma série sobre o assembly do Itanium. Apesar do Itanium ter sido um fiasco comercial sua arquitetura é genial e a série vale a leitura.

    Não tem índice para os tópicos da série no site dele. Então eu batuquei um regex no teclado e fiz um índice.

    Parte 1
    Parte 2
    Parte 3
    Parte 3b
    Parte 4
    Parte 5
    Parte 6
    Parte 7
    Parte 8
    Parte 9
    Parte 10

    Em 25/09/2015 22:37 - Comentários (0)

    C++11: Range based for loop

    Todos os programadores C++ sabem que para percorrer qualquer tipo de container devemos usar iterators e nunca um índice. Certo? Na dúvida, eu já escrevi uma extensa série sobre STL, e escrevi um post só sobre iterators. Lembre-se que se você usa C++ sem STL você está usando C com classes.

    Apesar do acesso via iterator ter inúmeras vantagens, ele requer uma dose grande código repetitivo. E isso é incompatível com o fato de que nós, programadores, somos preguiçosos por natureza. Afinal, muita gente começou a programar para automatizar tarefas repetitivas. Como resolver isso?

    Dois iterators definem um range (intervalo em pt-br). Eles delimitam o início desse intervalo, com o primeiro item inclusive e o segundo item exclusive (ou seja, o último item de um range **nunca** é válido e aponta para um item após o último item válido). Todos os algoritmos da STL ou compatíveis com a STL trabalham em cima de um par de iterators que define os itens que serão tratados pelo algoritmo (eu também já escrevi sobre algoritmos)

    Para percorrer todos os itens de um container (seja ele um vector ou um unordered_map) usando um for nomal, você precisa percorrê-lo do begin() até o end(). Apesar de os containers da STL não implementarem um interface que obrigue isso - já que a STL não usa polimorfismo e sim programação genérica - existe um concept que define que container.begin() e container.end() precisam retornar iterators para o início e o fim do container. A página da saudosa Silicon Graphics sobre STL tem essas definições bem explicadas e documentadas.

    Sabendo disso, esse é o código para percorrer todos os itens de um container:

    Repetitivo e verbose, certo? O conceito de iterators foi criado para que você possa aplicar um algoritmo à qualquer intervalo de um container, mas na maioria das vezes você faz isso sobre todos os itens. O range based for é um loop que "extrai" automaticamente o range completo de qualquer coisa que você passar para ele.

    Veja como fica o código acima usando range based for:

    A partir do C++11 o jeito preferido de pegar os iterators é usando as funções livres begin() e end(). Por que? Porque isso torna o código mais genérico. O vector possui begin() e end(), mas o array C não possui e o container de um framework de terceiros também não. Usando as funções livres permite que você crie especializações delas para containers que não sigam os concepts das STL. Veja isso:

    Sim, você entendeu direito. O range based for é só um syntax sugar.

    Em 01/10/2015 09:25 - Comentários (0)

    Semântica

    É engraçado como a semântica da maioria dos mortais não vale para programação. "Legado" da forma que aprendi na escola é algo bom, que é passado entre as gerações. O dicionário confirma. Mas o significado dessa palavra mudou no decorrer dos anos. Pelo menos para nós, escovadores de bits.

    Para quem trabalha com programação, "legado" é aquele código antigo que ninguém quer dar manutenção e que todo mundo culpa por todos os problemas. Problemas em produção? É culpa do código legado. O projeto levou o dobro do tempo? É porque "precisamos contornar o legado". O sistema dá problema em produção todo dia? "É só a parte do legado, o que fizemos no último ano funciona". O sistema e a empresa continua com problemas, mas parece que culpar o legado isenta todo mundo de toda e qualquer responsabilidade.

    Então começamos um projeto novo para "resolver o legado", depois de convencer todo mundo. Sempre pensamos que, dessa vez, vamos fazer diferente e montar uma arquitetura que é robusta, escalável e que vai ajudar a empresa a crescer, sem os problemas do passado. Afinal, agora somos mais espertos e entendemos mais sobre programação. Mas isso **nunca** acontece. Por que?

    Nós estamos hoje construindo o legado de amanhã. Tanto no bom sentido quanto no mal sentido (mais uma vez me lembro como é engraçado em programação a palavra "legado" ter um conotação ruim por si só). Daqui alguns anos, o código que estamos escrevendo hoje também será um "código legado". E por que isso? Existem vários motivos, o principal é: não conseguimos prever o futuro. Isso simplesmente não é possível. No futuro as necessidades serão outras, as técnicas serão outras, e pricipalmente as pessoas e suas visões de mundo serão outras. Todas as tentativas que eu vi de fazer um sistema que seria genérico o suficiente para prever o futuro acabou gerando um meta framework super genérico que só piorava a situação.

    O framework genérico caseiro é um erro clássico de grandes empresas, quase sempre obra dos ditos "Arquitetos de Software". Já vi vários casos onde a empresa faz seu próprio framework e bibliotecas internas para coisas simples como acesso à banco de dados, só para garantir que os programadores vão "fazer a coisa certa". Aí todo mundo passa boa parte do tempo contornando esse framework interno que foi feito por uma "equipe de arquitetura" que não tem programadores ou engenheiros de software experientes. Geralmente as equipes de arquitetura são formadas por pessoas que querem um salário maior e menos pressão por prazo e qualidade. Como um deles já me disse uma vez, "no Word sempre compila, ser arquiteto é bem mais fácil".

    E algumas vezes a proposta para "resolver o levado" é jogá-lo fora e recomeçar. **A IDEIA MAIS ESTÚPIDA DO PLANETA**. O Joel já disso isso faz tempo e parece que ninguém entendeu. Você não pega um código fonte que funciona e simplesmente joga fora. Você pode reorganizá-lo e substituir as partes problemáticas aos poucos, mas trocar tudo de uma vez é algo que eu nunca vi funcionar. Nunca.

    Software não é algo estático, ele precisa estar sempre em evolução. O legado é eterno. A atenção que damos para ele determina se o legado será bom ou ruim. Ao invés de fazer um projeto novo a cada 4 ou 5 anos, devemos manter o software evoluindo sempre. A Microsoft refaz o Word de tempos em tempos? E o Linux, ele passa por uma reformulação geral a cada 5 anos? Não. Software de qualidade está sempre evoluindo. É assim que deve ser.

    Lembre-se: código fonte é código fonte. Bonito ou feio, novo ou velho. E é nossa responsabilidade fazer com que o ele continue funcionando. Então, por favor, deixe de frescura, abra aquele código fonte antigo, entenda como ele funciona e mantenha tudo funcionando e evoluindo. É o seu legado.

    Em 19/11/2015 06:56 - Comentários (1)

    C++11: Inicialização uniforme

    Algumas mudanças no C++11 são tão grandes que têm o potencial de deixar a linguagem muito melhor, porém irreconhecível. Além do recurso de lambda (do que eu já falei) os recursos de inicialização uniforme e nova sintaxe para declaração de funções são desse tipo. Hoje vou falar sobre Uniform Initialization (ou inicialização uniforme em bom português).

    O C++ tem a característica de não ser uma linguagem uniforme. Isso quer dizer que uma sequência específica de tokens pode ter significados diferentes dependendo do contexto. Isso causa diversos problemas, entre ele uma dificuldade bem maior para escrever um parser (interpretador) para a linguagem. Veja a seguinte expressão:

    1. MeuObjeto algumaCoisa(10, "lala");

    Esse código pode significar duas coisas: uma declaração de função que retorna MeuObjeto ou a declaração de uma varíavel do tipo meu objeto que tem um construtor com dois parâmetros. Isso existe devido à algo que é chamado de Most Vexing Parser, ou Interpretador Mais Vexaminoso na língua de Camões. Nesse caso específico, essa linha será interpretada como uma declaração de função, algo bem inconviniente caso o trecho acima esteja dentro de uma função:

    1. class MeuObjeto
    2. {
    3. public:
    4.   MeuObjeto(int a, string b) {}
    5. };
    6.  
    7. int main()
    8. {
    9.   // Parece algo normal, mas isso é um erro de compilação
    10.   // Isso será interpretado como uma declaração de função
    11.   MeuObjeto algumaCoisa(10, "lala");
    12. }

    Para resolver esse problema, entre outros, foi criada a inicialização uniforme:

    1. int main()
    2. {
    3.   // Como usamos colchetes ao invés de parenteses,
    4.   // o compilador sabe com certeza que isso é uma chamada
    5.   // de construtor (e não uma declaração de função)
    6.   MeuObjeto algumaCoisa{10, "lala"};
    7. }

    Mas como sempre, a forma anterior de fazer as coisas continua valendo. Lembrem-se: o C++ tem como premissa continuar compatível com todo código fonte que já foi escrito até hoje. Essa é a maldição e a benção do C++: a linguagem evolui e fica competitiva em relação às linguagens mais novas, mas os programadores podem continuar fazendo as coisas da forma antiga e, muitas vezes, pouco recomendáveis. Um exemplo é a parte de gerenciamento de memória. Vou repetir o que digo em quase toda palestra: se você está usando new e delete você está fazendo isso errado.

    O recurso de inicialização uniforme permite que toda inicialização de instâncias de objetos e estruturas usem essa nova sintaxe:

    1. // Lembrando que tipos fundamentais também aceitam
    2. // a sintaxe de construtor
    3. int i {3};
    4.  
    5. // Inclusive um construtor vazio (nesse caso i2 == 0)
    6. int i2 {}; // empty braces initialize the object to it's default (0)
    7.  
    8. // a std:string é uma classe, e aceita uma string
    9. // literal no construtor
    10. std::string s {"hello"};
    11.  
    12. MeuObjeto algumaCoisa {10, "lala"};
    13.  
    14. MeuObjeto muitasCoisas[] = { {10, "lala"}, {20, "lili"} };

    É uma pequena mudança que resolve grandes problemas.

    Em 17/02/2016 06:43 - Comentários (3)


    Posts anteriores >>
    rebarba rebarba
      ::::