Pular para o conteúdo principal

Postagens

Eclipse PDE com Gradle

Na virada de 2016 para 2017, adotamos oficialmente o Gradle como ferramenta de construção de software na Prodist. A fila da migração está fluindo confortavelmente desde então. Já temos as provas de conceito necessárias para todos os projetos, inclusive os projetos nativos para compilador C++. Para fontes Java usamos Eclipse. Integramos projetos Gradle ao Eclipse usando Buildship . Acho que o Buildship ainda tem um longo caminho pela frente, mas realiza tudo o que necessitamos. Já estamos entregando com base nesse esquema. O problema mais importante que encontramos foi na integração de projetos OSGi. No Eclipse, trabalhamos projetos OSGi com o PDE. Nosso ciclo de desenvolvimento inclui rodar configurações "OSGi Framework" no Eclipse para teste. Infelizmente, o PDE assume uma estrutura de projeto própria e não funciona corretamente caso contrário. Estes problemas estão registrados em bugs como, por exemplo, o  bug 153023 . Nossos projetos OSGi com Gradle usam o layout ...

Construindo um "cross toolchain" com GCC 5.1 e binutils 2.25

Uma das Pesquisas no Laboratório investiga o uso de C++ moderno (inicialmente 2011, agora 2014) na definição de programas bare metal. Nosso interesse geral está em aplicações de I/O e um dos nossos interesses específicos está em aplicações tipo "dispositivo criptográfico". Ao retornar para esta Pesquisa, resolvemos atualizar nossas ferramentas para obter uma implementação completa do C++ 2014. Nossa motivação foi o lançamento do GCC 5.1 que inclui suporte completo para esta nova especificação. Nosso objetivo é estudar aplicações para BIOS, UEFI, Multiboot 1 e 2 e outras "máquinas" similares emuladas com qemu. Estamos construindo programas para arquitetura i686-pc-elf . Nosso ambiente de desenvolvimento é Fedora 21 64 bits, onde as ferramentas tem arquitura alvo x86_64-redhat-linux . Apesar de x86_64 e i686 serem "compatíveis", nesse tipo de projeto é necessário um cross toolchain. Precisamos basicamente de um cross binutils e um cross g++. Existem di...

Função "once" básica com <atomic>

Já é a segunda vez que uso este método simples para funções "once". std::atomic initialized = false; void initialize () {    if (initialized.exchange(true)) return;    // do initialization only once } // reentrant, safe to call from async threads void api (int foo, int bar) {    initialize();    // do interesting stuff }

Invertendo a direção das dependências

Hoje, mais uma vez, resolvi um problema de projeto virando o jogo de cabeça para baixo. Pretendo refazer este artigo no futuro com uns diagramas mais legais; por enquanto segue apenas o texto para não ficar esquecido. Um sistema extensível exibe padrões conhecidos; por toda parte vemos sistemas de "plugins". Imaginando nosso sistema com "plugins", vemos um elemento Central e diversos elementos Adicionais que podem ser inseridos e removidos do sistema à vontade. O elemento Central é um só e há vários Adicionais ligados ao Central. Extraindo dessa imagem uma imagem conceitual, vemos o tipo Central em relação com o tipo Adicional, onde Adicional tem multiplicidade zero ou mais. Partindo daqui para uma imagem comportamental, vemos o que acontece quando inserimos o Adicional. Ou tentamos ver. O que acontece? Quem faz o quê? Vamos supor que estamos criando tudo isso com um servidor de aplicação J2EE. Nesse ambiente, os módulos, quando iniciados, sofrem (1) inj...

return void();

É uma pequeneza mas eu gostaria muito que as linguagens da família do C permitissem retornar void quando a função retorna void . Escrever da forma a seguir me aborrece. public class Foo  {    public void log (String msg) { }    public void bar (String x, String y) { }    public void bar (String x)    {      if (x == null)      {        log("x was null");        return;      }      bar(x, "default");    } } Eu ficaria mais feliz escrevendo assim: public class Foo  {    public void log (String msg) { }    public void bar (String x, String y) { }    public void bar (String x)    {      if (x == null) return log("x was null");      bar(x, "default");     } }

JNDI e Plan 9

Acabei de realizar quão similar é o J2EE e o Plan 9 quando observados pela lente da arquitetura de componentes: no Plan 9, o sistema de arquivos é tão capaz quanto o JNDI no J2EE. Um componente no qual eu estou trabalhando agora depende de um componente de banco de dados e de um componente de correio eletrônico. O modo de ligação entre esses componentes se dá através de JNDI: meu componente faz referência aos nomes "dataSource" e "mailSession" no seu contexto local de nomes. Na configuração do componente em uma instalação, esses nomes locais são ligados aos nomes globais que o administrador preparou para oferecer as dependências. É claro que tanto o meu componente quanto suas dependências mantém um contrato entre si, na forma das interfaces java EntityManager e Session. No Plan 9, meu componente seria um programa que faz referência a dois arquivos no seu file system -- um arquivo que repr...

Aplicação J2EE não deve criar arquivos..?

Recentemente surgiu a necessidade para um web service  para upload  de arquivos. Uma aplicação de HTTP com GET e PUT seria suficiente. Lendo uns artigos por aí, encontrei novamente esse discurso: "aplicações J2EE não devem escrever em arquivos". O fundamento do discurso está no fato de um componente para um ambiente gerenciado programar o ambiente gerenciado e não o sistema operacional, de modo que não deve fazer suposições sobre coisas como threads  ou arquivos. Os detalhes do argumento, porém, são péssimos. Dizer -- o componente não deve usar arquivos porque se a implantação ocorre em um cluster  os dados não serão vistos com consistência -- é uma falácia. Se você implantará o componente em um cluster  usará um sistema de arquivos compartilhado. O erro implícito nessa "sabedoria" seria igualmente cometido por um administrador de cluster  que usasse um banco de dados independente para cada nó.

GWT DevMode com EJB

Este ano estamos iniciando nossa convivência com sistemas interativos via web e escolhemos o Google Web Toolkit para nos apoiar, convictos em jamais usar pré-processamento de HTML no servidor. Enquanto este toolkit (na versão 2.4) nos pareceu desde o início uma coisa totalmente excelente, uma preocupação se manteve firme: o ciclo de desenvolvimento ótimo do GWT exige o uso do DevMode. O DevMode do GWT é um truque excelente que permite ao desenvolvedor usar um depurador Java para rastrar a execução do programa que roda no user agent . Como GWT é fundamentalmente um compilador de Java para Javascript, depurar o código-fonte de produção seria no mínimo desagradável. A configuração padrão do DevMode (na versão 2.4) inclui o web container Jetty para servir o HTML, JS, CSS, Servlets e tudo o mais que compõe o programa. O Jetty é um container simples e não inclui, entre outras coisas, uma implementação de JPA ou EJB. Ora, como este projeto pode permanecer confortável? Eventualmen...

Pensamento

E se a interface do sistema de arquivos focasse primeiro nos arquivos e depois nos nomes, e não o contrário? Ao invés de criar um arquivo com nome tal, os programas primeiro criam arquivos, da mesma forma como alocam memória, e posteriormente criam ligações desse arquivo para locais na árvore de nomes. Um programa que criasse um arquivo mas nunca criasse ligações teria um arquivo temporário fatalmente destruído após o término do programa. Uma referência válida para o arquivo em um programa adquire o arquivo e portanto ele não pode ser destruído; transmitir essa referência a outro programa por interprocess communication  se torna uma forma de object remoting  paralela a shared memory . Isto evidenciaria melhor a distinção no sistema de arquivos entre os arquivos propriamente ditos e os nomes; no Unix, entre os inodes  e os paths . A interface de arquivos e a interfaces de nós na árvore seria logicamente associada porém claramente distinta. Esta distinção é útil a...

Pela milionésima vez este erro

Cometi pela milionésima vez o mesmo erro e deixo este artículo na tentativa de impedir a próxima: não, não interessa se a operação overlapped diz que completou imediatamente, haverá uma completion packet esperando na completion port . Apenas ignore.

Porting C to ... C

Chegou, então, a hora de ressuscitar um programa desenvolvido pelo fundador da empresa em um inominável sistema UNIX rodando em i386 no passado remoto. O programa, cujo sistema de construção permitia a configuração para inúmeras variantes do UNIX de então, permaneceu compilável com mínimas modificações até o Fedora 17. Impressionante. Infelizmente, ao executar o programa, segmentation fault a torto e a direito. Com a ajuda de um debugger , alcançamos a seguinte iteração, simplificada para este artigo: for (int i = 0; i < name_list_size; ++i) {   p = (char *) getenv(name_list[i]);   if (p != NULL)     if (*p != NULL)       strcpy(value_list[i], name_list[i]); } Simples, certo? O programa levantava SIGSEGV ao avaliar a expressão *p . Como é possível? Certamente getenv retorna um endereço válido ou NULL . Certamente p não é NULL quando avalia-se *p . Qual é o tipo de p ? char * p . Por acaso o programa está s...

Servidor e usuário COM na mesma "solution"

 Um dos objetivos da próxima iteração no meu projeto atual é quebrar o sistema em mais pedaços, e levantar os pedaços um nível de abstração até componentes COM. Uma das consequências é substituir diversas ligações mais estáticas e torná-las ligações mais dinâmicas através do COM. Tipicamente, um programa usuário de um componente COM, escrito para o Visual Studio, usará a diretiva #import para incluir na unidade de tradução C++ as declarações necessárias. Isso implica que o componente deve estar instalado na máquina que constrói o programa. O que fazer quando o componente em questão está no mesmo lote de construção que o programa usuário? A solução inicial é criar uma dependência de construção tal que o componente seja sempre construído primeiro, e usar um post-build event ao projeto do componente para registrá-lo no sistema. Desse modo, na construção do programa, o ambiente estará correto. Eu considero este arranjo prejudicial. Minha melhor razão é esta: o servidor de const...

.NET COM Interop, IStream e Visual Studio

Investigando oportunidades para otimizar um componente, experimentei definir métodos que não solicitam dados (em arrays etc.) como argumentos mas sim instâncias de IStream , e me deparei com um problema interessante: interoperabilidade com .NET. Usar componentes COM em programas .NET não é particularmente difícil; no Visual Studio, é possível adicionar componentes COM como referências no projeto. Se o componente for capaz de Automation, é possível usar a ferramenta tlbimp.exe para gerar manualmente um callable wrapper para o componente, que, se eu entendi as coisas corretamente, não precisa ser distribuído com a aplicação. Porém, todos os métodos que eu aprendi tem essa peculiaridade: eles parecem assumir que um componente não usa interfaces definidas por outros componentes -- como, por exemplo, declarando parâmetros com tipo IStream , uma interface definida pelo sistema. Pelo método do callable wrapper , a ferramenta assume que as interfaces mencionadas integram o componente...

Não misture Visual Studio 10 com o Developer Preview!

Infelizmente, eu caí na asneira de experimentar o Visual Studio 11 Developer Preview no meu sistema principal de desenvolvimento. O Developer Preview é razoável, e a presença dos testes de unidade para C++ me fizeram muito satisfeito. Porém, o Visual Studio 10 não funciona direito quando misturado com o Visual Studio 11 Developer Preview. O instalador do Developer Preview sobrescrever alguma configuração no sistema que é compartilhada com o 10. O primeiro efeito colateral que eu percebi foi nos projetos .NET, o que me levou a supor a natureza do problema. Projetos .NET começaram a indicar problemas nas referências a outros projetos -- com um ícone de exclamação em vermelho na tela de referências. A Internet me informa que isto é sinal de que os projetos ligados como referência são incompatíveis -- o referido indica uma versão do .NET mais recente que o referente. Minha hipótese é a de que o instalador do Developer Preview define, em algum lugar central, que o .NET 4.5 beta é a ...

Ensinando Programação Não-Estruturada

Conversando com várias pessoas novatas ao longo dos anos, percebi que é muito difícil obter uma compreensão clara do que é a programação estruturada por falta de contexto. O material didático e os cursos afirmam a maravilha da programação estruturada ao organizar programas, enumerando problemas que ninguém mais tem, porque todas as linguagens de programação atuais são estruturadas. Como é possível comunicar a dificuldade de programar de uma forma que é, atualmente, impossível programar? Uma solução seria ensinar BASIC e então mostrar como é problemático escrever programas longos em BASIC, em comparação com Pascal. Porém, BASIC e Pascal são escrotas por sí só, sem qualquer relação com a presença ou ausência de estruturas. Ensinar programação não-estruturada com o intuito de redescobrir a programação estruturada não pode implicar mais dores que esta: manifestar o fluxo de controle apenas com goto. Me ocorreu, então, que esta é exatamente a solução: tomar uma linguagem de programa...

Visual Studio 6: debugger vs. Unicode string

Gente, depois de uma eternidade de sofrimento para interpretar (TCHAR *) no debugger do Visual Studio 6, eu acabei de descobrir, nesse minuto!, que existe uma opção chamada "Display Unicode String" e que esta opção não está marcada na configuração padrão . Em Options, Debug. Você clica nela e o debugger mostra strings de wchar_t. Por quê!?

O Problema de Gestão de I/O

A palestra que dei no Sétimo Encontro do Grupo de Usuários de C e C++ do Brasil apresentou meu resultado atual na pesquisa de um framework baseado em uma forma de orientação a aspectos em C++ descrita no "Modern C++ Design" do Alexandrescu. Minha palestra não apresentou, porém, o que é exatamente o problema a resolver, assumindo que fosse razoavelmente conhecido. Seu foco foi apresentar todos os mecanismos disponíveis para se resolver o problema no Linux 2.6, suas peculiaridades e seus aspectos comuns. Uma certa razão me motivou, após esta apresentação, a rebobinar meu raciocínio e considerar o problema original. [1] O que há exatamente de tão problemático na gestão de I/O em um programa? A imagem inicial é a de um programa cujo objetivo é interagir com um dos dispositivos ligados ao computador. A forma básica dessa interação é a cópia de dados do dispositivo para a memória e da memória para o dispositivo, sendo que trabalho útil por parte do programa ocorre enquanto os...

Análise Sintática Incremental

Em 2008, perguntei na ccppbrasil.org sobre uma boa maneira de produzir parsers incrementais. Dois anos depois, pude parar e estudar o manual do Bison o suficiente para descobrir ali o meu objetivo: push parsers . Este exemplo do manual é precisamente o que eu quero: int status; yypstate *ps = yypstate_new (); do { status = yypush_parse (ps, yylex (), NULL); } while (status == YYPUSH_MORE); yypstate_delete (ps); Meu objetivo é aplicar esta técnica para produzir o decodificador de mensagens de protocolo de aplicação em um servidor baseado em readiness-notification e non-blocking I/O.

Por que goto é considerado prejudicial?

Recentemente, o Fabiano Vasconcelos abriu uma discussão no Grupo de Usuários de C e C++: De cara eu vi algo aqui um pouco estranho, se que o amigo Márcio me permite comentar: que muitos programadores, inclusive eu (se que posso ser rotulado como programador) foram instruídos com o princípio de NUNCA usar o goto, por ser considerado um mau estilo de programação. Durante a discussão, o Eduardo Vieira puxou um artigo da KernelTrap sobre uma discussão similar ocorrida no grupo de desenvolvimento do Linux, onde Robert Wilken disse o seguinte: In general, if you can structure your code properly, you should never need a goto, and if you don't need a goto you shouldn't use it. It's just "common sense" as I've always been taught. Unless you're intentionally trying to write code that's harder for others to read. É preciso colocar a máxima "goto considered harmful" na perspectiva histórica adequada. Acredito que praticamente todo programador trein...