You are currently browsing Alfredo Kojima's articles.

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, 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:

  1. 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>
    
  2. Compile prefix_header.h
    % g++ prefix_header.h $(pkg-config gtkmm-2.4 cairo --cflags)
    
  3. 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, arquivo texto, linhas, leitura, getline

Como dito num post anterior, a glibc possui uma função para ler linhas de comprimento arbitrário de um arquivo texto. Mas como esta função é especifica a glibc, alguns leitores pediram uma versão que faça o mesmo em qualquer sistema. Uma das muitas formas de fazer isto é:

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

char *pegalinha(FILE *f, char **buffer, size_t *buflen)
{
  char *ptr;
  char *tmp;
  int c;
  if (!*buffer || !*buflen)
  {
    *buflen= 32;
    *buffer= (char*)malloc(*buflen);
    if (!*buffer)
      return NULL;
  }

  ptr= *buffer;
  while ((c= fgetc(f)) != EOF) {
    *ptr++= c;
    if (c == '\\n') break;

    if (ptr - *buffer >= *buflen-1)
    {
      // aumenta o buffer um pouco mais
      tmp= (char*)realloc(*buffer, *buflen+32);
      if (!tmp)
        return NULL;
      // atualiza ptr para o caso do novo buffer estar em outro lugar
      ptr= tmp + (ptr - *buffer);
      *buflen+= 32;
      *buffer= tmp;
    }
  }
  *ptr= 0;
  return c==EOF ? NULL : *buffer;
}

int main(int argc, char **argv)
{
   FILE *file;
   char *linha= NULL;
   size_t tamlinha= 0;

   if (argc != 2) { printf("%s \\n", argv[0]); exit(1); }

   if (!(file= fopen(argv[1], "r"))) { perror(argv[1]); exit(1); }

   while (pegalinha(file, &linha, &tamlinha))
   {
     printf("%i:%s", tamlinha, linha);
   }
   if (linha)
     free(linha);
   fclose(file);
   return 0;
}

Palavras-chave: Python, split, tokenizacão, análise léxica, parser, strings, quotes, aspas

O módulo re possui a conveniente função split(), que faz o mesmo que o split() dos strings, mas aceita expressões regulares como separador. Isto pode ser aproveitado para fazer um “parser de pobre” para casos simples, como por exemplo, quebrar um texto em palavras, levando em conta “strings” entre aspas:

 >>> texto="lorem ipsum dolor 'hello world' 'mais texto'' assim'"
 >>> print filter(None,re.split("('(?:[^']|'')*')| ", texto))
['lorem', 'ipsum', 'dolor', "'hello world'", "'mais texto'' assim'"]

O filter() é necessário para remover espaços em branco e Nones que sobram no resultado. Como bônus, strings podem conter ' escapados com ''.

Palavras-chave: Unicode, utf8, SQLObject, TurboGears, erro

UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 50: ordinal not in range(128)

Este erro pode ocorrer quando se tenta executar queries que envolvem caracteres Unicode (na query em si ou no resultado) usando SQLObject (o módulo de banco de dados usado no TurboGears).

A solução é forçar o SQLObject a usar UTF-8 internamente e desativar conversões desnecessárias sendo feitas por ele. Para isso modifique sua URI de conexão para algo como:


sqlobject.dburi=
“mysql://root:@localhost:3306/fliki?use_unicode=1&sqlobject_encoding=utf8

Dependendo da versão do SQLObject pode ser necessário também adicionar o parâmetro charset=utf8 à URI.

Palavras-chave: C, linkagem estática, undefined reference, referência indefinida

Ao linkar programas que usam bibliotecas estáticas que por usa vez usam outras bibliotecas estáticas, pode ocorrer de o linker reclamar da falta de símbolos de uma das bibliotecas, apesar de eles estarem comprovadamente definidos em uma delas. Isso pode ocorrer por causa da ordem errada em que as bibliotecas foram passadas ao linker.

Apesar de o linker se virar para encontrar símbolos em bibliotecas dinâmicas, para as estáticas é necessário passá-las em ordem de dependência. Por exemplo, se seu programa usa a libpng.a que usa a libz.a internamente, o comando correto para linká-lo é:

gcc programa.c -oprograma /usr/lib/libpng.a /usr/lib/libz.a

Mas se a ordem estivesse invertida:

$ gcc programa.c -oprograma /usr/lib/libz.a /usr/lib/libpng.a
/usr/lib/libpng.a(png.o): In function `png_reset_crc': undefined reference to `crc32'
/usr/lib/libpng.a(png.o): In function `png_calculate_crc': undefined reference to `crc32'
/usr/lib/libpng.a(png.o): In function `png_calculate_crc': undefined reference to `crc32'
/usr/lib/libpng.a(png.o): In function `png_reset_zstream': undefined reference to `inflateReset'
collect2: ld returned 1 exit status

Palavras-chave: C, gcc, cpp, macros, …, varargs, número de parâmetros variável, variadic macros

Para escrever macros de função com número variável de argumentos:

#define DPRINT(fmt, ...)   printf("DEBUG: "fmt"\\n", ## __VA_ARGS__)

__VA_ARGS__ será substituído pelos parâmetros dados. E o ## entre a vírgula e __VA_ARGS__ se encarregará de remover a vírgula quando o número de argumentos for 0.

Exemplo:

#include <stdio.h>
#define DPRINT(fmt, ...) printf("DEBUG: "fmt"\\n", ##__VA_ARGS__)

int main()
{
  int i= 0, j= 0;
  DPRINT("chegou aqui");
  DPRINT("valor de i = %i, j = %i", i, j);
  return 0;
}

Nota: O Visual C++, a partir da versão 2005, passou a incluir o mesmo recurso.

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: for, iteração, índice

O for do Python é bastante conveniente para iterar listas, descartando a necessidade de variáveis de índice auxiliares. No entanto as vezes é necessário iterar uma lista e ter o índice de cada elemento além do elemento em si. A forma óbvia é algo como:

for i in range(len(lista)):
   elem= lista[i]
   print i, "=", elem

Mas algumas vezes isto pode ser inconveniente (na construção py:for do TurboGears, por exemplo). Nestes casos podemos usar o map() para criar uma lista de tuplas de cada elemento com seu índice:

>>> lista= ["maçã", "banana", "pêra"]
>>> for i, elem in map(None, range(len(lista)), lista):
>>>   print i, "=", elem
0 = maçã
1 = banana
2 = pêra

Atualização: Como comentado pelo Barbieri, a partir do Python 2.3 existe a função enumerate() que faz a mesma coisa:

>>> lista= ["maçã", "banana", "pêra"]
>>> for i, elem in enumerate(lista):
>>>   print i, "=", elem
0 = maçã
1 = banana
2 = pêra

Palavras-chave: C, memória, otimização, arrays, vetores, bitmap, pixmap

Ao se escrever código para manipulação de strings ou imagens, é comum percorrer grandes vetores de bytes. A maneira óbvia de fazer isto é algo no estilo de:

char *ptr= buffer;
for (i= 0; i < fim; i++)
      *ptr++= 0;

Mas percorrer memória byte a byte é bastante lento, por questões de alinhamento de memória, o fato dela ser endereçada por palavra. Por exemplo, um int em i386 e long em x86_64.

O exemplo abaixo inclui código que percorre por byte e por int, mas pode ser facilmente modificado para percorrer por longs:

#include <sys/types.h>
int main()
{
    char buffer[1000];
    uint32_t *iptr;
    char *ptr;
    int i, j;
    int max= 973;
    for (j= 0; j < 100000; j++)
    {
#ifdef lento
        ptr= buffer;
        for (i= max-1; i>=0; --i)
            *ptr++= 0;
#else
        iptr= (uint32_t*)buffer;
        // processa o vetor de 4 em 4 bytes,
        // arredondando o número de elementos
        for (i= max>>2-1; i>=0; --i)
            *iptr++= 0;
        ptr= iptr;
        // processa os últimos bytes restantes
        switch (max&((1<<2)-1))
        {
            case 3: *ptr++= 0;  // sem break
            case 2: *ptr++= 0;  // sem break
            case 1: *ptr++= 0;
              break;
        }
#endif
    }
    return 0;
}

Os tempos de execução para o código com percorrimento por palavras de 8, 32 e 64bits em uma máquina x86_64, são de: 0.190s, 0.100s e 0.025s, uma melhora de até 800%(!).

Obviamente, neste caso, um memset() seria mais rápido mas o ponto é que este conceito pode ser estendido para casos em que o memset() não serve. Ainda há espaço para mais otimizações, mas isto é deixado como exercício ;)

Obs.: lembre-se que o comprimento de long varia de acordo com a arquitetura e é prudente colocar trechos que dependem disso dentro de #ifdefs.

Leia também um artigo relacionado, sobre o aproveitamento da memória cache nesta dica do Eduardo.

Palavras-chave: OpenGL, Linux, multi-thread, drivers NVidia, crash, segmentation fault, segfault

Se seu programa explode em uma função OpenGL sem nenhum motivo aparente, verifique se a chamada está sendo feita desde a thread onde a OpenGL foi inicializada.

As bibliotecas que vêm com os drivers da NVidia (libGL.so) para X não são muito tolerantes com programas com multi-threading, enquanto a libGL.so da Mesa parece ser mais tolerante a isso. Isso pode levar à confusa situação de o programa rodar sem problemas em alguns sistemas e morrer em outros.

Nos casos em que tive esse problema, o segfault ocorreu na função glViewport(), mas é provável que ocorra com outras funções.

Não existe solucão mágica, mas um workaround — para ao menos testar a hipótese — é mudar o LD_LIBRARY_PATH para apontar ao libGL.so da xorg (se não a tem instalado, tente copiar de outro lugar etc). Verifique se a biblioteca correta está sendo usada com ldd e teste. Já a solucão definitiva é mover as chamadas OpenGL que acessam o mesmo contexto para uma única thread (ou protegê-las com mutexes).

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.

Palavras-chave: C, arquivo texto, fgets, linhas de comprimento arbitrário

O método típico para se ler um arquivo texto linha a linha é usando a função fgets() com um buffer estático. O problema é que nem sempre sabemos qual será o comprimento máximo das linhas e, de acordo com a Lei de Murphy, chutar um tamanho “grande o suficiente” pode não ser o suficiente na prática. Além da possibilidade de abusos ou conteúdo malicioso quando o arquivo é fornecido pelo usuário.

A maneira “correta” de se fazer isso é um pouco trabalhosa, mas felizmente a glibc possui a pouca conhecida função getline(). Esta função toma um buffer dinâmico e irá redimensioná-lo se necessário. É uma função específica da glibc, então se seu programa irá rodar em outras plataformas, não a utilize.

Exemplo:

#define _GNU_SOURCE // necessário porque getline() é extensão GNU
#include <stdio.h>
#include <stdlib.h>
int main()
{
  FILE *f= fopen("teste.txt", "r");
  size_t len= 100; // valor arbitrário
  char *linha= malloc(len);

if (!f)
  {
    perror("teste.txt");
    exit(1);
  }
  while (getline(&linha, &len, f) > 0)
  {
    printf("%s", linha);
  }
  if (linha)
    free(linha);
  fclose(f);
  return 0;
}

É uma pena que não seja uma função portável, mas é algo que pode economizar algumas linhas de código e neurônios quando escrevemos xu^Wprograminhas descartáveis em C.

Palavras-chave: HTML, escape, escapar, html_escape, htmlspecialchars, htmlentities

Funcões rápidas para escapar e desescapar caracteres especiais HTML (<, & etc)

from htmlentitydefs import codepoint2name as cp2nm, entitydefs as nm2cp
import re

def htmlescape(texto):
    return "".join([cp2nm.has_key(ord(x)) and
                   "&%s;"%cp2nm[ord(x)] or x for x in texto])

def htmlunescape(texto):
    return "".join([(x.startswith("&") and
                     x.endswith(";") and
                     nm2cp.has_key(x[1:-1]))
                    and nm2cp[x[1:-1]] or x
                   for x in re.split('(&\\\\w+;)', texto)])

Exemplo:

>>> htmlescape("a > x && y < 0")
'a &gt; x &amp;&amp; y &lt; 0'
>>> htmlunescape(htmlescape("a > x && y < 0"))
'a > x && y < 0'

Veja esta dica em Ruby.

Palavras-chave: Mac, embed framework, dylib, Cocoa, deployment, XCode

No OS X, bundles de aplicativos (as pastas .app) podem conter as bibliotecas dinâmicas e Frameworks que o programa usa. Assim sua aplicação passa a ser auto-contida: o usuário não precisa instalar bibliotecas extra que você usou, nem é necessário um instalador que copie essas bibliotecas no sistema do usuário.

Para isso, faça o seguinte:

  1. Compile o framework ou biblioteca usada com o atributo de diretório de instalação (Installation Directory) tendo o valor “@executable_path/../Frameworks”. Para configurar isso no XCode:
    1. Abra o projeto da biblioteca ou framework
    2. Vá em Project -> Edit Active Target
    3. No tab Build, selecione Configuration:All Configurations e Collection:Deployment
    4. Preencha o campo “Installation Directory” com @executable_path/../Frameworks
    5. Recompile

    Também é possível mudar este atributo em uma biblioteca já compilada, mas isto será tratado em um outro post.

  2. Copie suas dependências no diretório Contents/Frameworks do seu .app
    O XCode pode fazer isto automaticamente:

    1. Selecione o Target que corresponde à sua aplicação na lista Groups & Files e no combo Active Target
    2. Project -> New Build Phase -> New Copy Files Build Phase
    3. Em Destination: selecione Frameworks e deixe o resto como está
    4. Selecione o grupo “Copy Files” na lista Groups & Files e largue as bibliotecas e frameworks a incluir

Após estes passos, sua aplicação deverá conter suas bibliotecas em Aplicacao.app/Contents/Frameworks.

Add to Technorati Favorites

a