Murilo :P

C++, Computação, Programação, Web e afins :)

Posts Tagged ‘cpp

Políticas de Controle de Comportamento em C++

leave a comment »

Trabalhando no meu projeto na Boost, fui requisitado a implementar uma função para calcular o logaritmo na base 2 de números inteiros. De prontidão pesquisei qual a melhor forma e implementei-a da maneira mais otimizada possível. Fiz um Request For Comments (RFC) na mail list da Boost para saber opiniões sobre qual deveria ser o nome de tal função. Bom, depois de alguma conversa ficou (ao menos por enquanto) decidido que o melhor nome é ilog2.

Bom mas havia um problema logaritmo de 0 é indefinido. E na documentação eu coloquei:

If `value` is equal to 0, the value returned is undefined

Foi aí que um contribuidor da Boost chamado Paul Bristow recomendou a mim que fizesse com que esse comportamento (ilog2(0)) pudesse ser controlável através de políticas, mais especificamente usando boost::math::policies. Resisti um pouco no começo mas cedi e achei bastante interessante.

O que são políticas de tratamento de comportamento?

Basicamente são meios de controlar o comportamento de um algoritmo em certas situações (como o ilog2(0) por exemplo).

Um exemplo de política muito utilizada na biblioteca padrão são as classes que usam std::allocator.

Em C++ podemos implementar políticas facilmente em tempo de compilação.

Implementando Políticas

Vamos então implementar um modelo de política bem simples. Tomei por base as Policies da biblioteca Math da Boost, porém muito mais simplificada por questão de didática.

#ifndef POLICIES_HPP_INCLUDED
#define POLICIES_HPP_INCLUDED

#include <cerrno>      // errno
#include <stdexcept> // std::domain_error

namespace policies
{

// Nossa política
enum error_policy_type
{
	ignore_error = 0,
	errno_on_error,
	throw_on_error,
	user_error
};

// continua

Esse enum define a regra que queremos utilizar em nossa política.

  • ignore_error: ignora o erro e retorna um valor padrão.
  • errno_on_error: seta errno e retorna um valor padrão.
  • throw_on_error: dispara uma exceção.
  • user_error: executa uma função customizada definida pelo usuário, note que a declaração dessa função já existe, tendo apenas que ser feita a definição dessa função.

No caso defini 4 tipos de erros mas pode-se ter mais coisas como “dialog_on_error” se seu projeto utiliza GUI e você quer que apareça uma dialog box informando algo, de qualquer maneira pode-se também usar o user_error se for aplicável.

// continuação

template <int P>
struct domain_error
{
	const static int policy = P;
};

template <int P>
struct overflow_error
{
	const static int policy = P;
};

Aqui declaramos nossos tipos de erro. É basicamente um wrapper para nossas constantes declaradas no enum anterior. P é a política que queremos aplicar. Por exemplo typedef overflow_error<errno_on_error> my_pol;, my_pol é uma política que para tratar overflow_error seguindo a regra errno_on_error. Novamente podemos ter vários tipos de erros que queiramos tratar, coloquei esses dois como exemplo. Notemos também que para simplicidade não tratei os possíveis valores que P pode assumir.

typedef domain_error<ignore_error> default_domain_policy;
typedef overflow_error<errno_on_error> default_overflow_policy;

Aqui definimos quais as políticas padrões para cada tipo de erro. No caso definimos que a política padrão que deve ser seguida quando um domain_error for lançado é a ignore_error. Veremos mais para frente sugestões de como tratar cada política.

// Only the declaration, must be defined somewhere
template <typename T>
T user_domain_error(const char* function, const char* message, const T& val);

template <typename T>
T user_overflow_error(const char* function, const char* message, const T& val);

Aqui nós apenas declaramos os protótipos das funções que serão chamadas quando um user_error for lançado. Se alguém usar a política user_error, este deverá codificar essas funções em algum outro lugar.

template <typename T>
T raise_domain_error(const char* function, const char* message, const T& val, const policies::domain_error<ignore_error>&)
{
	// Simply ignoring the error
	return val;
}

template <typename T>
T raise_domain_error(const char* function, const char* message, const T& val, const policies::domain_error<errno_on_error>&)
{
	// Sets errno to Error DOMain
	errno = EDOM;
	return val;
}

template <typename T>
T raise_domain_error(const char* function, const char* message, const T& val, const policies::domain_error<throw_on_error>&)
{
	throw std::domain_error(message);
	
	// This will never be returned
	return val;
}

template <typename T>
T raise_domain_error(const char* function, const char* message, const T& val, const policies::domain_error<user_error>&)
{
	return user_domain_error(function, message, val);
}

}

Essas são as funções chamadas para lançar um domain_error. Note que para cada política definimos uma função para tratá-la. Por questão de simplicidade, defini apenas as funções que tratam as políticas de domain_error, para overflow_error por exemplo, precisaríamos definir raise_overflow_error() com sobrecargas para cada política diferente.

O protótipo das funções pode ser modificado de acordo com o que você necessitar. Aqui defini quatro parâmetros:

  • function – o nome da função que causou o comportamento (erro)
  • message – a descrição do que causou o comportamento.
  • val – o valor que essa função deve retornar (quando aplicável)
  • const policies::domain_error& – utilizado para fazer a sobrecarga de função, onde X é a política a ser tratada.

Pode paracer meio desgastante porém depois de pronto, seu sistema complexo pode se beneficiar com transparência das políticas que não devem ser tantas.

A Boost Math por exemplo define apenas 6 tipos de erros e as mesmas 4 políticas que estou usando como exemplo.

Exemplo de uso

Agora suponhamos que tenhamos que implementar uma função que faz a divisão de dois elementos a e b (ohhh divisão!). Essa função, chamada divide, está sendo representada abaixo.

Como sabemos, divisão por zero não existe, então para uma solução elegante para nossa função super complexa, iremos utilizar as nossas políticas.

#include "policies.hpp"

template <typename T, typename Policy>
T divide(T a, T b, const Policy& pol)
{
	if (b == 0) {
		return policies::raise_domain_error("divide(a, b)", 
			"Cannot divide by zero", 
			T(0),   // Value to be returned  
			pol  // Our policy
		);
	}
	
	return a / b;
}

template <typename T>
inline T divide(T a, T b)
{
	return divide(a, b, policies::default_policy());
}

Como pudemos notar, declarei duas funções divide, uma aceitando dois parâmetros que serão computados e a outra aceitando um parâmetro a mais que é a política a ser utilizada. A versão de dois parâmetros na verdade é somente um forwarder para a outra passando a política padrão para ser utilizada. O valor padrão a ser retornado é 0, como podemos notar em T(0).

O “mau” comportamento ocorre quando b == 0, então, se essa condição for satisfeita, simplesmente lançamos um domain_error utilizando a função raise_domain_error.

Para o usuário da função isso é transparente se ele não quiser trabalhar com as políticas, já que uma política padrão é adotada e um valor padrão é retornado.

int main()
{	
	using namespace policies;
	
        // Default policy, zero is returned
	assert((divide(1, 0) == 0));

	typedef domain_error<errno_on_error> errno_pol;
	typedef domain_error<ignore_error>   ignore_pol;
	typedef domain_error<throw_on_error> throw_pol;
	
	// Testing errno_on_error policy, errno is set equal EDOM
	errno = 0;
	divide(1, 0, errno_pol());
	assert((errno == EDOM));
	
	// Testing ignore_error policy, zero is returned
	assert((divide(1, 0, ignore_pol()) == 0));
	
	// Testing throw_on_error policy, a std::domain_error is threw
	try {
		divide(1, 0, throw_pol());
		
		// Never is threw
		throw "error 0";
	}
	catch (std::domain_error&) {
		// FINE!!!
        // Sucessfully caught!
	}
	catch (...) {
                // Bad!
		throw "error 1";
	}
	
	return 0;
}

Se estiver tudo certo, programa acima deve (após ser compilado) ser executado sem nenhuma saída e com nenhuma exceção.

Ficou agora faltando demonstrar apenas como usar as políticas com user_error.

#include <iostream>
#include "policies.hpp"
#include "divide.hpp"

namespace policies {
template <typename T>
T user_domain_error(const char* function, const char* message, const T& val)
{
	std::cout << "Problem on calling function "<< function << std::endl;
	std::cout << "Value " << val << " will be returned " << std::endl;
	
	// DialogBox db(message); or anything you want
	
	return val;
}
}

int main()
{
	policies::domain_error<policies::user_error> usr_err;
	divide(1, 0, usr_err);
        /*  Saída:
  	     Problem on calling function divide(a, b)
             Value 0 will be returned 
         */

	return 0;
}

Como pudemos ver, basta apenas definirmos a função user_domain_error do modo que quisermos para que possamos usar essa política. No caso acima apenas imprimimos uma mensagem com duas linhas e retornamos val.

Arquivos

policies.hpp
divide.hpp
policies_example.cpp
user_error.cpp

Referências

Boost Math Policies
ilog2 implementation
Policy Classes On Generic Programming Techniques from Boost

Written by Murilo Adriano

29 de July de 2010 at 22:02

Meta-Programação por Templates

with 2 comments

Ultimamente estou me aventurando por essas terras misteriosas e estou fascinado.

Segundo o Wikipedia[1] Meta-programação por templates (TMP ou Template metaprogramming) é:

Template metaprogramming is a metaprogramming technique in which templates are used by a compiler to generate temporary source code, which is merged by the compiler with the rest of the source code and then compiled. The output of these templates include compile-time constants, data structures, and complete function

Ou seja TMP é uma técnica usada para gerar código-fonte em tempo de compilação. Isso quer dizer que, no momento da compilação, as chamadas à esse tipo de código são avaliadas, o código correspondente é gerado e o resultado já é conhecido, antes mesmo do programa ser executado!

Uau!! Isso é perfeito! Resolverá todos meus problemas…

Não é bem assim, como tudo é feito em tempo de compilação, os valores de entrada para esses algoritmos também devem ser conhecidos em tempo de compilação. Isso quer dizer que os valores de entrada devem ser constantes.

Eis um exemplo de código C++ usando essa abordagem:

#include <iostream>

template<int V>
struct Int { enum { value = V }; }; // representa um valor inteiro

template<int V>
struct bin : Int<(V % 10) + 2 * (bin<V / 10>::value)> {}; // algoritmo recursivo

template<> struct bin<0> : Int<0> {}; // base da recursão

int main()
{
	std::cout << "1000b == " << bin<1000>::value << "d\n";
	std::cout << "10001b == " << bin<10001>::value << "d\n";
	std::cout << "111b == " << bin<111>::value << "d\n";
	std::cout << "10b == " << bin<10>::value << "d\n";
	
	return 0;
}

Como ainda estou aprendendo a respeito, vou finalizar esse post nesse código. Caso queiram discutir o tema em questão, sintam-se a vontade!

Links:

[1] Template Metaprogramming no Wikipedia
Outro material sobre TMP
Boost MPL – biblioteca Boost que provê frameworks para meta-programação

Written by Murilo Adriano

19 de April de 2010 at 11:41

Posted in C/C++, Programação

Tagged with ,

A importância de declarar os destrutores como virtual em C++

with 5 comments

Funções Virtuais

Depois de muito tempo sem postar algo aqui vou falar sobre um especificador do C++ que muitas vezes é esquecido: a keyword virtual.

Essa keyword é utilizada para dizer que queremos que faça a avaliação da chamada dessa função em run-time, não em compile-time como é feito por padrão.

Mas no que isso pode ajudar?

#include <iostream>

struct base
{
	void funcao()
	{
		std::cout << "chamada da base\n";
	} 
	
	virtual void vfuncao()
	{
		std::cout << "chamada da base\n";
	}
};

struct derivada: public base
{
	void funcao()
	{
		std::cout << "chamada da derivada\n";
	} 
	
	void vfuncao()
	{
		std::cout << "chamada da derivada\n";
	}
};

int main()
{
	base* pt = new derivada;
	
	//nao virtual
	pt->funcao();
	
	//virtual
	pt->vfuncao();
	
	delete pt;
	
	return 0;
}

Saída:

chamada da base
chamada da derivada

Ou seja, quando uma função-membro (aka. método) é declarada como virtual na classe base, a checagem em run-time é feita e é possível encontrar a vfuncao() na classe

derivada

, assim chamando a função correta, o que não acontece com funcao(): apesar de ter uma função-membro com mesmo nome na classe derivada, é chamada a funcao() da classe base porque o ponteiro que a referencia é do tipo da classe base. Pra isso serve a keyword virtual (êba, vamos usá-la daqui pra frente!).

Mas e os destrutores virtuais?

Segue a mesma idéia e com um agrave de risco eminente de memory leaks!
Se na classe base não temos o destrutor declarado como virtual, temos um grande problema. Vejamos o exemplo:

#include <iostream>

class base 
{
	int* ptr;
public:
	base()
	{
		ptr = new int;
	}
	
	~base() 
	{
		delete ptr;
		std::cout << "'destruindo' base\n";	
	}
};

class derivada: public base
{
	int* dptr;
public:
	derivada() {
		dptr = new int;
	}
	~derivada()
	{
		delete dptr;
		std::cout << "'destruindo' derivada\n";
	}
};

int main()
{
	base* pt = new derivada;
	delete pt;
}

Saída:

'destruindo' base

Como podemos ver, apenas o destrutor da classe base é chamado, deletando o ponteiro ptr. Mas e o dptr declarado em derivada e alocado em seu construtor? Leaked! Se perdeu!

A solução é declarar o destrutor da classe base como virtual e a mágica acontece.

#include <iostream>

class base 
{
	int* ptr;
public:
	base()
	{
		ptr = new int;
	}
	
	virtual ~base() 
	{
		delete ptr;
		std::cout << "'destruindo' base\n";	
	}
};

class derivada: public base
{
	int* dptr;
public:
	derivada() {
		dptr = new int;
	}
	~derivada()
	{
		delete dptr;
		std::cout << "'destruindo' derivada\n";
	}
};

int main()
{
	base* pt = new derivada;
	delete pt;
}

Saída:

'destruindo' derivada
'destruindo' base

Conclusão

É aconselhável que seus destrutores sejam virtuais e que pondere nas outras funções, pois tudo que deixa de ser feito em compile-time e passa a ser feito em run-time, gera overhead na sua aplicação (é… nem tudo é maravilha). Até a próxima!

Written by Murilo Adriano

12 de February de 2010 at 14:50

Posted in C/C++, Programação

Tagged with ,

Static Template Matrix – uma abordagem diferente

with 5 comments

Static Template Matrix?

C++
De fato eu nunca vi esse nome em outro lugar, mas foi o nome que eu pensei que se aproxima mais da implementação que veremos nesse post.

Trata-se template de uma matriz (matrix) de duas dimensões de tamanho constante (static). Uma das diferenças dessa matriz é que ao invés de se usar o convencional operator[] para acessar os elementos, nessa abordagem iremos utilizar o operator().

Motivações

Na realidade, não tem muito mais utilidades práticas do que matrizes normais. Pode ser que pode servir em algo para você. Então minha motivação é puramente de aprendizagem, principalmente da linguagem.

matrix.h

#ifndef _CLASS_MATRIX_H__
#define _CLASS_MATRIX_H__

/**
 * matrix.h
 * Defines a template type 'matrix' wich is a constant-sized 2-dimensioned
 * 	matrix and generic typed elements.
 *
 * author: Murilo Adriano Vasconcelos
 * website: https://murilo.wordpress.com
 */

/**
 * T = value type
 * R = number of rows
 * C = number of columns
 */
template <typename T, unsigned R, unsigned C>
class matrix
{
    //data matrix
    T data[R][C];

public:
    //type of the current matrix
    typedef matrix<T, R, C> type;
    typedef T            value_type;

    //quantity of rows
    const unsigned rows;
    //quantity of columns
    const unsigned columns;

    //default ctor
    matrix(): rows(R), columns(C) {};

    /*ctor wich initializes the matrix filled
      with 'value' elements */
    matrix(const T& value);

    //copy ctor: copies the matrix 'tocopy'
    matrix(const type& tocopy);

    /*gets and/or set the value of the element
      in the row 'i', column 'j' */
    T& operator()(unsigned i, unsigned j);

    /*gets the value of the element in the
      row 'i', column 'j' */
    const T& operator()(unsigned i, unsigned j) const;

    //copy operator
    type& operator=(const type& tocopy);
};

/* class functions definition */

template <typename T, unsigned R, unsigned C>
matrix<T, R, C>::matrix(const matrix& tocopy): rows(R), columns(C)
{
    for (unsigned i = 0; i < R; i++)
        for (unsigned j = 0; j < C; j++)
            data[i][j] = tocopy(i, j);
}

template <typename T, unsigned R, unsigned C>
matrix<T, R, C>::matrix(const T& value): rows(R), columns(C)
{
    for (unsigned i = 0; i < R; i++)
        for (unsigned j = 0; j < C; j++)
            data[i][j] = value;
}

template <typename T, unsigned R, unsigned C>
matrix<T, R, C>& matrix<T, R, C>::operator=(const matrix& tocopy)
{
    for (unsigned i = 0; i < R; i++)
        for (unsigned j = 0; j < C; j++)
            data[i][j] = tocopy(i, j);

    return *this;
}

template <typename T, unsigned R, unsigned C>
T& matrix<T, R, C>::operator()(unsigned i, unsigned j)
{
    return data[i][j];
}

template <typename T, unsigned R, unsigned C>
const T& matrix<T, R, C>::operator()(unsigned i, unsigned j) const
{
    return data[i][j];
}

#endif

main.cpp

#include "matrix.h" //our matrix
#include <iostream>  //cout'ing
#include <string>   //for strings

int main()
{
        //declares a 3x3 matrix filled with "nil"
        matrix<std::string, 5, 5> mat("nil");

        //modifies the value in the position (1, 1) to C++
        mat(1, 1) = "C++";

        //we can use the const atribbutes rows and columns
        //to check our limits
        for (unsigned i = 0; i < mat.rows; i++) {
                for (unsigned j = 0; j < mat.columns; j++) {
                        //getting, setting the value of matrix elements
                        //is the same way
                        std::cout << mat(i, j) << ' ';
                }

                std::cout << '\n';
        }

        std::cout << '\n';

        matrix<int, 3, 3> ones(1), identity;
        //copy
        identity = ones;

        //so silly
        for (unsigned i = 0; i < identity.rows; i++) {
                for (unsigned j = i; j < identity.columns; j++) {
                        if (i != j) {
                                identity(i, j) = identity(j, i) = 0;
                        }
                }
        }

        for (unsigned i = 0; i < identity.rows; i++) {
                for (unsigned j = 0; j < identity.columns; j++) {
                        std::cout << identity(i, j) << ' ';
                }

                std::cout << '\n';
        }

        return 0;
}

Como vocês puderam notar, a forma de acesso e modificação (get, set) dos elementos da matriz é da mesma forma. Outra coisa que podemos notar é que passamos o tipo e a quantidade de linhas e colunas direto no template. A quantidade de linha e colunas passada deve ser uma constante.

Bom, até mais.

obs.: Eu odeio profundamente o source highlight bugado do WordPress que converte todos os meus < e > e etc. para &lt; &gt;. Por isso os fontes acima não estão com hl.

Fixed!

Agora sim, até mais.

Written by Murilo Adriano

12 de October de 2009 at 21:08

C++: Uma introdução a Threads com boost::thread

with 10 comments

boostPara quem ainda não conhece, Boost é um conjunto de bibliotecas C++ multiplataforma que trabalha bem com a STL e é muito bem aceita na comunidade. Já existem dez bibliotecas Boost incluídas no C++ TR1 e que estarão no C++0x além de muitas outras que estarão no C++0x e que atualmente não estão no TR1.

Para instalar o boost siga esse Getting Started.

Fazer threads com boost::thread é muito simples. Basta criar uma função ou um functor e passar pro construtor da classe boost::thread:

class functor
{
public: void operator()() {};
};

functor::operator()()
{
	/* content */
}

void function()
{
	/* content */
}

functor obj;
boost::thread tObj(obj); //passa uma cópia de obj
boost::thread tObjRef(boost::ref(obj)); //passa uma referência para obj
boost::thread tFunc(&funcao);

// aguarda a finalização das threads
tObj.join();
tObjRef.join();
tFunc.join();

//ou pode-se criar grupos de threads

boost::thread_group my_threads;
my_threads.create_thread(obj);
my_threads.create_thread(boost::ref(obj));
my_threads.create_thread(&function);

my_threads.join_all(); // aguarda o grupo de threads retornar

Para ilustrar como funciona, fiz dois programinhas simples:

cin.cpp

#include <iostream>
#include <boost/thread/thread.hpp>
#include <boost/thread/xtime.hpp>

//determina se o usuário já entrou com o número
bool flag = false;

struct in
{
	int n;

	in() : n(0) {};

	void operator()()
	{
		//try to read the number
		while (!(std::cin >> n)) {
			std::cin.clear();
			std::cin.ignore(10000, '\n');

			std::cout << "Enter a valid number" << std::endl;
		}
		
		flag = true;
	}

};

void my_alarm()
{
	while (!flag) {
		//shows the message
		std::cout << "Enter a number" << std::endl;
		
		boost::xtime time;
		boost::xtime_get(&time, boost::TIME_UTC); //current time

		time.sec += 5; //adds 5 secs to time
		//sleep 5 secs
		boost::thread::sleep(time);
	}

	//exiting function
	std::cout << "Alarm finished" << std::endl;
}

int main()
{
	in test;
	
	//create the two threads
	boost::thread tin(boost::ref(test));
	boost::thread tal(&my_alarm);

	// wait
	tin.join();
	tal.join();

	//show the user's number
	std::cout << "Ok " << test.n << std::endl;
	
	return 0;
}

Esse programa é composto por duas threads (fora o fluxo principal) uma que é responsável por fazer a leitura de um número e a outra que fica a cada 5 segundos mostrando uma mensagem “Enter a number” enquanto o usuário não digitar um número válido. Quando o usuário digita um número válido, a flag é setada e os loops das duas threads são finalizados. Finalmente quando as duas threads são finalizadas, o fluxo normal de main() é reestabelecido e é mostrado o número digitado pelo usuário.

Lembrando que para usar boost::thread é necessário “linkar” a biblioteca ao programa. No caso do Linux é necessário linkar também a biblietca pthread em Windows “linkar” a win32api. No meu caso preferi usar linkagem estática.

murilo@blacksheep:~/programacao/boost$ g++ cin.cpp libboost_thread-gcc43-mt-1_39.a -o cin -lpthread
murilo@blacksheep:~/programacao/boost$ ./cin
Enter a number
Enter a number
naoehumnumero
Enter a valid number
Enter a number
4
Alarm finished
Ok 4

O outro exempo que fiz é um programinha que faz a multiplicação de duas matrizes e “simultaneamente” (depende de processador e SO) mostra uma barra de progresso com o progresso da operação.

multiplica.cpp

#include <iostream>
#include <boost/thread/thread.hpp>
#include <boost/thread/xtime.hpp>

const int MAX = 1000;

int A[MAX][MAX];
int B[MAX][MAX];
int C[MAX][MAX];

int porcentagem = 0;

void multiplica()
{
	int soma;	
	
	for (int i = 0; i < MAX; i++) {
		for (int j = 0; j < MAX; j++) {
			soma = 0;
			for (int k = 0; k < MAX; k++) {
				soma += A[i][k] * B[k][j];
			}
			
			C[i][j] = soma;			
		}
		if (i % 10 == 0) porcentagem++;
	}
}

void barra_de_progresso()
{
	int atual = 0;
	while (porcentagem != 100) {
		if (atual != porcentagem) {
			if (porcentagem % 10 == 0) {
				std::cout << "\b\b" << porcentagem << '%';
			}
			else std::cout << '=' << std::flush;
			
			atual++;
		}
	}
}

int main()
{
	boost::thread tMultiplica(&multiplica);
	boost::thread tProgresso(&barra_de_progresso);
	
	boost::xtime inicio, fim;
	boost::xtime_get(&inicio, boost::TIME_UTC);
	
	std::cout << "Iniciando a multiplicação de matrizes" << std::endl;
	
	tMultiplica.join();
	tProgresso.join();
	
	boost::xtime_get(&fim, boost::TIME_UTC);
	
	std::cout << "\n\nProcesso terminado em " << 
		(fim.sec - inicio.sec) << " segundos" << std::endl;
	
	return 0;	
}

Saída:

murilo@blacksheep:~/programacao/boost/thr$ g++ multiplica.cpp libboost_thread-gcc43-mt-1_39.a -lpthread -o multiplica
murilo@blacksheep:~/programacao/boost/thr$ ./multiplica
Iniciando a multiplicação de matrizes
=======10%=======20%=======30%=======40%=======50%=======60%=======70%=======80%=======90%=======100%

Processo terminado em 43 segundos

Claro que o que menos nos importa nesse momento é o valor das matrizes e a multiplicação em si, por isso nem me dei ao trabalho de inicializar as matrizes nem de fazer nada com a matriz resultante.

Acredito que para uma introdução está bom e deu pra mostrar como é fazer programas com boost::thread com esses simples exemplos. Mais exemplos você pode encontrar na pasta libs do boost.

Links:

Written by Murilo Adriano

6 de August de 2009 at 18:06

Posted in C/C++, Programação

Tagged with , , ,

Coisas simples de se fazer em C++ que alguns ainda complicam #2 – Aplicar funções aos elementos de um contêiner STL

with 5 comments

Bom, essa é a segunda parte da série e vamos falar sobre uma maneira simples de aplicar uma função à todos os elementos de um contêiner STL (vector, list, deque, stack, etc) ou à todos os elementos de uma std::string.

Para isso usaremos a função std::transform().

No nosso primeiro exemplo, usaremos transform() com uma função que retorna o quadrado de um número.

#include <iostream> //cout
#include <algorithm> //transform()
#include <vector>

using namespace std;

int quadrado(int num)
{
        return num * num;
}

int main()
{
        vector<int> vetor;
        for (int i = 0; i < 10; i++) vetor.push_back(i);
        
        transform(vetor.begin(), //iterator pro incio do range desejado
                vetor.end(), //iterator para o fim do range
                vetor.begin(), //iterator para conteiner o destino 
                quadrado //função a ser aplicada
        );
        
        //imprime os valores.
        vector<int>::iterator it;
        for (it = vetor.begin(); it != vetor.end(); it++) {
                cout << *it << ' '; 
        }
        cout << endl;
        return 0;
}

Saida:

0 1 4 9 16 25 36 49 64 81

No segundo exemplo, mostramos como converter uma std::string para minúsculo com transform():

#include <iostream> //cout
#include <algorithm> //transform()
#include <string>
#include <cctype> //tolower, toupper

using namespace std;

int main()
{
        string url = "HTTP://MURILO.WORDPRESS.COM";                
        transform(url.begin(), url.end(), //origem
                url.begin(), //destino
                ::tolower); //função tolower da <cctype> (necessário ::)
        
        cout << url << endl; 
        return 0;     
}

Saída

https://murilo.wordpress.com

transform() também aceita funções com dois parâmetros, para que possamos aplicar uma função aos valores de dois contêineres e armazenarmos em um:

#include <iostream> //cout
#include <algorithm> //transform()
#include <complex>
#include <vector>

using namespace std;

complex<int> func(int real, int imag)
{
        return complex<int>(real, imag);
}

int main()
{
        vector<int> real; //parte real
        vector<int> imag; //parte imaginaria
        
        real.push_back(1);
        real.push_back(3);
        real.push_back(152);
        
        imag.push_back(5);
        imag.push_back(8);
        imag.push_back(12);
        
        vector< complex<int> > complexo(3); //reserva 3 posicoes
        
        transform(real.begin(), real.end(), //origem
                imag.begin(), //origem do segund
                complexo.begin(), //destino
                func //função
        ); 
        
        vector< complex<int> >::iterator it;
        for (it = complexo.begin(); it != complexo.end(); it++) {
                cout << *it << endl;
        }
      
        return 0;
}

Saída:

(1,5)
(3,8)
(152,12)

Até a próxima.
Ver a primeira parte da série.

Written by Murilo Adriano

27 de July de 2009 at 19:16

Posted in C/C++, Programação, STL

Tagged with ,

Coisas simples de se fazer em C++ que alguns ainda complicam #1 – Imprimir valores de contêineres STL

with one comment

Bom, vou fazer uma série de posts dizendo sobre coisas simples de se fazer em C++ que muitos ainda complicam. A idéia surgiu do post de um colega.

O primeiro post dessa série é sobre como imprimir facilmente todos os valores de um contêiner STL.

Iremos usar para isso, ostream_iterators que fornecem iterators para escrita em streams de saída. ostream_iterator está declarado em .

Vamos ao nosso exemplo:

#include <iostream> //para cout
#include <iterator> //para ostream_iterator
#include <vector>

using namespace std;

int main()
{
        vector<int> v(5);
        v[0] = 1;
        v[1] = 1;
        v[2] = 2;
        v[3] = 3;
        v[4] = 5;
        
        //copia todo o vetor [begin() -> end()) pro cout com um  ", " de delimitador
        copy(v.begin(), v.end(), ostream_iterator<int>(cout, ", ")); 
}

Resultado:


1, 1, 2, 3, 5,

A seguir um exemplo mais funcional, um gerador de arquivos CSV. Ao invés de usarmos o ostream_iterator(cout, ","), utilizaremos ostream_iterator(arquivo, ";") que diz que que queremos passar strings para serem escritas em um arquivo de texto com o delimitador ";":

#include //para ofstream
#include //para cerr
#include //para ostream_iterator
#include
#include

#define DELIMITADOR “;”

using namespace std;

int main()
{
typedef vector< vector > vvs;
vvs csv(3);

//preenchendo
vector v;
v[0] = “‘Murilo Adriano'”;
v[1] = “‘muriloufg(at)gmail(dot)com'”;
v[2] = “8888-8888”;
v[3] = “‘murilo.wordpress.com'”;
csv[0] = v;

vector v2(4);
v2[0] = “‘Outra Pessoa'”;
v2[1] = “‘outoemail(at)outrohost(dot)com'”;
v2[2] = “9999-9999”;
v2[3] = “‘website.com'”;
csv[1] = v2;

vector v3(4);
v3[0] = “‘Foo Bar'”;
v3[1] = “‘foo(at)bar(dot)com'”;
v3[2] = “0000-0000”;
v3[3] = “‘foobar.com'”;
csv[2] = v3;

//tenta abrir o arquivo
ofstream arquivo(“planilha.csv”);
if (arquivo.is_open()) {
vvs::iterator it;
for (it = csv.begin(); it != csv.end(); it++) {
//escreve os elementos de cada item do vetor no arquivo
copy((*it).begin(), (*it).end(),
ostream_iterator(arquivo, DELIMITADOR));

//quebra uma linha
arquivo << '\n'; } arquivo.close(); // fecha o arquivo } else { //se não foi possível abrir o arquivo cerr << "Falha ao abrir o arquivo\n" << endl; } } [/code] Após rodar o programa, abra o arquivo gerado no seu software de planilha eletrônica preferido e veja o resultado: [caption id="attachment_185" align="aligncenter" width="534" caption="Arquivo gerado pelo programa acima aberto no OpenOffice"]Arquivo gerado[/caption]

É isso aí pessoal, nos próximos posts exploraremos mais o C++ e STL.
Até a próxima.

Written by Murilo Adriano

26 de July de 2009 at 14:21

Posted in C/C++, Programação

Tagged with , , , , , ,