You are currently browsing the category archive for the 'CPP' category.
Apesar dos templates para hash_set e hash_map não serem padronizados, ambos são relativamente comuns, estando disponíveis tanto na implementação GNU (ie, no GCC) quanto no Visual C++ no Windows. Existem algumas diferenças, como o namespace um pouco mais escondido no GNUC (__gnu_cxx vs stdext) e alguns métodos que existem em um mas não em outro (p.ex.: reverse_iterator que não existe no GCC); mas a maioria das vezes servem para o serviço.
Um problema comum com essas classes no GCC é um erro meio criptico quando tentamos usar __gnu_cxx::hash_set<std::string> ou __gnu_cxx::hash_map<std::string>:
/usr/include/c++/4.2/ext/hashtable.h:595: error: no match for call to ‘(const __gnu_cxx::hash<std::basic_string<char, std::char_traits, std::allocator > >) (const std::basic_string<char, std::char_traits, std::allocator >&)’
Este erro ocorre porque a libstdc++ não define uma função de hash para std::string. A solução é definir uma (O RLY?). Felizmente a biblioteca padrão define uma função de hash para strings do tipo char*, então podemos aproveitá-la:
struct string_hash : public std::unary_function<std::string,size_t>
{
size_t operator() (const std::string &v) const
{
return __gnu_cxx::hash()(v.c_str());
}
};
typedef __gnu_cxx::hash_set<std::string,string_hash> string_hash_set;
Para obter ponteiros a métodos ou funções sobrecarregadas, como exemplo:
class Foo
{
public:
void bla(int a);
int bla() const;
};
Se tentarmos usar o nome da função diretamente, o compilador não saberá a qual dos métodos você se refere. Para sair da ambiguidade, necessitamos usar a “assinatura” (os tipos dos parâmetros e do valor retornado) da função indiretamente, possivelmente com o uso de variáveis temporárias:
void (Foo::*ptr1)(int)= &Foo::bla; int (Foo::*ptr2)() const= &Foo::bla;
Palavras-chave: C, pré-processador, macros, macro, define, gcc
O pré-processador do GCC define uma série de macros por default. Muitas delas podem lhe ser útil, caso se deseje que um determinado bloco de código seja compilado para apenas uma determinada arquitetura, por exemplo. Para conhecer estas macros, basta perguntar ao pré-processador:
$ echo foo |gcc -dM -E - (...) #define __VERSION__ "4.1.2 20061115 (prerelease) (Debian 4.1.1-21)" #define i386 1 #define __linux__ 1 #define __gnu_linux__ 1 #define __i486__ 1 #define unix 1 #define __i386__ 1 #define __SIZE_TYPE__ unsigned int #define __ELF__ 1 #define __FLT_HAS_QUIET_NAN__ 1 #define __FLT_MAX_10_EXP__ 38 #define __LONG_MAX__ 2147483647L #define __FLT_HAS_INFINITY__ 1 #define linux 1 (...) $
A saída acima, mostra algumas das macros definidas pelo pré-processador i386-linux do Debian Etch. No pré-processador para a arquitetura arm-palmos temos as seguintes macros:
$ echo foo |arm-palmos-gcc -dM -E - (...) #define __palmos__ 1 #define __FLT_MIN__ 1.17549435e-38F #define __CHAR_BIT__ 8 #define __WCHAR_MAX__ 2147483647 #define __DBL_DENORM_MIN__ 4.9406564584124654e-324 #define __FLT_EVAL_METHOD__ 0 #define __SIZE_TYPE__ long unsigned int #define __ELF__ 1 #define __DBL_MIN_10_EXP__ (-307) #define __FINITE_MATH_ONLY__ 0 #define __ARMEL__ 1 #define __GNUC_PATCHLEVEL__ 1 #define __FLT_RADIX__ 2 (...) $
Palavras-chave: stdout, stderr, verbose, terminal, console, /dev/null Uma forma de impedir que seu programa imprima na tela, seja por linkar com uma biblioteca que abusa da verbosidade ou para garantir que um daemon não polua seu terminal quando for para background, é fechando a saída padrão (stdout) e a saída de erro (stderr), como mostrado no exemplo seguinte:
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
int main(void)
{
int fd = open("/dev/null", O_WRONLY);
close(1); // stdout
close(2); // stderr
dup(fd); // Duplica o descritor fd que corresponde a /dev/null
dup(fd); // usando o menor numero disponivel, no caso, 1 e 2
printf("No verbosity anymore!\\n"); // Nao imprime nada na tela
return 0;
}
Apenas fechar as saídas não é o bastante, senão um printf ou um cout (C++), que por padrão escrevem em stdout, iriam escrever em um descritor inválido. Direcionar a saída para o /dev/null é uma boa opção, já que ele descarta tudo que é escrito nele. Vale observar que a implementação mostrada nesta dica não é thread-safe.
Palavras-chave: warning, unused parameter, variable
Em alguns casos temos que implementar uma função (na maioria dos casos uma callback) cujo o protótipo já é definido pela API utilizada. Alguns parâmetros podem ser desnecessários, mas geram warnings de compilação caso não forem utilizados. O exemplo a seguir gera um warning quando compilado com o gcc:
void my_callback(char *data, int size)
{
printf("Size is %d\n", size);
return;
}
foo.c:2: warning: unused parameter 'data'
Podemos contornar esta situação de duas formas. A primeira e mais portável é fazer um cast para o tipo void:
void my_callback(char *data, int size)
{
(void) data;
printf("Size is %d\n", size);
return;
}
A segunda forma é usar uma extensão do gcc:
void my_callback(char *data __attribute__((unused)), int size)
{
printf("Size is %d\n", size);
return;
}
Palavras-chave: non-constant initializers, dynamic stack allocation
Muitas vezes alocamos memória para um buffer baseado em um tamanho que é passado como parâmetro para a função. Como no seguinte exemplo:
void foobar (size_t size)
{
char *buffer = malloc(size * 2);
No gcc existe uma extensão pouco conhecida, mas bastante útil, chamada non-constant initializers. Usando este recurso, a função poderia ser simplificada para:
void foobar (size_t size)
{
char buffer[size * 2];
A variável é alocada na pilha com um tamanho dinâmico que depende do valor passado como parâmetro e a desalocação do buffer é feita no retorno da função.
Palavras-chave: C, C++, varargs, argumentos variáveis
Para escrever funções que aceitam quantidade variável de parâmetros, usa-se o mecanismo de stdargs do C. Para isso coloca-se “…” como último parâmetro na declaração da função e depois usa-se as funções de stdarg.h para acessar os valores passados quando a funcão é chamada.
-
É obrigatório a inclusão de pelo menos um parâmetro normal antes da parte variável. (ex.:
char *concat(const char *s, …)) -
Qualquer tipo de valor pode ser passado como parâmetro, mas é impossível determinar em runtime o tipo dos valores que uma função recebeu. Porém é possível criar formas de o código que chama a funcão comunicar os tipos dos valores passados. Por exemplo, a função printf() determina os tipos dos parâmetros recebidos pela string de formatação (%s para char*, %i para int etc); a função
concat()no exemplo espera que todos os parâmetros recebidos sejam strings e assim por diante. -
O número de parâmetros recebido também não pode ser determinado explicitamente. É necessário usar artifícios adicionais como passar uma variável extra com o número de valores sendo passados, usar o valor NULL como indicador de último parâmetro ou derivar a quantidade de valores a partir da string de formatação, como no caso do printf() e similares.
Exemplo:
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
char *concat(const char *s, ...)
{
va_list args;
char *tmp;
char *res;
size_t len= strlen(s);
// pega um handle ao início da lista de parâmetros
va_start(args, s);
// calcula o tamanho total de todas as strings
// pega o próximo parâmetro da lista, até chegar no NULL
while ((tmp= va_arg(args, char*))) {
len+= strlen(tmp);
}
va_end(args);
res= malloc(len+1);
if (!res) return NULL;
// cria a string concatenada
strcpy(res, s);
va_start(args, s);
// pega o próximo parâmetro da lista, até chegar no NULL
while ((tmp= va_arg(args, char*))) {
strcat(res, tmp);
}
va_end(args);
return res;
}
int main()
{
char *s= concat("hello", " ", "world", "!!!!", NULL);
puts(s);
free(s);
return 0;
}
Se você quer usar macros com número de parâmetros variável (por exemplo, uma macro que chama uma função com stdargs), veja o post sobre o assunto.
Palavras-chave: C++, C, C#, precompiled header, prefix header, g++, lento
Se seu programa em C++ demora séculos para compilar, considere usar um recurso do GCC 4 que acelera compilações de maneira bastante simples.
Um dos motivo porque programas C++ demoram tanto para compilar é a grande quantidade de headers a se incluir. Um programa com #include <gtkmm.h>, por exemplo, vai incluir literalmente milhares de arquivos, dentre eles os do gtkmm em si, glibmm, pangomm, atkmm, sigc++, gtk+, pango, atk, gdk, glib etc etc etc. Além disso, pela complexidade de linguagem, o processamento de headers C++ é demorado.
Como isso tudo é feito para cada arquivo fonte do programa, no final das contas é bem possível que boa parte do tempo de compilação seja gasto processando esse mesmo grupo de headers tantas vezes quanto o número de arquivos no seu projeto.
No GCC 4 foi incluído o recurso de pré-compilação de headers. Ou seja, você pode pré-compilar os headers em separado para que este passo não precise ser repetido para cada arquivo. Para pré-compilar um cabeçalho, simplesmente rode o gcc nele como se fosse compilar um arquivo fonte comum:
% g++ meuheader.h -I/path/para/diretorios/de/includes (se tudo der certo, um arquivo meuheader.h.gch será gerado)
Se o gcc encontrar a versão pré-compilada de um header enquanto procura por ele, esta será usada. Uma forma simples de se usar esse recurso no seu projeto é pré-compilar um “prefix header” que será automaticamente incluído no começo de todos os arquivos fonte de seu programa. Este arquivo deve conter #includes para todos os headers que seu programa precisa.
Segue uma receita de bolo para usar prefix headers pré-compilados:
- Crie um arquivo (por exemplo, prefix_header.h) que inclua todos os headers de bibliotecas que seu programa usa, ex.:
#include <list> #include <vector> #include <string> #include <map> #include <gtkmm.h> #include <cairo.h> #include <math.h>
- Compile prefix_header.h
% g++ prefix_header.h $(pkg-config gtkmm-2.4 cairo --cflags)
- Compile seu programa usando o prefix header
% make "CXXFLAGS=-include prefix_header.h"
Isso também funciona em C e Objective-C, mas a maior diferença pode ser notada no C++.
Palavras-chave: C++, gcc, g++, erro, compilacão, linker, linking
Na maioria dos casos, o erro
undefined reference to `vtable for Class'
ocorre quando falta a definicão de algum método declarado na classe.
Certifique-se que seu Makefile contém o arquivo com a definicão da classe onde ocorreu o erro e que todos os métodos declarados estejam definidos.
Se você quiser mais detalhes sobre o quê causa este erro, leia http://gcc.gnu.org/faq.html#vtables.
Palavras-chave: exceções, gdb, depuração, debug exceptions, catch
Ao depurarmos programas C++ o GDB costuma interromper a execução quando a exceção é pega (catch) ou já quando um signal é lançado e o programa é abortado. O problema é que esse momento é tarde demais para saber o que ocorreu e o stacktrace é inútil. O que queremos é que o GDB faça um break quando a exceção é lançada e para isso usa-se o comando catch throw. O mesmo comando pode ser usado para interromper a execução na captura (catch catch).
Palavras-chave: C, C++, CPP, macro, string, identificador
Para transformar o nome de um macro ou identificador em string:
#define STR(s) #s
Para transformar o valor de um macro ou identificador em string:
#define STRV(s) STR(s)
O seguinte exemplo ilustra a diferença:
#define STRV(s) STR(s)
#define STR(s) #s
int main()
{
puts("1: "STR(puts));
puts("2: "STR(123));
puts("3: "STR(__LINE__));
puts("4: "STRV(__LINE__));
return 0;
}
Resultado:
1: puts 2: 123 3: __LINE__ 4: 9
Note como STR() retorna um string com o nome do macro, enquanto que STRV() retorna o valor.







Comentários Recentes