Planet C e C++ Brasil

June 28, 2008

Otavio Rodolfo Piske

Modelagem UML Genérica

Recebi do meu amigo Bruno, lá de Chapecó, essa excelente modelagem UML genérica que se aplica a qualquer aplicação. Agora você não precisa mais perder tempo fazendo a análise e design. É só seguir os diagramas abaixo e tudo ficará bem.

Diagrama de Classes

Diagrama de Classes

Diagrama de Sequência

Diagrama de Sequência

Diagrama de Casos de Uso

Diagrama de Casos de Uso

by angusyoung at June 28, 2008 08:49 PM

June 24, 2008

Fábio Galuppo

Two versions for Lock-Free Stack

 
The two codes below are implementations of lock-free stack structure.
The purpose is the same, but the first version can be ported easily to other OSes.
Performance benchmark is a lesson to the reader.
 
The raw C++ implementation. In C++0x we'll change Interlocked APIs to atomic operations:
 
template
<typename T>
struct
LockFreeStack
{
  LockFreeStack() : Head_( NULL ){}

  void Push( T& value )
  {
    PNODE node = new NODE( value );
    PNODE oldHead;

    do
    {
      node->Next = oldHead = Head_;
    }while( oldHead != reinterpret_cast<PNODE>( InterlockedCompareExchangePointer(
                                      reinterpret_cast<volatile PVOID*>(&Head_), node, oldHead )));
  }

  T Pop()
  {
    PNODE node;
    PNODE oldHead;

    do
    {
      oldHead = Head_;
    }while( oldHead != (node = reinterpret_cast<PNODE>( InterlockedCompareExchangePointer(
                                            reinterpret_cast<volatile PVOID*>(&Head_), Head_->Next, oldHead ))));

    T temp = node->Data;
    delete node;
    return temp;
  }

private
:
  typedef struct NODE_TAG
  {
    NODE_TAG( T value ) : Next(NULL), Data(value){}
    NODE_TAG* Next;
    T Data;
  } NODE, *PNODE;

  PNODE Head_;

  LockFreeStack( const LockFreeStack& ){}
  LockFreeStack& operator=( const LockFreeStack& ){}
};


Using a Windows API facility called Singly Linked Lists

template<typename T>
struct LockFreeStack
{
  LockFreeStack()
  {
    Head_ = new_aligned<SLIST_HEADER>();
    if( NULL == Head_ )
      throw "memory allocation failed";

    InitializeSListHead( Head_ );
  }

  ~LockFreeStack()
  {
    InterlockedFlushSList( Head_ );
  }

  void Push( T value )
  {
    Item_ = new_aligned<ITEM>();
    if( NULL == Item_ )
      throw "stack is full";

    Item_->Value = value;
    Entry_ = InterlockedPushEntrySList( Head_, &Item_->Entry );
  }

  T Pop()
  {
    PSLIST_ENTRY tempEntry = InterlockedPopEntrySList( Head_ );
    if( NULL == tempEntry )
      throw "stack is empty";

    Item_ = reinterpret_cast<PITEM>( tempEntry );
    T temp = Item_->Value;
    _aligned_free( tempEntry );
    return temp;
  }

private
:
  typedef struct ITEM_TAG
  {
    SLIST_ENTRY Entry;
    T Value;
  } ITEM, *PITEM;

  PSLIST_ENTRY Entry_;
  PSLIST_HEADER Head_;
  PITEM Item_;

  template <class M> M* new_aligned()
  {
    return reinterpret_cast<M*>(_aligned_malloc( sizeof(M), MEMORY_ALLOCATION_ALIGNMENT ));
  }

  LockFreeStack( const LockFreeStack& ){}
  LockFreeStack& operator=( const LockFreeStack& ){}
};

 

June 24, 2008 11:09 AM

June 21, 2008

Rodrigo Strauss

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> <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;
}

1 comentário(s)

June 21, 2008 07:42 PM

June 13, 2008

Fábio Galuppo

Samples from my Concurrency Talk at Seminário TempoReal C++ Portabilidade e Performance

  
Samples from my Concurrency Talk at Seminário TempoReal C++ Portabilidade e Performance:
The following code is a C++ port to one of my previous samples: MapReduce with Parallelspace
 
#include
<iostream>
#include
<fstream>
#include
<vector>
#include
<map>
#include
<string>
#include
<boost/thread/thread.hpp>
#include
<boost/thread/mutex.hpp>
#include
<boost/filesystem.hpp>
#include
<boost/function.hpp>
#include
<boost/algorithm/string.hpp>
 
using
namespace std;
using
namespace boost;
using
namespace boost::filesystem;
 
void
get_files( const path& directory, vector<path>& files )
{
  directory_iterator end_iter;
  for( directory_iterator iter( directory ); iter != end_iter; ++iter )
  {
    if( is_directory( iter->status() ) ) continue;
    files.push_back( iter->path() );
  }
}
 
int
get_ProcessorCount(){ return 2; }
 
class TaskExecutor
{
typedef function2<void, map<string, int>&, const map<string, int>&> ConsolidationFunctionType;
typedef function1<map<string, int>, const path&> CountWordsFunctionType;

public:
  TaskExecutor( vector<path>::const_iterator begin, vector<path>::const_iterator end, 
                      const CountWordsFunctionType countWords,
                      const ConsolidationFunctionType consolidation,
                      map<string, int>& result ) : Begin_( begin ), 
                      End_( end ),
                      CountWordsFunction_( countWords ),
                      ConsolidationFunction_( consolidation ),
                      Result_( result ){}

  void operator()()
  {
    for( vector<path>::const_iterator iter = Begin_; iter != End_; ++iter )
      ConsolidationFunction_( Result_, CountWordsFunction_( *iter ) );
  }
 
private
:
  vector<path>::const_iterator Begin_, End_;
  const ConsolidationFunctionType ConsolidationFunction_;
  const CountWordsFunctionType CountWordsFunction_;
  map<string, int>& Result_;
};
 
void
key_count( map<string, int>& dictionary, const string& word, int value )
{
  dictionary[ word ] = dictionary.end() != dictionary.find( word ) ? dictionary[ word ] + value : value;
}
 
map<string, int> map_function( const path& filename )
{
  map<string, int> wordCount;

  ifstream file;
  const int MAXLEN = 1024;
  char line[ MAXLEN ];

  file.open( filename.directory_string().c_str() ); 
 
  while( file.getline( line, MAXLEN ) )
  {
    vector<string> split_v;
    split( split_v, string( line ), is_any_of( " " ) );
    for( vector<string>::const_iterator iter = split_v.begin(); iter != split_v.end(); ++iter )
      key_count( wordCount, *iter, 1 );
  } 
 
  file.close();
  return wordCount;
}
 
void
reduce_function( map<string, int>& destination, const map<string, int>& source )
{
  for( map<string, int>::const_iterator iter = source.begin(); iter != source.end(); ++iter )
  key_count( destination, iter->first, iter->second );
}
 
void
wordcount_mapreduced( const vector<path>& files, map<string, int>& wordCount )
{
  //data partition
  int numberOfPartitions = get_ProcessorCount() * 2; //2 threads per core
  int numberOfFiles = static_cast<int>( files.size() );
  if( numberOfFiles < numberOfPartitions ) numberOfPartitions = numberOfFiles;
  int delta = numberOfFiles / numberOfPartitions;
  vector< map<string, int> > result( numberOfPartitions );
  thread_group tg;
  //for parallel
  for( int step = 0; step < numberOfPartitions; ++step )
  {
    vector<path>::const_iterator begin = files.begin() + delta * step,
    end = numberOfPartitions - 1 == step ? files.end() : files.begin() + delta * step + delta;
    //fork
    tg.create_thread( TaskExecutor( begin, end, map_function, reduce_function, result[step] ) );
  }
 
  //join
  tg.join_all();
  for( vector< map<string, int> >::const_iterator iter = result.begin(); iter != result.end(); ++iter )
    reduce_function( wordCount, *iter );
}
 
int
main()
{
  vector<path> files;
  get_files( "c:\\txttest\\", files );
  map<string, int> wordCount;

  wordcount_mapreduced( files, wordCount );

  for( map<string, int>::const_iterator iter = wordCount.begin(); iter != wordCount.end(); ++iter )
    cout << iter->first << " = " << iter->second << endl;
}

June 13, 2008 01:11 PM

June 08, 2008

Rodrigo Delduca

Instalador da boost

Para quem ainda tem dificuldades para compilar a biblioteca boost, eu encontrei um instalador que baixa os pacotes dependendo de suas escolhas (multithreaded, static, debug, etc) sem contar que nos polpa de ter que esperar pela compilação, pena que é só para o Visual C++.

O instalador pode ser baixado aqui, mais informações em http://www.boost-consulting.com/products/free

by SKHAZ at June 08, 2008 07:55 PM

June 06, 2008

Wanderley Caloni

Declaração x definição

Uma diferença que eu considero crucial na linguagem C/C++ é a questão da declaração/definição (em inglês, declaration/definition). É a diferença entre esses dois conceitos que permite, por exemplo, que sejam criadas estruturas prontas para serem conectadas a listas ligadas:

struct Element
{
   int x;
   int y;
   Element* next; /* olha eu mesmo aqui! */
};

Por outro lado, e mais importante ainda, é ela que permite que as funções sejam organizadas em unidades de tradução (cpps) distintas para depois se unirem durante o link, mesmo que entre elas exista uma relação de dependência indissociável:

cdepends.gif

Existem diversas formas de entender esses dois conceitos. Eu prefiro explicar pela mesma experiência que temos quando descobrimos a divisão hardware/software:

  • Hardware é o que você chuta
  • Software é o que você xinga

Exatamente. Hardware é algo paupável, que você pode até chutar se quiser. Por exemplo, a sua memória RAM! No entanto, software é algo mais abstrato, que nós, seres humanos, não temos a capacidade de dar umas boas pauladas. Portanto, nos abstemos a somente xingar o maldito que fez o programa "buggento".

Da mesma forma, uma declaração em C/C++ nos permite moldar como será alguma coisa na memória, sem no entanto ocupar nem um mísero byte no seu programa:

int func(int x, int y, int z); /* tamanho em memória: zero bytes */
struct Teste
{
	char bufao[0x100000]; /* tamanho em memória: zero bytes */
	int intao[0xffffff];  /* tamanho em memória: zero bytes */
};
extern int x; /* tamanho em memória: adivinha! */

Por outro lado, a definição, o hardware da história, sempre ocupará alguma coisa na memória RAM, o que, de certa forma, permite que você chute uma variável (embora muitas outras também irão para o saco).

int func(int x, int y, int z) /* tamanho em memória:
{
	int ret = x + y + z; /* alguns _asm add + */
	return ret;          /* um _asm ret */
}
Teste tst; /* tamanho em memória: 0x100000 + 0xffffff * 4 = 1048576 bytes */
int x; /* tamanho em memória: sizeof(int) bytes */

Dessa comparação só existe uma pegadinha: uma definição também é uma declaração. Por exemplo, nos exemplos acima, além de definir func, tst e x, o código também informa ao compilador que existe uma função chamada func, que existe uma variável tst do tipo Teste e uma variável x do tipo int.

Informa ao compilador? Essa é uma outra ótima maneira de pensar a respeito de declarações: elas sempre estão conversando diretamente com o compilador. Por outro lado, nunca conversam diretamente com o hardware, pois ao executar seu código compilado, as declarações não mais existem. Foi apenas um interlúdio para que o compilador conseguisse alocar memória da maneira correta.

Complicado? Talvez seja, mesmo. Mas é algo que vale a pena fixar na mente. Isso, é claro, se você quiser ser um programador C/C++ mais esperto que os outros e resolver pequenos problemas de compilação que muitos perdem horas se perdendo.

Corolário

Então por que diabos a separação declaração/definição consegue definir coisas como listas ligadas, como no código acima? A resposta é um pouco ambígua, mas representa regra essencial na sintaxe da linguagem: após a definição do nome e do tipo de declaração envolvida podemos referenciá-la como declaração, ou seja, não ferindo a limitação de que não sabemos o tamanho de uma variável do tipo declarado. Dessa forma, é perfeitamente legal definirmos um ponteiro para uma estrutura que ainda não se sabe muita coisa, além de que é uma estrutura:

struct Estrutura; /* atenção: declaração apenas! */
Estrutura* st; /* ponteiro para declaração: não sabemos o tamanho ainda */

Dessa forma, o começo de uma definição de estrutura já declara o nome da estrutura antes de terminar a declaração do tipo inteiro. Bizarro, não? De qualquer forma, isso permite a construção clássica de lista ligada:

struct Estrutura /* a partir daqui Estrutura já está visível */
{
	Estrutura* st; /* recursividade? é apenas um ponteiro! */
};

Se vermos pelo lado prático, de qualquer forma seria impossível definir uma variável dentro dela mesma, pois isso geraria uma recursão infinita de definições, e, como sabemos, os recurso da máquina são finitos.

by Wanderley Caloni at June 06, 2008 12:27 PM

June 05, 2008

Rodrigo Delduca

Agendamento de Tarefas

Essa é minha implementação de um pequeno sistema de agendamento de tarefas, que futuramente usarei em um outro projeto, achei que ficou legal então resolvi postar. Vamos à implementação

task.hpp

#ifndef _task_hpp
#define _task_hpp

#include <ctime>
#include <boost /function.hpp>
#include </boost><boost /bind.hpp>

class task
{
	public:
		explicit task(const boost::function<void ()>& callback)
		: _call_back(callback)
		, _call_count(0)
		, _total_time_used(0)
		{
		}

		virtual ~task() { }

		virtual void before_task() { }

		void do_task()
		{
			before_task();
			_call_back();
			after_task();
		}

		virtual void after_task() { }

		void set_callback(const boost::function</void><void ()>& callback)
		{
			_call_back = callback;
		}

		const boost::function</void><void ()>& get_callback() const
		{
			return _call_back;
		}

		bool is_valid() const
		{
			return true;
		}

	private:
		boost::function</void><void ()> _call_back;
		std::size_t _call_count;
		std::clock_t _total_time_used;
};

#endif

schedule.hpp

#ifndef _schedule_hpp
#define _schedule_hpp

#include <algorithm>
#include <deque>
#include <boost /pool/pool_alloc.hpp>
#include </boost><boost /shared_ptr.hpp>
#include </boost><boost /noncopyable.hpp>
#include </boost><boost /thread/thread.hpp>
#include </boost><boost /thread/xtime.hpp>
#include </boost><boost /thread/mutex.hpp>

#include "task.hpp"

using std::for_each;
using std::deque;
using boost::shared_ptr;

class task;
typedef shared_ptr<task> task_ptr;
typedef deque<task_ptr , boost::pool_allocator<task_ptr> > deque_task;

class schedule : public boost::noncopyable
{
	public:
		schedule() : job(boost::bind(&schedule::thread_dispatcher, this))
		{
			_thread_sleep.sec = 10;

			try
			{
				job.join();
			}

			catch (boost::thread_interrupted&) { /* don't care... */ }
		}

		virtual ~schedule() { }

		void add_task(const shared_ptr<task>& _task)
		{
			boost::mutex::scoped_lock lock(monitor);

			deque_task::const_iterator it = std::find(_task_pool.begin(),
					_task_pool.end(), _task);

			if (it == _task_pool.end())
			{
				_task_pool.push_back(_task);
			}

			else {
				// duplicate item
			}
		}

		void remove_task(const shared_ptr</task><task>& _task)
		{
			boost::mutex::scoped_lock lock(monitor);

			deque_task::iterator it = std::find(_task_pool.begin(),
					_task_pool.end(), _task);

			if (it != _task_pool.end())
			{
				_task_pool.erase(it);
			}

			else {
				// does't exist
			}
		}

	protected:
		void thread_dispatcher()
		{
			for (;;)
			{
				for_each(_task_pool.begin(), _task_pool.end(),
					boost::mem_fn(&task::do_task));

				boost::thread::sleep(_thread_sleep);
			}
		}

	private:
		deque_task _task_pool;
		boost::thread job;
		boost::mutex monitor;
		boost::xtime _thread_sleep;
};

#endif

Unidade para testes, main.cpp

#include <iostream>
#include <boost /scoped_ptr.hpp>
#include </boost><boost /bind.hpp>

#include "schedule.hpp"

void f1()
{
	std::cout < < "f1()" << std::endl;
}

void f2()
{
	std::cout << "f2()" << std::endl;
}

int main()
{
	boost::scoped_ptr<schedule> _schedule(new schedule());

	shared_ptr<task> _task1 (new task(f1));
	shared_ptr</task><task> _task2 (new task(f2));

	_schedule->add_task(_task1);
	_schedule->add_task(_task1); // repetido, nao insere...
	_schedule->add_task(_task2);

	_schedule->start();

	return 0;
}

Agradecimentos

  • Daniel pela sugestão de usar fila e apenas uma thread ao invés de uma para cada tarefa e assim evitando busy-waiting
  • Alex pelos testes de compatibilidade com mingw e msvc (vide mem_fun e shared_ptr)

schedule.zip

by SKHAZ at June 05, 2008 08:18 PM

June 03, 2008

Wanderley Caloni

Resultado do Seminário CCPP

seminario.jpgAconteceu nesse fim-de-semana, como era previsto, o nosso primeiro Seminário CCPP Brasil, com direito a pessoas de todas as idades e origens, mas todas com algo em comum: a paixão e o interesse pelas linguagens-mestre do mundo da programação.

Começo esse artigo agradecendo a todos os que direta e indiretamente participaram para o sucesso do evento, entre eles os organizadores, o carro-chefe responsável por acordar o espírito C++ da galera no início do ano, os palestrantes e, claro, óbvio, toda a comunidade C++ que participou em corpo (vulgo hardware) e alma (vulgo software).

Termino a introdução fazendo uma minicrítica ao preço pago pelos participantes. Não que eu ache que seja muito, pelo contrário: dado o alto nível técnico das palestras, parece até mentira termos acesso a um evento com essa estrutura por tão pouco. Porém, o muito e o pouco são relativos, e ainda acredito que existam pessoas que não vão aos encontros por falta de recursos. Por isso mesmo vai um apelo para que nos futuros encontros tenhamos alguma forma de permitir às pessoas menos favorecidas de participar democraticamente dessa que é a expressão viva das linguagens C e C++ em nosso país.

Vamos às palestras!

Dicas e Truques de Portabilidade
Wanderley Caloni

Apresentação para baixar em PDF, PPT e ODP.

seminario-caloni.jpgÉ muito difícil analisar uma palestra feita por você mesmo. É mais difícil ainda quando essa palestra é a primeira de uma batelada de argumentações de alto nível técnico que seguiram o dia. Posso dizer, no entanto, que consegui o que queria quando fui para o evento: demonstrar as dificuldades e as facilidades de tornar um código portável, independente se entre sistemas operacionais, ambientes ou compiladores.

Foi visto primeiramente o que faz da portabilidade uma coisa difícil. Detalhes como sintaxe e gramática fazem toda a diferença quando o que se almeja é um código limpo de imperfeições trazidas pelo ambiente de desenvolvimento. Também foi dada especial atenção às famigeradas extensões de compiladores, que fazem a linguagem parecer uma coisa que não é.

Por fim, foram apresentadas algumas sugestões movidas pela experiência e estudo dessas mesmas dificuldades. Para ilustrar, dois exemplos bem vivos de como um código portável deve se comportar, tanto no código-fonte quanto em sua documentação.

Programação Concorrente com C++
Fábio Galuppo

Artigo sobre apresentação

seminario-galuppo.jpgPara quem está acostumado com os temas geralmente "gerenciados" de Fábio Galuppo com certeza deve ter se surpreendido com a descrição teórica dos inúmeros problemas que cercam a vida do programador multithreading. O palestrante partiu do mais simples, o conceito de threads, conceito que, segundo ele mesmo, pode ser explicado em 15 minutos, para algo mais sutil e que gera muitos erros escondidos: o conceito de locks (semáforos, mutexes, etc).

Os programadores em nível de sistema devem ter adorado o contexto histórico dos problemas (você sabia que o primeiro lock inventado foi o semáforo?) tanto quanto o contexto teórico (explicação sobre modelo de memória).

Um destaque especial foram os experimentos com código rodando de verdade no Visual Studio, como o exemplo que tenta criar o maior número de threads possível na arquitetura 64. Simplesmente assustador!

Se por um lado faltou tempo para explicar os usos e princípios das bibliotecas de programação paralela disponíveis e mais usadas do mercado, por outro a palestra preencheu uma lacuna importante na minha primeira palestra sobre threads em C++, demonstrando os erros mais comuns e o que não se deve fazer em programas que rodam mais de uma thread.

Mais uma vez voltando à teoria, a palestra foca mais uma vez em bons princípios de design, como o padrão de projeto monitor e a descrição dos modelos onde é justificado o uso de mais de uma thread no programa.

Programação Multiplataforma Usando STL e Boost
Rodrigo Strauss

seminari-strauss.jpgComo sempre, Strauss está apaixonado pelo Boost (e a STL). Descrevendo as partes mais importantes que todo programador C++ moderno deve saber sobre essas bibliotecas, ambas modernas, a palestra focou principalmente no uso do dia-a-dia, e as vantagens produtivas que o C++ atual pode ter sobre o velho e tradicional programa em C com listas encadeadas artesanais.

Entre as coisas mais importantes citadas, que todo programador do novo século deveria saber, estão:

  • A total falta da necessidade de desalocarmos objetos manualmente em nossos programas, visto que o auto_ptr (STL) e shared_ptr (Boost) dão conta do recado de maneira impecável.
  • A total falta da necessidade de usarmos aqueles velhos arrays em C que quase nunca sabemos o tamanho exato para guardar nossos valores (e que continuamente colocávamos com o tamanho 100, MAX_PATH, ou UM_OUTRO_DEFINE_COMUM_EM_LINUX). A classe boost::array provê todas as funcionalidades básicas, além das avançadas, do uso de arrays tradicionais, sem qualquer overhead adicional de um array em C.
  • A total falta de necessidade de ficar convertendo strings e inteiros. Com a ajuda da classe std::string e de construções geniais como lexical_cast (Boost), felizmente podemos deixar nossas velhas funções que precisavam de um buffer, como _itoa (embora não-padrão).

Enfim, para quem pôde ver, a palestra focou nos princípios que farão hoje em dia um programador C++ completo, profissional e que, como seus colegas de outras linguagens, se preocupa igualmente com a produtividade de seu código. Ah, sim, e não gosta nem um pouco de reinventar a roda.
Técnicas de Otimização de Código
Rodrigo Kumpera & André Tupinambá

seminario-otimizacao1.jpgAparentemente o que pensei que seria, em minha sincera opinião, um desastre (dois palestrantes falando sobre a mesma coisa) se transformou em uma combinação estupenda de teoria e prática aplicadas à arte de otimização de código. Rodrigo e André conseguiram destrinchar o tema harmoniosamente, sempre dividido entre técnicas avançadas (algumas demonstradas pela experiência dos palestrantes) e teoria disciplinar, que visa alertar o wannabe que otimizar pode ser uma coisa boa; porém, preste atenção aos que já fizeram isso têm a dizer.

seminario-otimizacao2.jpgCom uma didática impecável, o novato nesse tema (como eu) pôde ver as dificuldades de conseguir determinar o objetivo de todo otimizador de código que, segundo eles, deve estar sempre atento na máxima de que "toda otimização é na verdade uma troca". Ou seja, se o programador quer melhor processamento, pagará com memória, se quiser otimizar espaço na RAM, irá gastar mais com processamento e/ou disco, e assim por diante.

Foram apresentados exemplos reais de otimização, além de dicas muito importantes sobre o comportamento das compilaçõe de cada dia. Você sabia, por exemplo, que ao declarar em escopos mais locais suas variáveis usadas apenas em pequenos trechos de código, estará dando uma poderosa dica ao compilador para que ele consiga usar os registradores no máximo de sua capacidade?

Conclusão: estamos indo de bem a melhor!

Ao final, como é de praxe, tivemos um sorteio de ótimos livros sobre programação e C++ em geral, com destaque aos livros do Herb Sutter. Rodrigo Strauss, conhecido fundador dos encontros, recebeu sua mais que merecida homenagem ao receber um de seus livros autografados. É o mais novo MVP da comunidade!

E por falar em comunidade, e agora podemos ver claramente, estamos com uma força bem maior do que no início do ano. A seqüência de ótimos eventos, além de nossos mestres do conselho Jedi de programadores C++, prova finalmente que, se depender da qualidade dos desenvolvedores, o Brasil pode sim ser uma poderosa fonte de programas de qualidade que façam coisas bem mais interessantes do que acessar um banco SQL. Nós já temos a matéria-prima.

Mais linques sobre o evento

Imagens do evento cedidas por Fernando Roberto (valeu, Ferdinando!).

by Wanderley Caloni at June 03, 2008 12:17 AM

May 31, 2008

Otavio Rodolfo Piske

Dica: Corrigindo erro da user32.lib no CMake com NMake Makefiles

No CMake, quando se usa o gerador NMake Makefiles ele costuma dar o erro:

LINK : fatal error LNK1104: cannot open file 'user32.lib'
LINK Pass 1 failed.  with 2
NMAKE : fatal error U1077: ‘”C:\Arquivos de programas\CMake
2.6\bin\cmake.exe”‘ : return code ‘0xffffffff’
Stop.
NMAKE : fatal error U1077: ‘”C:\Arquivos de programas\Microsoft Visual
Studio 8\VC\BIN\nmake.exe”‘ : return code ‘0×2′
Stop.

Mesmo que você tenha o Microsoft Platform SDK, isso ocorre porque o linker não consegue encontrar a user32.lib. Para projetos que usam o gerador do Visual Studio você pode configurar isso seguindo as recomendações da configuração do SDK, mas para resolver este problema em projetos que usam a NMake, você precisa adicionar o path da user32.lib na variável de ambiente LIB. Você pode fazer isso na mão, após iniciar o prompt de comando do Visual Studio:

set LIB=%LIB%;"C:\Arquivos de programas\Microsoft Platform SDK\Lib"

Ou então adicionar o path nas variáveis de ambiente do sistema através da aba “Avançado” nas propriedades do sistema.

by angusyoung at May 31, 2008 05:59 PM

May 28, 2008

Rodrigo Delduca

deque + shared_ptr + for_each + mem_fun = cabumm

Recentemente tive problemas com mem_fun e shared_ptr em um loop for_each

Os elementos

typedef shared_ptr<task> task_ptr;
typedef deque<task_ptr , pool_allocator<task_ptr> > deque_task_ptr;

Agora a combinação explosiva

for_each(_task_pool.begin(), _task_pool.end(),
       std::mem_fun(&task::do_task));

O problema está em usar std::mem_fun, ela não suporta chamar métodos de uma classe que estão em um shared_ptr, eu poderia ter usado bind, mais preferi manter o código, como a boost tem sua própria implementação de mem_fn ficando assim:

for_each(_task_pool.begin(), _task_pool.end(),
       boost::mem_fn(&task::do_task));

Resolvido, obrigado Alex por reportar o problema.

by SKHAZ at May 28, 2008 04:03 PM

May 21, 2008

Otavio Rodolfo Piske

IDE online

Meu amigo Diego me passou a dica sobre o CodeIDE. Segundo ele, é um site onde pode executar códigos, suporta várias linguagens como Pascal, C++, Perl, JavaScript, HTML, e outras. Você digita o código, e mandar executar, já ve o resultado na página mesmo. O site também oferece suporte a chat e grupos, o que possibilita vc explicar o código para alguém, discutir sobre um código, etc, etc. Aqui tem um artigo sobre o site
http://blog.cidandrade.pro.br/technology/interface-online-para-ensino-de-programacao-codeide/

Eu achei o site uma grande mão na roda pra quando você quer mostrar alguma técnica ou explicar algo para alguém. Além disso, ele tem um recurso de realce de sintaxe (syntax highlight) que melhora a percepção do código.

by angusyoung at May 21, 2008 10:56 AM

May 19, 2008

Blabos de Blebe

Membros privados em estruturas C

Esta semana, lá no trabalho tive mais uma prova que paradigma de programação é algo completamente independente de linguagem, ou seja, não é pelo fato de você estar programando em C++, compilando com o g++ que o seu código vai ser orientado a objetos, tão pouco, se você programa em ANSI C o seu código obrigatoriamente vai ser estruturado ou você estará impedido de programar orientado a objetos.

Estou lendo certo, Orientação a Objetos em ANSI C?

Sim e não!

Coisas como herança, polimorfismo e sobrecarga são complicadas de fazer/emular em C, mas você pode programar utilizando um estilo que se comporte de forma semelhante à orientação a objetos. A libdfb é escrita em C mas “orientada a objetos”, de forma que você cria, manipula de destrói elementos que se comportam de forma bem semelhante a objetos.

Aqui no trabalho, temos um sistema de abstração de hardware em C que se conecta com uma GUI em C++. A camada mais baixa, em C também foi escrita (e muito bem escrita, diga-se de passagem - não por mim ) com essas técnicas, simulando uma orientação a objetos. Uma dessas técnicas, me chamou atenção por usar uma daquelas notas de rodapé dos livros de C.

Ao compilar código em C, cada símbolo só tem visibilidade dentro da unidade de compilação na qual ele foi declarado, a menos que seja declarado novamente nas outras unidades como extern. Dessa forma, é possível “esconder” certos símbolos dentro de sua unidade de compilação, tornando-os inacessíveis ao mundo exterior. Temos com isso encapsulamento.

A unidade de compilação é o conjunto de arquivos que depois de pre-processados e compilados geram um único código objeto. Basicamente (mas não exatamente), podemos tomar como unidade de compilação cada arquivo de implementação de fonte (*.c, *.cpp. etc). Maiores detalhes sobre as etapas de compilação em C e C++ podem ser encontrados no blog do Caloni.

Utilizando essas informações, podemos criar uma struc em ANSI C na qual os seus membros internos são “privados”. A mágica está em aprisionar a definição dos membros dentro da unidade de compilação e criar métodos de acesso para esses membros. Para isso usamos as notas de rodapé que nos mostram a diferença entre declaração e definição de elementos em C:

Declaração ou manifesto: Apresenta ao compilador um identificador sem dizer muito sobre seu significado, ou seja, diz ao compilador que o identificador XXX existe, mas pouco se sabe sobre o que ele representa.
Ex.:

extern int a;
void bla(void);
struct st_data;

Definição ou implementação: Diz ao compilador o que determinado identificador representa, como por exemplo quanto de memória deve ser alocada para ele e qual o endereço de memória onde podemos encontra-lo.
Ex.:

int a;
void bla(void) {/* Do anything.  */}
struct st_data {/* Some members. */}

Quando falamos de estruturas e tipos, sem a definição o compilador não tem como alocar memória para ele pois ele nada sabe a respeito de qual o espaço que uma variável daquele tipo precisa. Por outro lado, algumas vezes, sem a declaração, o linker não tem como saber que aquele símbolo existe.

Quando declaramos uma estrutura num cabeçalho, normalmente nós também definimos seus membros ali mesmo e toda vez que adicionamos esse cabeçalho a um fonte nosso, nós incluimos na unidade de compilação desse fonte tanto a declaração quanto a definição dessa estrutura, tornando os membros da estutura públicos à essa unidade de compilação. Isso nos permite acessar seus membros diretamente.

Se separarmos a declaração da definição, somente símbolo que representa a estrutura estará disponível, mas não os seus membros. Assim, qualquer tentativa de acesso direto a um membro, gerará um erro de compilação. Um efeito colateral interessante é que como o compilador nada sabe sobre o tamanho da estrutura, não será possível definir diretamente uma variável do tipo da estrutura, somente um ponteiro para ela, pois ponteiros têm todos o mesmo tamanho e o compilador precisa apenas do nome símbolo do tipo para criar o ponteiro.

Temos então o header mytype.h mais ou menos assim:

#ifndef MY_TYPE_H
#define MY_TYPE_H
 
/* O typedef é apenas pra não ficar repetindo a palavra struct. */
/* A declaração é somente o trecho:                             */
/* struct _mytype                                               */
typedef struct _mytype my_type;
 
void create_my_type( my_type** );
void destroy_my_type( my_type** );
 
void set_data( my_type* , int );
int get_data( my_type* );
void set_text( my_type* , const char* );
char* get_text( my_type* );
 
#endif

A implementação mytype.c:

#include "mytype.h"
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
struct _mytype {        /* Aqui fica a definição da estrtura. */
    int data;           /* Somente depois disso é que o com-  */
    int text[21];       /* pilador vai saber como alocá-la.   */
};                      /* Tente um sizeof(my_type) no main.  */
 
void create_my_type( my_type** my_ptr )             { /* some code... */ }
void destroy_my_type( my_type** my_ptr )            { /* some code... */ }
void set_data( my_type* my_ptr , int d )            { /* some code... */ }
int get_data( my_type* my_ptr )                     { /* some code... */ }
void set_text( my_type* my_ptr , const char* text ) { /* some code... */ }
char* get_text( my_type* my_ptr )                   { /* some code... */ }

O código fonte completo do exemplo pode ser encontrado aqui.

Como não é possível criar diretamente variáveis desse tipo, precisamos definir um “construtor” e um “destrutor”.

Tentativas de acesso direto a membros geram erro de compilação:

user@host:~/private-struct-members$ gcc -o teste mytype.h mytype.c main.c
main.c: Na função ‘main’:
main.c:22: erro: dereferencing pointer to incomplete type
user@host:~/private-struct-members$

Dessa forma, com um pouco de criatividade e tendo os conceitos tanto da linguagem quanto dos paradigmas, é possível implementar códigos realmente interessantes. Neste exemplo bobo talvez não tenha ficado clara a utilidade de forçar uma emulação de encapsulamento ou o uso de construtor/destrutor em C, mas em sistemas onde as circunstâncias não permitem um C++, ou que a complexidade tenda a atingir níveis críticos, essas técnicas se mostram de grande valia. No nosso caso, essa técnica especificamente, permitiu que um programador experiente, que não participou do projeto todo, descobrisse que sua tentativa de acesso direto a um membro de uma estrutura, estava contextualmente inadequada. Sem isso, um bug cabuloso de lógica iria aparecer somente em tempo de execução, provavelmente fazendo o software explodir na cara do cliente.

Links úteis (ou não…):
http://www.directfb.org
http://www.caloni.com.br
http://www.numaboa.com.br/informatica/c/
Livro Desenvolvimento do Kernel do Linux

by blabos at May 19, 2008 12:08 AM

May 16, 2008

Otavio Rodolfo Piske

Dica: XPath

O XPath é uma linguagem para seleção de nós em um documento XML. Entre outas coisas, é bastante utilizada quando se trabalha com XSLT. Como ela também é suportada em diversas bibliotecas ela é comumente utilizada com C (libxml2), C++ (xalan, xerces, etc), Java (jaxp, etc), Python, e muitas outras linguagens
Hoje, precisei fazer uma expressão para obter um nó somente quando o nó pai fosse igual a um determinado valor e tivesse algum atributo específico.

<paises continente="Europa"/>
<pais nome="Portugal"/>
<pais nome="França" />
</paises>
<paises continente="Africa" />
<pais nome="Angola"/>
</paises>

Vamos supor que você quisesse obter o nó referente a Angola. Você poderia faze-lo através da seguinte expressão:

CODE:
  1. //paises[@continente='Africa']/pais[@nome='Angola']

No caso a expressão // representa os nós descendentes ou o próprio e a expressão @ representa um atributo. A expressão completa representa um caminho para o nó (de modo análogo /usr/share ou C:/windows representam um caminho no disco rígido).
Mais sobre XPath neste excelente tutorial.

by angusyoung at May 16, 2008 05:15 AM

May 15, 2008

Wanderley Caloni

Aquele do-while engraçado

Nesses últimos dias andei conversando com um amigo que está estudando sistemas operacionais na faculdade. Melhor ainda, vendo o código real de um sistema operacional em funcionamento. A conseqüência é que, além de aprender um bocado de como as coisas funcionam de verdade debaixo dos panos, acaba-se aprendendo alguns truquezinhos básicos e tradicionais da linguagem C.

Por exemplo, é um hábito conhecido o uso de construções do-while quando existe a necessidade de definir uma macro que possui mais de um comando em vez de usar a igualmente conhecida { construção de múltiplos comandos entre chaves }.

O que talvez não seja tão conhecido é o porquê das coisas serem assim.

Vamos imaginar uma macro de logue que é habilitada em compilações debug, mas é mantida em silêncio em compilações release:

#ifdef NDEBUG
 
#define MYTRACE( message ) /* nothing */
 
#else
 
#define MYTRACE( message )        \
	{                              \
		char buffer[500];           \
		sprintf(buffer,             \
			"MYTRACE: %s(%d) %s\n",  \
			__FILE__,                \
			__LINE__,                \
			message);                \
		OutputDebugString(buffer);  \
	}
 
#endif /* NDEBUG */ 
 

Nada de mais, e parece até funcionar. Porém, como veremos nas próximas linhas, esse é realmente um exemplo de código "buguento", já que uma chamada dentro de uma construção if-else simplesmente não funciona.

if( exploded() )
	MYTRACE("Oh, my God");
else
	MYTRACE("That's right");

error C2181: illegal else without matching if

Por que isso? Para responder a essa questão nós precisamos olhar um pouco mais de perto no resultado do preprocessador da linguagem, que apenas troca nossa macro pelo pedaço de código que ela representa:

if( exploded() )
	{
		char buffer[500];
		sprintf(buffer,
			"MYTRACE: %s(%d) %s\n",
			__FILE__,
			__LINE__,
			"Oh, my God");
		OutputDebugString(buffer);
	};
else
	{
		char buffer[500];
		sprintf(buffer,
			"MYTRACE: %s(%d) %s\n",
			__FILE__,
			__LINE__,
			"That's right");
		OutputDebugString(buffer);
	};

Dessa forma, podemos ver o porquê. Quando chamamos a macro, geralmente usamos a sintaxe de chamada de função, colocando um sinal de ponto-e-vírgula logo após a chamada. Essa é a maneira correta de se chamar uma função, mas no caso de uma macro, dessa macro, é um desastre, porque ela cria dois comandos em vez de um só (um ponto-e-vírgula vazio, apesar de não fazer nada, é um comando válido). Então, isso é o que o compilador faz:

if( instruction )
{
	/* um monte de comandos */

} /* aqui eu esperaria um else ou uma instrução nova */

; /* uma instrução nova! ok, sem else desa vez */

else /* espere ae! o que esse else está fazendo aqui sem um if?!?! */
{
	/* mais comandos */
}

Pense sobre o comando vazio como se ele fosse um comando real, o que é a maneira mais fácil de entender o erro de compilação que recebemos ao compilar o código abaixo:

if( error() )
{
	printf("error");
}

printf("here we go");

else /* llegal else without matching if! */
{
	printf("okay");
}

Por essa razão, a maneira tradicional de escapar desse erro comum é usar uma construção válida que peça de fato um ponto-e-vírgula no final. Felizmente nós, programadores C/C++, temos essa construção, e ela é... muito bem, o do-while!

do
{
	/* múltiplos comandos aqui */
}
while( expression ) ; /* eu espero um ponto-e-vírgula aqui, para
                         finalizar minha instrução do-while */

Assim nós podemos reescrever nossa macro de logue da maneira certa (e todas as 549.797 macros já escritas em nossa vida de programador). E, apesar de ser uma construção um tanto bizarra, ela funciona melhor do que nossa tentativa inicial:

#ifdef NDEBUG
 
#define MYTRACE( message ) /* nothing */
 
#else
 
#define MYTRACE( message )        \
	do                             \
	{                              \
		char buffer[500];           \
		sprintf(buffer,             \
			"MYTRACE: %s(%d) %s\n",  \
			__FILE__,                \
			__LINE__,                \
			message);                \
		printf(buffer);             \
	}                              \
	while( 0 )
 
#endif /* NDEBUG */ 
 

Ao usar um do-while (com uma expressão que retorna falso dentro do teste, de maneira que o código seja executado apenas uma vez) a construção if-else consegue funcionar perfeitamente:

if( exploded() )
	do
	{
		char buffer[500];
		sprintf(buffer,
			"MYTRACE: %s(%d) %s\n",
			__FILE__,
			__LINE__,
			"Oh, my God");
		OutputDebugString(buffer);
	}
	while( 0 );
else
	do
	{
		char buffer[500];
		sprintf(buffer,
			"MYTRACE: %s(%d) %s\n",
			__FILE__,
			__LINE__,
			"That's right");
		OutputDebugString(buffer);
	}
	while( 0 );

by Wanderley Caloni at May 15, 2008 03:35 AM

May 12, 2008

Alberto Fabiano

techberto


Concordo com o DQ que o gênio (ou genioso se preferir) Adelir de Carli é um Candidato a um Darwin Award, assim como concordo com o Christiano Anderson que o encontro do GruPy-SP no escritório do Google-SP foi excelente e principalmente com o Caloni que está na hora de reservar suas cadeiras para o Seminário C++ Portabilidade & Performance, afinal dentro de várias boas razões para se utilizar C++ uma delas é a Performance! Este post do Caloni é um bom começo, porém se preferir vá direto na fonte e faça sua inscrição na página do evento.

O curioso é que um dia antes irá ocorrer o encontro de Maio do Grupy-SP no Centro de Computação da Unicamp, em Campinas que irá durar o dia inteiro. Iniciar o final de semana na sexta com o encontro de Python e no sábado ir para o seminário C++ P&P será muito divertido!

Já me perguntaram num metrô, num shopping, numa livraria, num restaurante e por vários e-mails quando será o próximo encontro do EPA-CCPP, sinceramente fico feliz que os anteriores tenham agradado mas por enquanto não há nada definido, mas espero em breve ter boas novas sobre isto! E você não foi no último encontro? Tenha um overview pela cobertura que o nosso amigo Caloni deu no qual ele afirmou que nossa comunidade está ganhando forma, assim como recomendo uma visita a página do 4o.EPA_CCPP que contém link para as apresentações utilizadas, além de um excelente tutorial de QT e também link dos vídeos de 3 apresentações que ocorreram no encontro. E modéstia a parte, como o nosso colega nerd pós-moderno Lamarão afirmou; foi um Nerds Meeting que exalou inteligência! :-)

by techberto at May 12, 2008 02:27 AM

May 09, 2008

Blabos de Blebe

Eclipse + Qt + svn

Mais como log pessoal do que um tutorial, senti a necessidade de deixar num local de fácil acesso, as etapas de configuração do eclipse para meu próprio uso, ou seja, a instalação do eclipse e dos plugins que eu normalmente uso (svn e qt).

Os comandos que afetam diretórios fora de seu home, precisarão de permissão de superusuário.

Se você resolveu instalar o eclipse apartir dos pacotes disponíveis em sua distribuição, pode pular direto para o passo #2.

Passo #0: Instalação do Java

Para que o eclipse funcione, vc precisa do Java Runtime Environment (JRE) instalado *E* configurado.

O JRE pode ser baixado direto do site da Sun http://java.sun.com/javase/downloads/index.jsp. Instruções completas de instalação e configuração podem ser encontradas em http://java.sun.com/javase/6/webnotes/install/index.html, para o caso do JRE 6.

Passo #1: Donwload e instalação do eclipse

O eclipse pode ser facilmente baixado a partir da área de downloads no seu site oficial: http://www.eclipse.org/downloads. Após o download, basta descompactá-lo e sair usando. No meu caso eu usei:

user@host$ tar -xvzf eclipse-cpp-europa-winter-linux-gtk.tar.gz -C /usr/local

Se tudo deu certo o executável do eclipse estará em /usr/local/eclipse/eclipse, daí é só criar um link/atalho/whatever no seu ambiente gráfico favorito.

Passo #2: Download e instalação do plugin de integração com o QT


Qt Eclipse Integration
Qt Eclipse Configuration

O plugin que eu atualmente uso é o fornecido pela própria Trolltech. Nele você pode gerenciar os seus arquivos de projeto a partir de um pequeno editor gráfico, e ainda se preferir, tem acesso direto ao arquivo .pro.

O download pode ser feito a partir de http://trolltech.com/developer/downloads/qt/eclipse-integration-download, e a instalação cujas instruções completas podem ser encontradas em http://trolltech.com/developer/downloads/qt/qteclipse-installmanual, é complicadíssima:

user@host$ tar -xvzf qt-eclipse-integration-linux.x86-gcc3.3-1.4.0.tar.gz -C /usr/local

Se tudo deu certo, os arquivos do plugin foram copiados para o diretório /usr/local/eclipse/plugins.

Nota: Para quem optou pela instalação do eclipse através dos pacotes da distribuição, atenção!!! Dentro do arquivo compactado, há o diretório eclipse/plugins/, e dento dele os arquivos do plugin, que devem ser copiados para o diretório de plugins da instalação do seu eclipse, normalmente /usr/lib/eclipse/plugins.

Após a instalação, inicie o eclipse com o comando:

user@host$ /usr/local/eclipse/eclipse -clean

Agora, vá em Window>Preferences>Qt e ajuste a versão e os “pathes”, de acordo com a sua instalação do Qt.

Para ficar mais cômodo, se você invoca o eclipse direto da linha de comando, adicione o diretório do seu executável na variável de ambiente $PATH, dentro de algum dos scripts de inicialização (.bash_profile)

export PATH=$PATH:/usr/local/eclipse

Passo #3: Subversion

No eclipse vá em Help>Softwares Updates>Find and Install. Marke a opção Search for new features to install e clique em Next. Agora adicione os sites remotos:

Buckminster
http://download.eclipse.org/tools/buckminster/updates

SubClipse
http://subclipse.tigris.org/update_1.2.x

Marque os respectivos checkboxes, e clique em Finish. Depois de uma pequena consulta à internet, é só marcar o plugin e dependências e correr pro abraço.

by blabos at May 09, 2008 11:00 AM

May 08, 2008

Pedro Lamarão

Projeto Orientado a Componentes, parte II

Quando dizemos que componentes são por definição intercambiáveis estamos continuando uma longa tradição de bons projetistas de bons sistemas, cujo conjunto de características sempre integrará, por mais sofisticada a metodologia e/ou a nomenclatura, a aplicação da separação entre interface e implementação. [1]

Se a interface continua a mesma, podemos substituir a implementação sem perturbar o comportamento do sistema. Obviamente que o comportamento do sistema deve mudar de alguma forma, senão trocar a porcaria do componente não teria propósito. Esperamos, portanto, que erros sejam corrigidos (de modo que o sistema de fato funcione como deveria), ou que o desempenho aumente (de modo que o sistema funcione antes que eu caia no sono) ou sei lá.

Aplicávamos esta distinção já na era do projeto estruturado; e no projeto essencial; e no projeto orientado a objetos; e agora no projeto orientado a componentes e aplicaremos no projeto orientado a serviços quando finalmente este troço alcançar a plebe.

Em um sistema orientado a objetos, nós temos tipicamente dois elementos em evidência como detalhe de implementação: a estrutura de um objeto, e as instruções levadas a cabo por seus métodos. Nós queremos liberdade para alterar a estrutura de um objeto e queremos liberdade para alterar a instruções executadas por seus métodos. (Há mais por debaixo do pano, como sempre.)

Em um sistema orientado a componentes, um terceiro elemento surge ofuscando, como se fosse, os outros dois; o componente propriamente dito. Entendemos que um componente possui uma interface e uma implementação; sendo a implementação de um componente composta por inúmeras classes. Além disso, um componente é um artefato componente (heh) de um sistema implantado; é parte da definição de componente que este seja intercambiável por um outro, que implemente a mesma interface, em um sistema implantado -- em contraste com um tipo de substituição que exige recompilação.

Esta última restrição, quando posta no contexto dos sistemas operacionais reais, com seus linkers e loaders reais, nos trás à crux do problema de projeto orientado a componentes: a implementação de uma classe integrante de um componente absolutamente não pode depender de um detalhe de implementação de uma classe integrante de outro componente.

Observe que esta restrição não é exatamente uma novidade, já que bons sistemas isolarão módulos através de interfaces para aumentar a coesão e diminuir o acoplamento; porém, quando componentes entram na jogada, a metodologia exige uma restrição mais forte.

(De fato, esta restrição metodológica se torna uma impossibilidade tecnológica assim que os componentes são levados ao próximo estágio, passam a não mais morar no mesmo espaço de memória virtual, e começam a ser chamados "distribuídos" ou "serviços".) [2]

É por esta razão que os líderes do Projeto Spaghetti falharão em "componentizar" seu sistema; mesmo que, à primeira vista, seja possível seccionar sua estrutura de classes em artefatos binários distintos.

[1] Nós, filhos da cultura européia, nem escrevendo programas nos livramos da dicotomia corpo versus espírito. Nem imagino o que seria uma metodologia de desenvolvimento de sistemas desenvolvida por monges taoístas chineses.

[2] E portanto, domar a restrição metodológica significa poder conviver com a impossibilidade tecnológica e eventualmente programar os sistemas do futuro.

by P. (noreply@blogger.com) at May 08, 2008 03:28 PM

Projeto Orientado a Componentes, parte III

Agora, uma pausa para reflexão. Esta dicotomia interface versus implementação é difícil de definir. Por baixo dos panos nós temos memória de onde nós lemos e para onde nós escrevemos. O que caracteriza uma interface e o que a distingue de uma implementação?

Essa pergunta só pode ser respondida em um determinado contexto. Um bom sistema escrito em assembler talvez tenha interfaces mais bem definidas que um mau sistema escrito em Java. Porém, está claro para a indústria que certos mecanismos de linguagem favorecem o estabelecimento de boas interfaces no sistema implementado.

Além disso, a noção de interface surge diante de um problema de substituição. É interface aquilo que, mantendo-se estável, permite a substituição daquilo que é implementação. Porém, o que é este permite? Este permite, quando caracterizado, por conseguinte caracteriza o que é interface.

Suponha um sistema escrito em C++ por uma equipe que considera irrisório o tempo de recompilação do código-fonte. Neste contexto, podemos seguramente aceitar que a seguinte substituição mantém estável uma interface.

antes

class Foo {
public:

Foo ();

void
mutate_stuff ();

int
observe_stuff () const;

private:
Bar* m_bar;
};

depois

class Foo {
public:

Foo ();

void
mutate_stuff ();

int
observe_stuff () const;

private:
int m_cache;
shared_ptr<bar> m_bar;
};

Após a alteração acima e uma recompilação o sistema continua a funcionar normalmente. [1] Dizendo de forma mais extensa: neste contexto, a substituição realizada não altera propriedades observáveis externamente da classe, de modo que do ponto de vista de um observador, a classe mantém estável sua interface. (Apenas membros privados foram alterados.)

Agora vamos alterar as nossas premissas. O tempo de recompilação desse sistema é enorme e o ciclo de testes não pode esperar, de modo que a estrutura de classes do sistema foi particionada e esta classe mora dentro de um objeto compartilhado, uma biblioteca dinâmica.

A equipe realiza a substituição, recompila o objeto compartilhado e o entrega a uma equipe de testes.
E os testes falham miseravelmente.

Isto acontece porque, neste novo contexto, há mais propriedades observáveis a considerar, que devem se manter estáveis -- que fazem parte da interface da classe.
Neste caso, o layout de um objeto da classe Foo é uma propriedade observável; um programa compilado criando objetos da definição antes criará objetos com um layout diferente daquele esperado pelo objeto compartilhado que espera objetos dada a definição depois.
A definição depois tem um int no offset onde a definição antes tinha um ponteiro, ela ocupa mais memória etc.

Podemos dizer que o primeiro caso é o caso de um projeto orientado a objetos, onde a alteração de elementos privados da classe não altera a interface; e podemos dizer que o segundo caso é o caso de um projeto orientado a componentes, onde a alteração do layout na memória de uma classe altera sua interface.

Como no segundo caso nós estamos violando a interface do componente, nós não podemos substituir o componente de antes pelo componente de depois impunemente. Dito de trás para a frente, pelo fato de não podermos substituir o componente de antes pelo componente de depois, por definição estamos violando sua interface.

Imagine então o que acontece em um sistema spaghetti, onde todas as classes mantém referência a todas as outras classes; uma violação de interface em um "componente" perturbará implacavelmente todos os outros "componentes".

Quando projetamos um sistema pensando em componentes, ou quando desejamos aplicar a idéia forte de interface para melhorar o nosso projeto diminuindo o acoplamento entre as coisas que são distintas, é útil usar das ferramentas à disposição para dar forma ao que é uma interface.

Linguagens que se propõe a facilitar o desenvolvimento de componentes, como Java e C#, possuem como parte integrante do seu vocabulário e mecanismo nativo uma interface em contraste com as classes. Normalmente algo do tipo:

interface IFoo {

void
mutate_stuff ();

int
observe_stuff () const;

};

Em C++ não existe uma construção análoga mas é possível emulá-la integralmente usando classes, da seguinte maneira:

class IFoo {

virtual
void
mutate_stuff () = 0;

virtual
int
observe_stuff () const = 0;

};

(O leitor astuto observará que esta construção-interface não possui atributos.)

O propósito de usar construções como estas é evidenciar a natureza destes elementos como interfaces -- aquilo que se deseja manter estável -- e diminuir a chance disto que é interface ser inadvertidamente alterado -- causando o desastre. A construção-interface é a melhor amiga do projetista de componentes, permitindo a representação de um conceito de projeto diretamente na linguagem do código-fonte, restringindo as possibilidades de violação durante o processo de implementação, ou ao menos tornando a (necessidade de) violação claramente evidente.

Infelizmente para nós nem tudo que existe na fronteira de um componente pode ser uma interface, já que é preciso comunicar coisas de um componente para outro. E quem pode viver comunicando apenas doubles e bools? Nós queremos comunicar objetos.

Além disso, as construções-interface não são garantia de estabilidade de interfaces, já que há mais sobre o que é publicamente observável em um objeto que sua estrutura: há o seu comportamento.

[1] Estamos, naturalmente, assumindo que o programador não é louco e que a alteração tem algum sentido, exatamente como aparenta ter.

by P. (noreply@blogger.com) at May 08, 2008 03:28 PM

Projeto Orientado a Componentes, parte IV

Uma ligeira digressão, para aproveitar a oportunidade que se apresenta; apresentei esta semana um brevíssimo seminário sobre interoperabilidade entre C++ e Java (com ênfase na ferramenta SWIG).

O problema a resolver, quando a necessidade de interoperabilidade entre C++ e a Java surge, é exatamente um problema de projeto de componente, por uma via indireta: se não é tão necessária a garantia de substituição de componentes, é inescapável a completa separação entre a implementação do "componente C++" e do "componente Java" devido ao completo isolamento entre seus espaços de memória.

Esta mesma situação ocorre em um sistema convencional de componentes em que cada componente se localiza em processos distintos, ou mesmo em sistemas distintos, se comunicando através de algum mecanismo inter-processos. O próximo passo na escala evolutiva do projeto de sistemas, o projeto orientado a serviços, lida explicitamente com esta situação, já que se assume como normal que serviços se localizam em sistemas (portanto processos) distintos.

Esse contexto, e as restrições que ele impõe, transforma a natureza da troca de informação; a memória de um processo não é mais um recurso compartilhado entre rotinas.

Considere a seguinte função em C, parte de uma interface de componente IFoo. (O componente concreto Foo implementa a interface IFoo.)

Foo*
foo_retrieve_from_persistence (char* foo_name);

Esta função recebe uma NTBS identificando unicamente um objeto Foo na persistência e retorna o endereço do objeto Foo na memória, trazendo o objeto para a memória uma primeira vez se necessário.

Suponha que seja desejável, através de um programa Java, usar objetos Foo obtidos através desta função.

Do componente Bar, uma operação obtém uma referência a uma implementação de IFoo e decide chamar a operação foo_retrieve_from_persistence, passando como argumento uma NTBS. Observe que uma NTBS é uma referência a um espaço contíguo de memória; simplesmente entregar esse endereço para o componente Foo causará um desastre quando este componente resolver acessar esse endereço em seu próprio espaço de memória, cujo significado é incerto. Do mesmo problema sofre o valor de retorno da função; este é o endereço de um objeto na memória do componente Foo, cujo significado no espaço da memória de Bar é incerto.

Essa situação exibe a impossibilidade de tratar a semântica de referência, na travessia do limiar entre componentes, da mesma maneira como é tratada em projetos mais simples. Como já dissemos, não é possível assumir a memória como recurso compartilhado entre operações em um projeto orientado a componentes. [1]

A solução canônica é copiar esses valores. Esta tarefa deve ser realizado por aquele elemento do sistema que existe no umbral entre componentes e é responsável por transportar informação de um lado para o outro. Este elemento deverá, então, copiar todo o segmento de memória endereçado por foo_name da memória do componente Bar para a memória do componente Foo. Esta atividade se denomina "data marshalling" em um certo vocabulário e regras particulares de "data marshalling" são chamadas "type maps" em um certo outro vocabulário.

Este é um exemplo simples de "type map" que transporta um objeto String do Java para um argumento de tipo const std::string& do C++.

// na prática, estes objetos são argumentos de uma função JNI.
extern JNIEnv* jenv;
extern jstring jargN;

// type map
const char *argN_pstr = (const char *)jenv->GetStringUTFChars(jargN, 0);
if (!argN_pstr) return 0;
std::string argN_str(argN_pstr);

A função GetStringUTFChars realiza a tarefa concreta de copiar os segmentos de memória do espaço de memória Java para o espaço de memória C++. Assim, a operação da interface de componente Java/JNI terá a seguinte forma:

public static native jlong foo_retrieve_from_persistence (String foo_name);

Essa solução, infelizmente, não resolverá o problema do valor de retorno da função. Foo não é um tipo primitivo da linguagem; não existe uma função na JNI para copiar objetos Foo. Mesmo que existisse, Foo é um objeto; o que nós queremos fazer com ele é chamar suas operações. De certa forma, nossa vontade se divide em duas: expor ao programa Java as operações da classe Foo e expor ao programa Java objetos Foo sobre o qual operar. Essas duas necessidades serão resolvidas com mecanismos diferentes.

Digamos que esta seja a classe Foo.

class Foo {
public:

Foo (char* name);

char*
ask_question (char* question);

char*
get_name () const;

};

A primeira vontade é realizável produzindo, para cada operação de Foo, uma operação na interface do componente IFoo.

Foo*
Foo_new (char* name);

void
Foo_delete (Foo* foo);

char*
Foo_ask_question (Foo* foo, char* question);

char*
Foo_get_name (const Foo* foo);

A segunda é entregando ao programa Java o endereço do objeto Foo necessário. Chegamos, aparentemente, a um impasse; já que nosso problema original era justamente como transportar o valor de retorno da função foo_retrieve_from_persistence, que é do tipo Foo*! Porém, tendo caminhado até aqui, o problema se torna ligeiramente diferente, e uma solução é possível. Agora que nós temos todas as operações de Foo que desejamos exportadas pela interface de componente IFoo, tudo o que resta é entregar ao componente cliente uma referência opaca a um objeto Foo. O componente cliente da interface nunca necessitará resolver (ou de-referenciar) esta referência; ele apenas mantém este valor para usá-lo como argumento de chamada a uma operação da interface IFoo. [2]

Endereços de memória do C++ podem ser guardados apropriadamente na memória do Java como um valor do tipo Long. Assim, o valor de retorno da operação Foo_create é resolvido pelo seguinte "type map":

// este é o objeto retornado pela chamada JNI
jlong jresult = 0;
// este é o objeto retornado pela função C++
Foo* result = NULL;

// type map
result = new Foo(arg1_str); // vide type map anterior
*(Foo **)&jresult = result;

// por fim...
return jresult;

e a operação na interface de componente Java/JNI terá a seguinte forma:

public static native jlong Foo_create (String name);

Assim, o programa Java chamador de Foo_create através desta interface receberá um valor Long que contém o endereço, na memória do C++, do objeto Foo recém-criado. A operação Foo_get_name através desta interface terá a seguinte forma:

public static native String Foo_get_name (jlong foo);

sendo que o valor de retorno da operação Foo_get_name do componente IFoo será "marshalled" pelo "type map" como já vimos anteriormente.

Havendo resolvido o problema da possibilidade de referenciar, de maneira opaca, um objeto Foo da memória do C++ na memória do Java, e o problema de chamar operações da classe Foo em C++, podemos então criar uma classe Foo em Java cujo único propósito é imitar a classe Foo em C++ por conveniência.

class Foo {

private Long cPtr;

// @Override
public void finalize () {
delete();
}

public void delete () {
IFooJNI.Foo_delete(cPtr);
}

public Foo (String name) {
cPtr = IFooJNI.Foo_new(name);
}

public String ask_question (String question) {
return IFooJNI.Foo_ask_question(cPtr, question);
}

public String get_name () {
return IFooJNI.Foo_get_name(cPtr);
}

};

Desta forma concretizamos uma forma ideal de interoperabilidade entre Java e C++ onde, para cara classe C++, há uma classe Java equivalente, responsável por esconder as chamadas à interface de componente JNI que, por sua vez, é responsável por realizar o transporte de informação através do umbral dos componentes.

Nestes exemplos ocultamos o fato de que, para a JNI, é necessário não somente um componente Java/JNI mas também um componente C++/JNI além do próprio componente que implementa a interface IFoo; também não lidamos com os casos em que registramos "objetos callback" criados no Java em sistemas C++; não lidamos com a vontade de transportar exceções disparadas em C++ de volta para o chamador Java; entre outras coisas de que não falamos.

[1] É claro que um projeto pode se utilizar da noção de componentes de uma forma restrita, para obter um conjunto restrito de benefícios, abandonando esta restrição; é possível, por exemplo, obter os benefícios de recompilação veloz e carga de código por demanda abandonando a possibilidade de distribuir os componentes de modo a mantê-los sempre no mesmo espaço de memória e permitir o uso convencional de ponteiros.

[2] Em projetos de sistemas com componentes é comum que esta idéia do "endereço opaco" seja generalizada para qualquer tipo de informação "opaca" que seja capaz de identificar univocamente um objeto de Foo no domínio do componente Foo; por exemplo, um "nome", ou um GUID, ou outra coisa qualquer.

by P. (noreply@blogger.com) at May 08, 2008 03:27 PM

May 07, 2008

Blabos de Blebe

Iniciando no QT, parte III - qmake e .pro

Neste terceiro post sobre QT vamos falar do utilitário qmake e dos arquivos de projeto *.pro. Vamos entender para que serve o qmake e como configurar diferentes tipos de projetos.

O qmake

O qmake é um utilitário que acompanha o framework QT. Sua função é parsear um arquivo de projeto (*.pro) e gerar um Makefile já com as regras do moc, uic e opções do QT embutidas. Sem ele por exemplo, teríamos que chamar o moc explicitamente para criar os arquivos moc_* e passar explicitamente para o compilador e linker, as opções corretas para incluir o QT aos nossos projetos.

A documentação oficial sobre o qmake e arquivos de projeto, pode ser encontrada aqui.

Arquivos de projeto *.pro

Para criar um arquivo de projeto simples pela linha de comando, entramos no diretório do projeto e digitamos:

user@host$ qmake -project

Feito isso, será criado um arquivo com o mesmo nome do diretório corrente, seguido da extensão .pro. Caso já existam nesse diretório arquivos reconhecidos pelo qmake, como arquivos de códigos fontes (.h, .cpp), forms (.ui), etc, eles serão automaticamente adicionados ao arquivo de projeto.

Criado o arquivo .pro, ao executarmos qmake sem argumentos ele tentará parsear um arquivo .pro com o mesmo nome do diretório corrente. Você ainda pode especificar um arquivo de projeto alternativo como argumento para o qmake. Se tudo deu certo, um arquivo Makefile foi criado, e com um simples make, podemos compilar o projeto.

Os arquivos de projeto são arquivos de texto normais, contendo macros e diretivas que serão interpretadas pelo qmake para criar o Makefile. A lista completa de opções pode ser encontrada na documentação online. As mais comuns são:

TEMPLATE: Indicam o tipo de projeto. Use ‘app’ para proramas executáveis ou ‘lib’ para criar bibliotecas.

CONFIG: Adicionam opções diversas ao projeto. Entre elas, ‘debug’ para adicionar informações de depuração, ’staticlib’ em conjunto com o template ‘lib’, para que abiblioteca criada seja estática (.a no linux).

TARGET: O nome e a localização do alvo, ou seja, do aplicativo ou biblioteca.

MOC_DIR: Diretório onde serão grados os arquivos moc_*. Útil para não poluir o diretório de códigos fontes.

OBJECTS_DIR: Complementar à opção anterior, indica o diretório onde serão gerados os aquivos de código objeto (*.o).

INCLUDEPATH: Diretórios externos onde existem headers que serão utilizados no projeto, como headers de bibliotecas externas.

DEPENDPATH: Diretórios de códigos fontes externos que serão utilizados pelo projeto.

HEADERS: Os arquivos de cabeçalho do projeto (*.h).

FORMS: arquivos de interface gerados com o QtDesigner (*.ui).

SOURCES: Os arquivos de implementação de código fonte do projeto (*.cpp).

LIBS: Bibliotecas externas utilizadas pelo projeto. -L indica o path para a biblioteca, e -l diz o nome da biblioteca.

QT: Módulos do QT que devem ser adicionados/excluidos do projeto. Se for passado ‘QT =’ (QT igual vazio), nenhum módulo QT será utilizado no projeto.

SUBDIRS: Utilizado em conjunto com o template ’subdirs’, indica os subdiretórios ons o qmake deve procurar por outros arquivos de projeto.

Com isso em mãos podemos criar algums projetos simples.

Um aplicativo simples:

# O primeiro caracter desta linha cria um comentário
# O nome deste arquivo é 'app.pro'
TEMPLATE     =  app            # Nosso template é um aplicativo chamado
TARGET       =  bin/myapp.bin  # myapp.bin, dentro do dir ./bin
MOC_DIR      =  tmp/moc        # Diretório para mocs, opcional
OBJECTS_DIR  =  tmp/obj        # Diretório para código objeto, opcional
HEADERS      += myclass.h      # Header da classe MyClass
SOURCES      += main.cpp \     # Utilize \ para organizar os arquivos em
                myclass.cpp    # várias linhas.

Uma biblioteca simples:

# O nome deste arquivo é 'lib.pro'
TEMPLATE     =  lib            # Nosso template é uma biblioteca
CONFIG       += dll            # dinâmica, chamada
TARGET       =  lib/mylib      # mylib, dentro do dir ./lib
MOC_DIR      =  tmp/moc        # Diretório para mocs, opcional
OBJECTS_DIR  =  tmp/obj        # Diretório para código objeto, opcional
HEADERS      += myclass.h      # Header da classe MyClass
SOURCES      += myclass.cpp    # Implementação da classe MyClass

Um aplicativo que usa uma biblioteca externa:

# O nome deste arquivo é 'mixed.pro'
TEMPLATE     =  app            # Nosso template é um aplicativo chamado
TARGET       =  bin/myapp.lkd  # myapp.lkd, dentro do dir ./bin
MOC_DIR      =  tmp/moc        # Diretório para mocs, opcional
OBJECTS_DIR  =  tmp/obj        # Diretório para código objeto, opcional
INCLUDEPATH  += .              # Dir onde estão os Headers da lib externa
SOURCES      += main.cpp       # Implementação do aplicativo.

Diretórios aninhados:

Para utilizarmos diretórios aninhados, precisamos de um arquivo de projeto no diretório atual, e outro no subdiretório.

No diretório atual, utilizamos as opções:

TEMPLATE = subdirs
SUBDIRS  = [lista de subdirs para serem compilados em sequência]

Caso seja fornecido apenas o nome do diretório, ele precisa conter uma arquivo de projeto com o seu próprio nome. É possível no entando, informar diretamente na lista de subdiretórios, um diretório seguido por um arquivo de projeto com quaquer nome.

O código fonte completo dos exemplos abordados neste tutorial pode ser encontrado aqui.

Concluindo

O qmake é uma ferramenta simples e muito poderosa que facilita enormemente a vida de quem tem de gerenciar aŕvores de projeto complexas, mesmo que o projeto não utilize QT (basta acrescentar no final a opção ‘QT = ‘). Adicione a isso o fato de os arquivos de projeto terem uma sintaxe bastante simples e até certo ponto intuitiva. Se você precisar de ajustes mais finos, a documentação online lhe dará uma dezena de opções para se divertir.

Com QT ou sem QT, o qmake é sempre uma boa opção para gerenciamento de árvore de build.

by blabos at May 07, 2008 11:00 AM

April 28, 2008

Pedro Lamarão

Projeto Orientado a Componentes, parte I

Ano passado fiz um curso de extensão em Projeto Orientado a Componentes com UML; na época, eu estava progredindo na minha capacidade de construir modelos mentais de projetos de software orientados a objeto e já havia lido bastante sobre web services e arquiteturas orientada a serviços.

O mais interessante sobre os Componentes é a maneira como eles dão um nó na sofisticação aparente dos sistemas e obrigam o projetista a voltar aos básicos. Digo isso porque estes sistemas são aparentemente sofisticados; na prática, eles apresentam baixa coesão e alto acoplamento, significando que elementos deste sistema se relacionam diretamente com praticamente todos os outros de maneira ad hoc, fenômeno que, em sua forma mais concreta, denomina-se código spaghetti.

É possível construir um sistema orientado a objetos onde todos os objetos conhecem ponteiros para todos os outros, ou praticamente todos os outros, usando truques safados como embrulhar todo mundo em shared_ptr e tornando todas as classes enable_shared_from_this e olerê olará. Isto, é claro, se você tem um mínimo de conhecimento sobre C++ contemporâneo -- e se você está de fato escrevendo seu sistema em C++! -- e se você se importa com a memória. O sistema acima é "orientado a objetos". Muitos deles, embolados entre si.

Quando você se propõe a fazer sistemas orientados a componentes, porém, o buraco se alarga e se torna mais profundo. Os componentes existem com o propósito de serem intercambiáveis. Você não tem componentes se você não pode tirar um deles e colocar outro no lugar impunemente.

Frequentemente, os responsáveis por sistemas spaghetti ou aberrações similares, diante dos problemas insuportáveis causados inevitavelmente pela baixa coesão e pelo alto acoplamento, tentam "componentizar" seus sistemas com o intuito de obter os benefícios maravilhosos oferecidos pelos livros.

Vamos, então, pegar pedaços do sistema e enfiar dentro de DLLs!

Certamente isto não funciona.

by P. (noreply@blogger.com) at April 28, 2008 09:59 PM

April 25, 2008

Wanderley Caloni

Seminário CCPP Portabilidade e Performance

Reserve sua cadeira. Está marcado para o último dia do mês de maio o primeiro seminário de nosso grupo nacional de programadores e aficionados por C e C++. É bom ou não é?

O assunto gira em torno de duas preocupações constantes na vida de todo programador de linguagens de nível médio:

  • Quanta velocidade eu preciso nesse código?
  • Em quais plataformas eu conseguiria compilar e rodar meu projeto?

Para responder estas questões teremos uma bateria de palestras com temas que, dessa vez, focam o simples, puro e independente uso das linguagens C/C++:

wanderley.gifDicas e Truques de Portabilidade
Wanderley Caloni

O objetivo dessa palestra é focar nos problemas da vida real que enfrentamos no dia-a-dia para tornar um código portável ou mais maleável para um dia ser. Nesse caso existem vários assuntos a tratar, como: construções ambígüas não-padrão, isolamento de particularidades de cada sistema, identificação de problemas de portabilidade, organização do código-fonte portável, entre outros.

O nível dessa palestra será o intermediário, porque eu preciso que o público tenha o conhecimento prévio de programação C e C++. Quando você está aprendendo, uma dica ou outra sobre portabilidade pode ser interessante para não ser desvirtuado desde o início. Porém, para realmente começar a programar multiplataforma, existem desafios que devem ser transpostos por aqueles que já conseguem um nível de organização e desenvolvimento em C e C++ que não deixa dúvidas sobre a qualidade do código.

fabio.jpg

Programação Concorrente com C++
Fábio Galuppo

Fábio Galuppo estréia na nossa rede de palestrantes, depois de seu inspirador e excitante relato das peripécias do SD West desse ano. Ele irá falar de um tema extremamente atual, que é o uso de programação paralela, em especial usando C++. Existe uma série de coisas para entender, como os modelos a ser seguidos, o uso consciente de threads, a programação com bom desempenho nos novos chips com mútiplos núcleos de processamento e por aí vai.