Google Analytics

quinta-feira, 26 de abril de 2012

Esqueletos

Eu gosto de usar esqueletos, ou seja, aqueles arquivos de modelo. Eles me ajudam a lembrar de coisas que normalmente precisamos e também deixam o código com uma cara homogênea.

Mas eu não gosto de esqueletos mortos, daqueles que estão estáticos e não podem ser mudados, ou que são iguais para todos os projetos. Gosto de esqueletos vivos, que evoluem, sendo customizados para cada projeto conforme vão sendo usados e gosto também que eles estejam versionados no sistema de versionamento DO PROJETO EM QUESTÃO.

Costumo usar os seguintes esqueletos:

header:
//*****************************************************************************
// Version/Revision log :
// ====================
//$Log: $
//
//*****************************************************************************

#ifndef TODO_NAME_H_INCLUDED
#define TODO_NAME_H_INCLUDED

//################################# Includes ##################################


//################################## Types ####################################


//################################ Constants ##################################


//############################## Public Variables #############################


//############################# ForwardDeclarations ###########################


//################################# Classes ###################################


//####################### Public Function Declarations ########################


#endif // TODO_NAME_H_INCLUDED

//*************************    E n d   F i l e    *****************************



cpp:

//*****************************************************************************
// Version/Revision log :
// ====================
//$Log: $
//
//*****************************************************************************

//################################# Includes ##################################
#include "TODO_NAME.h"


//################################## Types ####################################


//######################### Private Classes Declaration  ######################


//################################ Constants ##################################


//############################# Private Variables #############################


//############################## Public Variables #############################


//############################# Private Functions #############################


//###################### Private Classes Implementation #######################


//###################### Public Classes Implementation ########################


//##################### Public Functions Implementation #######################


//-------------------------------------------------
// Unit tests
//-------------------------------------------------
#ifdef UNIT_TESTS
#include <iostream>
#include <assert.h>
using namespace std;

int main ()
{
    cout << "Unit Testing for TODO_NAME class" << endl;
    Logger::logTostdout();
       
    cout << "Testing TODO" << endl;
    {
        assert(!"No UNITTESTS implemented yet!");
    }

    cout << ">>>>>> Tests finished with no errors" << endl;
    return 0;
}

#endif

//*************************    E n d   F i l e    *****************************


declaração de classe:
//=============================================================================
// Class: TODO_NAME
// Remarks: TODO
//=============================================================================
class TODO_NAME
{
public:
    TODO_NAME() {}
    ~TODO_NAME() {}
private:
    TODO_NAME(const TODO_NAME& x);    // Hide copy ctor to avoid implicit calling
    TODO_NAME& operator=(const TODO_NAME& x);    // Hide = operator to avoid implicit calling
}; // End of class TODO_NAME



Implementação de clase:
//=============================================================================
// Class: TODO_NAME
//=============================================================================
//-----------------------------------------------------------------------------
// Method: TODO
// Remarks: TODO
//-----------------------------------------------------------------------------
void TODO_NAME::TODO(TODO)
{
    //TODO
}


Makefile:
#*****************************************************************************
# Version/Revision log :
# ====================
#$Log: $
#
#*****************************************************************************
all:
    <<TODO: Implement all target to build your items in the current dir

clean:
    <<TODO: Implement clean target that undoes what all does...

install:
    <<TODO: Implement install target that installs the itens created by all to the correct places.

uninstall:
    <<TODO: Implement clean target that undoes EVERYTHING that install does...


.PHONY: all clean install uninstall



quinta-feira, 5 de abril de 2012

Uso robusto do default em switches

Noto uma grande tendência a usarem o default do switch para economizar digitação.

Por exemplo:

O cara tem 8 possíveis valores e tem de fazer o mesmo processamento para 6 deles; então coloca 2 cases e cobre os 6 comuns com um default. 

enum {V1, V2, V3, V4, V5, V6, V7, V8} v;
...

switch (v)
{
case V1:
  processV1();
case V2:
  processV2();
 
default:
  process3to8();
}

Funciona no dia em que é implementado. Mas é um pesadelo para manutenção. No dia que alguém incluir novos valores na enumeração, o novo valor vai ser "automagicamente" considerado como devendo ter o mesmo comportamento que o V3 a V8 tinham. O que pode estar errado. Coisas que acontecem automagicamente não são boas em programação. Por isso é uma boa prática se colocar os valores explicitamente e usar o default para logar erro indicando um valor inesperado (e, de preferência, abortar o programa imediatamente):

enum {V1, V2, V3, V4, V5, V6, V7, V8} v;
...


switch (v)
{
case V1:
  processV1();

case V2:
  processV2();

case V3:
case V4:
case V5:
case V6:
case V7:
case V8:
  process3to8();
default:
  /* Logar uma mensagem indicando que um valor inesperado ocorreu na variável v, da função tal... */
/* Sempre que possível, abortar o programa nesta situação. */
}


O ponto é que o default deve ser usado para expressar: Faça isto para todos os outros valores possíveis (tanto conhecidos quanto os desconhecidos). E não é bom programar para fazer coisas com valores desconhecidos.

O mesmo fenômeno ocorre para o else em trechos do tipo:

enum {V1, V2, V3, V4} v;
...
v = getValueFromSomewhere()

if(v== V1|| v==V2)
  doSomething()
else //Tratando o V3 e V4 como genérico (isto não é bom...)
  doOtherStuff();


Melhor seria usar algo como:

if (v== V1|| v==V2)
{
  doSomething()
 }
else if (v== V3 || v == V4)
{

   doOtherStuff();
 }
else
{
  DebugLog <<"Unexpected value received  in function XXXX. Value is " << v;
  abort();
}

Era isto.