quarta-feira, 8 de dezembro de 2010

Vídeo: O som de alguns algoritmos de ordenação

Cada ordenação é realizada num array contendo notas musicais em som MIDI. Quando dois valores são comparados, é feita a conversão dos valores em tons e os mesmos são tocados.

 

Faltaram alguns clássicos como HeapSort, ShellSort e QuickSort, mas ficou bem legal. Detalhes aqui.

quarta-feira, 17 de novembro de 2010

Bons frameworks C++ para criação automática de mocks

Frameworks para criação automática de objetos substitutos (“mocks”) em C++ foram evoluindo num passo muito mais lento que os para Java, C# ou mesmo Ruby. A dificuldade de criar uma boa arquitetura para simulação da criação automática sem perder a portatilidade entre compiladores e também a falta de desenvolvedores para melhorar os frameworks contribuiram para esse atraso.

Ainda há poucos frameworks altamente portáteis e, menos ainda, estáveis, mas alguns funcionam bem para a maioria dos casos. Há projetos bem estáveis e portáteis, como Google Mock ou MockPP, mas que não abrangem a criação automática de objetos substitutos. Neles, é preciso criar uma classe substituta (implementando a interface que se deseja simular) e usar macros para gerar os métodos.

Os frameworks de geração automática em C++ geralmente exploram o modelo de representação interna (layout ABI) de classes usado em um determinado compilador. Combinado ao uso de templates e algumas técnicas como Fast Delegates, é feito a geração do código na hora da declaração. Como o uso da representação interna varia de compilador para compilador (se bem que em muitos a representação é parecida), alguns projetos deixam de adicionar a compatibilidade com determinados compiladores - até pelo fato de faltar desenvolvedores interessados (ou habilitados) em fazer a adaptação.

Exemplo base

Para exemplificar o uso dos frameworks, tomarei por base o simples código abaixo:

class Piada
{
public:
Piada(const string &autor, const string &texto)
: _autor( autor ), _texto( texto )
{
}
// ... gets e sets
private:
// ... ctor, dtor, ops...
string _autor;
string _texto;
};


// Piada retirada de: http://confiar.atspace.com/curtas_boas.htm
const Piada PIADA_TOSCA( "Desconhecido",
"Você conhece a piada do fotógrafo ? Ainda não foi revelada." );


class RepositorioPiada
{
public:
virtual ~RepositorioPiada() {};
// ...
virtual bool Existe() const = 0;
virtual void Adicionar(const Piada &piada) = 0;
virtual void Alterar(const Piada &piada) = 0;
// ...
};

class ServicoPiada
{
public:
ServicoPiada(RepositorioPiada &repositorio)
: _repositorio( repositorio )
{
}
//...
void Salvar(const Piada &piada)
{
if ( _repositorio.existe( piada ) )
_repositorio.alterar( piada );
else
_repositorio.adicionar( piada );
}
private:
//...
RepositorioPiada _repositorio;
};


Na maioria dos exemplos será feito o uso do UnitTest++, com o qual todos são compatíveis.


Isolator ++


Começando por uma opção paga, lançada mês passado, o Isolator ++ possui uma característica extremamente rara em relação aos concorrentes: permite criar substitutos para classes concretas, métodos não virtuais e métodos estáticos. Além disso, funciona em conjunto como alguns frameworks de teste populares: Google Test, UnitTest++, Yaffut, CPPUnit e Boost::Test.


Porém, ele tem algumas limitações consideráveis, na versão avaliada durante esse post: só funciona com o Visual Studio (2008 e 2010) e métodos abstratos (ex: virtual void FazAlgo() = 0;) devem ser declarados com a macro PURE do Visual Studio (ex: virtual void FazAlgo() = PURE;), algo que eles pretendem resolver na próxima versão. Assim, o Isolator ++ tem grandes vantagens e desvantagens importantes que limitam sua abrangência. Por ser um produto novo e pago, deve evoluir rapidamente, o que torna seu futuro promissor.

TEST_F(TesteServicoPiada, DeveSalvarUmaPiadaNova)
{
RepositorioPiada *rp = FAKE<RepositorioPiada>();
WHEN_CALLED(rp->Existe( PIADA_TOSCA )).ReturnVal( false );

ServicoPiada servico( rp );
servico.Salvar( PIADA_TOSCA );

ASSERT_WAS_CALLED( rp->Adicionar( PIADA_TOSCA ) );
ISOLATOR_CLEANUP();
}

Acima um teste realizado com o Isolator ++. Repare o uso de FAKE para criação do objeto substituto, WHEN_CALLED para criação da expectativa, ASSERT_WAS_CALLED para verificar a chamada de um método e ISOLATOR_CLEANUP para a destruição dos objetos do Isolator ++.


AMOP


AMOP (Automatic Mock Object for C++) é um projeto de código aberto que conheci há uns dois anos (logo assim que surgiu) e teve poucas atualizações de lá pra cá, o que o fez permanecer com alguns bugs inconvenientes. Ele possui suporte ao Visual Studio, ao GCC e ao  C++ Builder 2009 – sendo o suporte a este último uma contribuição minha ao projeto – e funciona em conjunto com UnitTest++.

TEST_F(TesteServicoPiada, DeveSalvarUmaPiadaNova)
{
MockObject< RepositorioPiada > mock;

mock.call( &RepositorioPiada::Existe )
.expect( PIADA_TOSCA )
.returning( false );

mock.call( &RepositorioPiada::Adicionar )
.expect( PIADA_TOSCA );

ServicoPiada servico( mock );
servico.Salvar( PIADA_TOSCA );

mock.verify();
}

Acima um teste realizado com o AMOP. Repare uso de MockObject parametrizado para a criação do objeto substituto, do método call para criação da expectativa e do método verify para a verificação das expectativas criadas. O método verify não necessita ser chamado, pois é chamado automaticamente na destruição do objeto mock, que ocorre no fim do escopo do teste.


MockitoPP


O MockitoPP é a versão C++ do Mockito, projeto originalmente escrito em Java. De código aberto, é compatível com GCC e Visual Studio e pode ser usado com Google Test, o Hamcrest ou a Boost (regex). Venho usando exporadicamente há cerca de um ano e sinto que a versão atual apresenta poucos problemas (no GCC, era preciso colocar o operador < em algumas classes).

TEST(TesteServicoPiada, DeveSalvarUmaPiadaNova)
{
mock_object< RepositorioPiada > mock;

mock( &RepositorioPiada::Existe )
.when( PIADA_TOSCA )
.thenReturn( false );

mock( &RepositorioPiada::Adicionar )
.when( PIADA_TOSCA )
.thenReturn();

RepositorioPiada &rp = mock.getInstance();

ServicoPiada servico( mock );
servico.Salvar( PIADA_TOSCA );

ASSERT_TRUE( mock.verify( &RepositorioPiada::Existe ).exactly( 1 ) );
ASSERT_TRUE( mock.verify( &RepositorioPiada::Adicionar ).exactly( 1 ) );
}

Acima um teste realizado com MockitoPP. Repare o uso mock_object parametrizado para a criação do objeto substituto, do método when para criação da expectativa e do método verify para a verificação das expectativas criadas. Como pode ser visto, sua sintaxe é bem parecida com a AMOP, porém tendo mais opções de verifição de comportamento (veja a documentação no site).


HippoMocks


O HippoMocks é outro projeto de código aberto, compatível com GCC, Visual Studio e Comeau. O framework de testes usado pelo HippoMocks é o Yaffut, que reune as qualidades dos outros frameworks de teste (veja os detalhes na página do Yaffut). No que visto há cerca de um ano, tem boa estabilidade, sendo ligeiramente melhor que a do MockitoPP.

FUNC( DeveSalvarUmaPiadaNova )
{
MockRepository mocks;
RepositorioPiada *mock = mocks.InterfaceMock< RepositorioPiada >();

mocks.ExpectCall( mock, RepositorioPiada::Existe )
.With( PIADA_TOSCA )
.Return( false );

mocks.ExpectCall( mock, RepositorioPiada::Adicionar )
.With( PIADA_TOSCA );

ServicoPiada servico( mock );
servico.Salvar( PIADA_TOSCA );
}

Acima um teste realizado com HippoMocks. Repare que o objeto substituto é criado através do método InterfaceMock, da classe MockRepository. As espectativas são criadas usando ExpectCall e, ao final, elas são verificadas automaticamente.


Comparativo


O comparativo a seguir é subjetivo, mas mostra uma percepção prática dos frameworks em relação aos critérios julgados relevantes na escolha. A nota varia entre 1 e 4, sendo 1=Ruim, 2=Razoável, 3=Bom e 4=Ótimo.


comparacao


Pelo fato da estabilidade, pessoalmente, dou preferência ao uso de HippoMocks. Apesar disto, gosto de poder usar os matchers do Hamcrest, que já vem “nativos” no MockitoPP.


Ficaram de fora da análise alguns frameworks que ainda não experimentei, mas parecem interessantes: M0cxx0r e MockItNow. Caso você conheça algum outro, por favor deixe seu comentário.

domingo, 7 de novembro de 2010

premake é uma mão na roda

Pra você que desenvolve código multiplataforma ou quer manter seu projeto compatível com diferentes compiladores e IDEs, o premake pode poupar algumas horas de trabalho. Com ele, não é preciso manter arquivos make sincronizados ou ter que ter todos os ambientes configurados no seu computador para poder testá-los.

O Premake é uma ferramenta de configuração de build compatível com as linguagens C, C++ e C# que gera o arquivo de projeto para MS Visual Studio (2002, 2003, 2006, 2008 e Express), GNU Make, Cygwin, MinGW, Apple XCode, CodeBlocks, CodeLite, SharpDevelop e MonoDevelop. Na versão experimental 4.1, permite builds para 32 e 64 bits em Windows, GNU/Linux, Mac OS X, Playstation 3 e Xbox 360.

image

O Premake também possui um ambiente para scripts Lua, linguagem na qual ele foi escrito e interpreta. Por exemplo, o seguinte script poderia ser usado para gerar um projeto C++ para modo console, com as configurações de build para debug e release:


-- Uma solução contém projetos e define as configurações disponíveis para eles
solution "MyApplication"
configurations { "Debug", "Release" }

-- Um projeto configura o build e define os parâmetros para as configurações
project "MyApplication"
kind "ConsoleApp"
language "C++"
files { "**.h", "**.cpp" }

configuration "Debug"
defines { "DEBUG" }
flags { "Symbols" }

configuration "Release"
defines { "NDEBUG" }
flags { "Optimize" }


Veja o User Guide para mais informações.


Outro uso útil é quando temos um projeto opensource interessante que não possui o arquivo de projeto para um determinado IDE. Assim, podemos dar uma olhada em como está o arquivo make do projeto e criar um script Lua para que o Premake crie o arquivo de projeto. Muito mais simples que passar horas criando o arquivo de projeto na mão…

domingo, 31 de outubro de 2010

BD: ON DELETE com RESTRICT ou NO ACTION ?

Essa é uma pergunta que ouço algumas vezes de alunos ao definir a propagação de exclusão. Vamos entender as implicações.

Ao definir o tipo de integridade referencial entre tabelas de um banco de dados, definimos a propagação de atualizações de uma tabela pai em tabelas filhas tanto para atualização quanto para exclusão de registros. Nesta definição de propagação (que aceita CASCADE, RESTRICT, NO ACTION, SET NULL ou SET DEFAULT, na maioria dos bancos de dados), há uma diferença singela entre o uso de RESTRICT e de NO ACTION, que por muitas vezes leva à certa confusão.

Na propagação de uma atualização (UPDATE) é fácil visualizar a diferença. Ao usar NO ACTION, se um registro na tabela pai se atualizar, as chaves estrangeiras nos registros na tabela filha não serão atualizados (nenhuma ação mesmo, literalmente) e o comando não irá falhar se (e somente se) a integridade referencial se mantiver, ou seja, se a chave estrangeira continuar referenciando um registro existente na tabela pai. Ao usar RESTRICT, simplesmente a mudança de valor é restringida (impedida), fazendo com que a atualização na tabela pai falhe.

Na proparação de uma exclusão (DELETE), ao usar RESTRICT, a exclusão de um registro pai será impedida se houverem registros filhos. Agora, ao usar NO ACTION, será aguardado para ver o que ocorre com a integridade referencial. Se não se mantiver - o que parece sensato, pois se está excluindo o registro pai, fazendo com que os filhos fiquem órfãos - o comando irá falhar. Um possível caso dos registros não ficarem órfãos ocorreria se fosse rodado um TRIGGER que ajustasse a chave estrangeira nos registros filhos para registros existentes na tabela pai. Como esse caso é raro, pois são poucos os casos práticos onde se necessita efetuar este ajuste, a maioria dos DELETEs com NO ACTION irão falhar, fazendo com que seu comportamento (na prática) se assemelhe com o do RESTRICT, visto que ambos irão impedir a exclusão do registro pai.

Portanto, apesar da semelhança prática entre  RESTRICTNO ACTION para propagações de exclusão, é importante saber diferenciá-las para realizar uma escolha acertada. Na maioria dos casos, RESTRICT pode ser a escolha mais adequada. Porém, provavelmente por questões de performance – para não precisar efetuar a checagem dos valores, mas só da integridade, ao final – os bancos de dados vem como a opção NO ACTION como a default.

sábado, 30 de outubro de 2010

SVN: Mudando o local do repositório na cópia de trabalho

Quando temos um repositório num servidor que não tem um IP fixo, por exemplo, nossa cópia de trabalho (working copy) continua referenciando o último IP utilizado.

Isto faz com que, ao efetuarmos um commit, haja falha no envio dos dados já que, obviamente, o servidor não existe mais naquele endereço (IP).

Para resolver este problema, simplesmente podemos usar o comando:

svn switch --relocate <repositório_antigo> <repositório_novo>

Por exemplo:

svn switch --relocate http://192.168.1.76/projeto/trunk http://192.168.1.41/projeto/trunk

Com o Tortoise SVN, isto pode ser feito através da opção Relocate, que abrirá uma janela onde podemos fornecer o local do repositório.

Restart

Trabalho, pós, mestrado… muitas coisas tiveram sua participação no tempo em que fiquei sem escrever neste blog. Não estou em situação diferente que estava, mas apesar disto, estou disposto a tentar escrever um post aqui e outro acolá, mantendo uma certa regularidade.

Time will tell…

Agradeço aos leitores, seguidores e alunos pelo incentivo, sugestões e dúvidas, já que estas sempre servem de inspiração para novos posts.

sábado, 6 de fevereiro de 2010

Symbian agora totalmente opensource

Antes o kernel e agora toda a plataforma. A Symbian Foundation decidiu abrir todo o código-fonte para os desenvolvedores. É certo que muitas das ferramentas necessárias para a compilação e construção do sistema operacional ainda são proprietárias, mas com a ajuda da comunidade de desenvolvedores, esta migração será questão de tempo.

Ao mer ver, a abertura do código-fonte é parte da estratégia para manter a hegemonia do mercado e conter o recente avanço da plataforma aberta Android, da Google, além do uso do Microsoft Windows Mobile em smart phones.

A Symbian já prepara a liberação das versões 3 e 4 de seu sistema operacional, com olho nas interfaces multi-toque. Outra iniciativa é a Symbian Horizon, que pretende facilitar o acesso às informações referentes à plataforma, a aprovação de aplicações, a tradução e seu marketing. Uma aplicação poderá, por exemplo, ser publicada em diversas lojas, como a Nokia Ovi Store, Samsung Application Store, Sony Ericsson PlayNow Arena e AT&T Media mall. Talvez seja uma possível retaliação à plataforma iPhone e sua AppStore.

 

Symbian:  http://developer.symbian.org
Android:  http://source.android.com/
iPhone:  http://developer.apple.com/iphone/
Windows Mobile:

 http://developer.windowsmobile.com/