Murilo :P

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

Posts Tagged ‘tips

Arrays with negative indexes

with 2 comments

When we to deal with negative ranges in arrays and those ranges can be negative, we do shifts so we can use natural indexes. For example, if we’re given a range from -50 to 20, usually we allocate an array of 70 positions and when indexing an element we shift by +50 so the position -50 is actually stored on the position 0.

These days, while doing random stuff on the internet, I’ve figured that we can easily work with arrays not only starting from zero but starting from any integer we want. To illustrate that, I wrote this function which allocate sz elements of type T where the array’s first element is in min_index position:

template <typename T>
T* alloc(size_t sz, int min_index = 0)
{
	return (new T[sz]) - min_index;
}

An example of use:

int main()
{	
	char* p = alloc<char>(70, -50);
	
	p[-50] = 'h'; p[-49] = 'e'; 
	p[-48] = 'l'; p[-47] = 'l';
	p[-46] = 'o'; p[-45] = '\0';
	
	std::cout << &p[-50] << '\n';
	
	for (int i = -50; i < -45; ++i) {
		std::cout << p[i] << '\n';
	}
}

The result, as expected:

hello
h
e
l
l
o
Advertisements

Written by Murilo Adriano

29 de October de 2012 at 14:24

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

Tagged with , ,

C++ Idioms: SFINAE

leave a comment »

Introdução

Nesse post falarei sobre uma técnica (ou recurso) utilizada em  C++ que estou utilizando muito no meu projeto[0][1] na Boost Libraries[2] no GSoC, essa técnica é o SFINAE.

SFINAE[3] é uma sigla para “Substitution failure is not an error” que em uma tradução livre quer dizer “Falha na substituição não é um erro”. Segundo[3]:

SFINAE se refere a uma situação em C++ que uma substituição inválida de templates não é um erro.

Especificamente, na criação de um conjunto candidato para a resolução de sobrecarga, alguns (talvez todos) os candidatos deste conjunto pode ser resultante da substituição de argumentos templates deduzidos para os parâmetros de template. Se um erro ocorre durante a substituição, o compilador remove essa potencial sobrecarga do conjunto candidato ao invés de parar com um erro de compilação. Se restam um ou mais candidatos (i.é: pelo menos uma sobrecarga é válida), a resolução é feita e a invocação é bem formada.

Como funciona

Para entender melhor vejamos esse trecho de código:

template <typename T>
struct A
{
	typedef T type;
	T value;
};

template <>
struct A<bool>
{
};

template<typename T>
typename A<T>::type func(const A<T>& param)
{
	std::cout << param.value << std::endl;
}

Esse código compila sem warnings. A função template func() é sobrecarregada e retorna o tipo T. Mas você deve estar se perguntando, o que acontece quando T = bool? Esse é justo o ponto do SFINAE!
Para T = bool, typename A::type seria uma declaração inválida já que A não contém o tipo type declarado, porém, com SFINAE, essa sobrecarga é simplesmente descartada e para qualquer outro T que não bool, func(A) é uma chamada legal porque sua sobrecarga é gerada.

Aplicações

Como eu disse no começo deste post eu estou utilizando bastante SFINAE no meu projeto do Google Summer of Code com uma utilidade chamada enable_if[4] encontrado na Boost.

Enable_if é uma classe (ou struct) que “ativa” a declaração de algo (classe, função, etc) se uma condição for satisfeita por meio do SFINAE. Vou mostrar aqui uma maneira de fazer um enable_if like, e quem quiser saber mais sobre o enable_if da Boost é encorajado a ler[4][5].

Vamos ao código:

/* Cond é a condição de ativação
 * Type é o tipo que será declarado pelo membro type.
 */
template <bool Cond, typename Type = void>
struct enable_if_c
{
	typedef Type type;
};

/*
 * Especialização, quando Cond for false,
 * 	esse template vazio é invocado.
 */
template <typename Type>
struct enable_if_c<false, Type>
{
	// empty
};

Ou seja, sempre quando Cond for true, existirá um membro type declarado em enable_if_c, quando Cond for false o membro type não existirá.

Um exemplo de uso:

/*
 * Nossa função pega o bit mais significativo de value e retorna um bool.
 * 	***Note que nesse exemplo estamos assumindo que T é um tipo inteiro.***
 */

/*
 * Para retornar o bit mais significativo de um T com 4bytes (32 bits),
 * 	deslocamos 31 bits para a direita e fazemos um & com 1.
 */
template <typename T>
typename enable_if_c<sizeof(T) == 4, bool>::type msb(T value)
{
	std::cout << "Value have 4 bytes.\n";
	return (value >> 31) & T(1);
}

/*
 * Porém se T é um tipo de 1byte (8 bits) o deslocamento deve ser de
 * 	7 bits para pegarmos o bit mais significativo.
 */
template <typename T>
typename enable_if_c<sizeof(T) == 1, bool>::type msb(T value)
{
	std::cout << "Value have 1 byte.\n";
	return (value >> 7) & T(1);
}

Ou seja, a função de cima, é sobrecarregada para todos os tipos que tenham 32 bits (sizeof(T) == 4) e a de baixo é sobrecarregada para todos os tipos que tenham 8 bits (sizeof(T) == 1). Assim, no processo de resolução de sobrecarga, os outros tipos que não tem nem 8 nem 32 bits, são descartadas pois cairá na especialização enable_if_c que não possui um membro type (usado como retorno da função msb), comportamento citado anteriormente.

Para testar:

int main()
{
	int a = -3;
	unsigned b = 190000;
	char c = 129;
	uint16_t d = 10;
	uint64_t e = 10000000;
	
	std::cout << "Retorno int: " << msb(a) << std::endl;
	std::cout << "Retorno unsigned: " << msb(b) << std::endl;
	std::cout << "Retorno char: " << msb(c) << std::endl;
}

Se tentarmos executar com uma condição que não é true, teremos um erro em tempo de compilação, como podemos ver abaixo:

...
uint16_t d = 10;
uint64_t e = 10000000;

std::cout << "Ret: uint16_t: " << msb(d) << std::endl
std::cout << "Ret: uint64_t: " << msb(e) << std::endl;
...

/*
/Users/murilo/Documents/programming/blog/sfinae.cpp: In function ‘int main()’: /Users/murilo/Documents/programming/blog/sfinae.cpp:61: error: no matching function for call to ‘msb(uint16_t&)’ 
/Users/murilo/Documents/programming/blog/sfinae.cpp:62: error: no matching function for call to ‘msb(uint64_t&)’
*/

Alguns outros exemplos usando o enable_if da Boost você pode encontrar no meu projeto[1] na Boost onde utilizo enable_if aos montes.

Nesse post exemplifiquei SFINAE com funções, mas também é aplicável para classes. Um exemplo você pode ver aqui [6].

Até a próxima!

Referências

[0] – Estou no Google Summer of Code 2010https://murilo.wordpress.com/2010/05/03/estou-no-google-summer-of-code-2010
[1] – Source of Bits and Ints projecthttps://svn.boost.org/trac/boost/browser/sandbox/SOC/2010/bits_and_ints/boost/integer
[2] – Boost C++ Librarieshttp://www.boost.org/
[3] – Artigo “Substitution failure is not an error” na Wikipediahttp://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error
[4] – Boost enable_ifhttp://www.boost.org/doc/libs/1_43_0/libs/utility/enable_if.html
[5] – enable_if.hpphttp://www.boost.org/doc/libs/1_35_0/boost/utility/enable_if.hpp
[6] – static_sign_extend.hpp at Boost Trac – https://svn.boost.org/trac/boost/browser/sandbox/SOC/2010/bits_and_ints/boost/integer/static_sign_extend.hpp

Written by Murilo Adriano

19 de June de 2010 at 17:02

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

Tagged with , , , ,

Como programar em C Orientado a Objetos

with 14 comments

Olá, hoje eu vou dar um tempo na série “Coisas simples de se fazer em C++ que alguns ainda complicam” e irei falar sobre uma experiência minha tentando alguma maneira de programar em C orientado a objetos.

Sei que muitos vão pensar “pra quê isso?” ou dizer que é péssimo fazer isso ou coisa parecida, mas o intuito desse post é outro: é mostrar o que é possível fazer ou até mesmo enxergar alguma utilidade nisso. Nesse blog, procuro colocar coisas diferentes, curiosidades sobre programação e linguagens, porque acho que conteúdo “normal” já existem em muitos lugares na grande rede.

O que me instigou a fazer isso foi um post no CODARE “C: Escondendo o conteúdo de structs com tipos incompletos” de autoria do Thiago Santos, no qual ele cita sobre usar com orientação a objetos em C.

Li, achei interessante e quis, vamos dizer assim, dar um cara mais parecida de orientação a objetos ao que ele fez.

O primeiro passo foi pensar em como funcionam os famosos objetos.
Uma classe em uma linguagem orientada a objetos geralmente tem 2 tipos de elementos:

  • Elementos de classe: elementos (funções e variáveis estáticas) que só são criados uma vez, todos os objetos da classe tem acesso ao mesmo elemento.
  • Elementos de instância (ou objeto): elementos (atributos) que são criadas para cada objeto instanciado.

Ou seja:

class Animal
{
        int age;
        std::string specie;
        static int count;
public:
        int birthday();
};

No exemplo acima, age e specie são elementos de instância, ou seja, cada objeto têm o seus próprios. Já count e birthday() são de classe, pois todos os objetos dessa classe utilizarão o mesmo. O que ocorre no caso de funções é que na chamada da função, um ponteiro do objeto chamador é passado para a função. Isso faz com que não precise a cada instância de objeto criar uma nova função já que elas fazem a mesma coisa.

Algo como:

Animal animal, animal2;

//lembrando que é APENAS uma ilustração do que acontece
//não é assim que é realmente implementado mas é a mesma idéia
//uma função só para todos os objetos de uma classe

animal.birthday(); //vtable::animal::birthday(&animal);
animal2.birthday(); //vtable::animal::birthday(&animal2);

A função birthday() é a mesma para as duas chamadas, o que muda é o ponteiro para os dados de cada objeto.

A minha implementação seguiu a idéia do Thiago Santos: uma classe person que tem os atributos name e age funções para instanciar, imprimir o nome e idade e deletar o objeto.

Vamos então dar uma olhada no nosso headerperson.h:

#ifndef PERSON_H__
#define PERSON_H__

//Incomplete type declaration
typedef struct person_private person_private;

typedef struct person {
	//"private" data.
	person_private* data;

	//"class" functions
	void (*free)();
	void (*print)();
} person;

//instatiate a new person
person* new_person(const char*, int);

//pointer to the actual person in the context
person* __actual_person;

//sets the actual person
person* _(person* obj);

#endif

Nosso header contém a declaração incompleta de person_private leia aqui para saber o porquê, a definição da nossa “classe” person, as declarações das funções new_person() e _() e um ponteiro __actual_person.

Nossa “classe” person contém um ponteiro data para os dados que não poderão ser acessíveis através do objeto (name, age) além de ponteiros para funções (que serão nossos “métodos”).

A função new_person simplesmente instancia um objeto do tipo person.

O ponteiro __actual_person irá funcionar como o ponteiro passado para as funções de classe. Através do ponteiro saberemos qual objeto chamou a função.

A função _() seta o ponteiro __actual_person para que as funções sejam corretamente chamadas. Nota: não é thread safe. (hehehe)

Vamos ao nosso person.c:

#include "person.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

//private data... visible only by the functions below
struct person_private
{
	char* name;
	int age;
};

//a "manual destructor"
void free_person()
{
	free(__actual_person->data->name);
	free(__actual_person->data);
	free(__actual_person);
       
        puts("Person sucessfuly freed!\nBye");
}

//prints 
void print_person()
{
	printf("%s -: %d\n", __actual_person->data->name,
		 __actual_person->data->age);
}

person* new_person(const char* name, int age)
{

	//Allocate the object	
	person* new = (person*)malloc(sizeof(person));
	new->data = (person_private*)malloc(sizeof(person_private));
	
	//Initialize the data
	new->data->name = (char*)malloc(strlen(name) * sizeof(char) + 1);
	strcpy(new->data->name, name);

	new->data->age = age;

	//Set the functions pointers
	new->print = print_person;
	new->free = free_person;
	
	return new;
}

//must call the objects with this function
person* _(person* obj)
{
	__actual_person = obj;
	return obj;
}

O que temos aí é a função _() que seta o objeto atual (__actual_person) para que possa ser utilizado a função com o objeto certo, a função new_person que funciona como nosso construtor, as outras duas funções que são os “métodos da classe” print e free e a definição da estrutura person_private que contém os atributos da classe.
Os comentários ajudam no resto :).

Vamos então ver o main.c que é o nosso teste:

#include <stdio.h>
#include "person.h"

int main()
{
	//Instantiates 2 persons
	person* person1 = new_person("Murilo", 21);
	person* person2 = new_person("Rovane", 47);
	
	//print
	_(person1)->print();
	_(person2)->print();
	
	//free
	_(person1)->free();
	_(person2)->free();
}

Eis o resultado:

murilo@blacksheep:~/programacao/cobject$ gcc main.c person.c
murilo@blacksheep:~/programacao/cobject$ ./a.out
Murilo -: 21
Rovane -: 47
Person sucessfuly freed!
Bye
Person sucessfuly freed!
Bye

Nota: deve-se usar sempre a função _() para utilizar o objeto.

Fiquei pensando se tem como fazer uma espécie de gerador de classes nesse formato no próprio C (com macros por exemplo). Vou procurar saber se existe algo a respeito, são 04h40min da mardugada não estou mais com boas idéias.

Qualquer sugestão, bug, idéia, reclamação, estamos aí!

Written by Murilo Adriano

5 de August de 2009 at 04:50

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

Tagged with , , , , ,

Sobrecarga do operator++ para enums

with one comment

Mais uma trick com enums.

A sobrecarga de operadores podem facilitar o manuseio de enums. Abaixo está um exemplo de como pode ser útil a sobrecarga do operator++.

#include

using std::cout;
using std::endl;

enum Semana
{
DOM,
SEG,
TER,
QUA,
QUI,
SEX,
SAB
};

Semana& operator++(Semana& dia) //pré-incremento
{
return dia = static_cast((dia + 1) % 7);
}

Semana& operator++(Semana& dia, int) //pós-incremento
{
return dia = static_cast((dia + 1) % 7);
}

int main()
{
Semana dia = SEX;
cout << dia << endl; // SEX (5) cout << ++dia << endl; // SAB (6) cout << ++dia << endl; // DOM (0) dia++; // SEG (1) cout << dia << endl; } [/code] Quando chegamos no último dia da semana (SAB) e chamamos o operator++, obtemos o primeiro dia da semana graças ao operador módulo (%). Referência.

Written by Murilo Adriano

25 de July de 2009 at 00:35

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

Tagged with , , ,