Desenvolvimento - C/C++

Acesso multiplataforma a banco de dados com o C++ utilizando DTL (Database template library)

Com certa frequência aparecem perguntas sobre como acessar bancos de dados usando o C/C++ e principalmente como adequar programas para rodar estes procedimentos no Windows, Linux e outros S.O.s. Para ajudar nesta questão, escrevi este pequeno artigo...

por Adenilson Cavalcanti da Silva



1) Introdução

O cenário de acesso a arquivos de bancos de dados é um universo rico em oportunidades para trabalhos profissionais, pois esta é a área onde você lidará com o item mais importante para qualquer empresa: a informação. Na atualidade, existem muitos profissionais trabalhando nesta área, mas poucos são realmente BONS naquilo que fazem.

Certamente, trabalhar com bancos de dados não é uma das tarefas mais gloriosas da programação (especialmente se você é apaixonado por jogos, como eu). Mas é uma fonte de recursos segura e constante pois sempre há alguém conhecido precisando de um banco de dados. Assim, caso você ande meio sem dinheiro e ainda não acabou de programar seu novo jogo de tiro em primeira pessoa com suporte a T&L via hardware, I.A. com redes neurais, agentes autonomos e Modelos 3-D foto-realísticos, fazer um banco de dados sempre é uma oportunidade para pagar aqueles livros de OpenGL/DirectX e 3-D Studio Max.

No mercado existem várias alternativas de acesso a bancos de dados, cada uma com seus recursos únicos e promessas de facilitar sua vida. Temos como exemplo a excelente BDE (Borland DataBaseEngine) do Delphi/C++ Builder, os MFC-records-set do Visual C++ ou VB com toda uma miríade de compontes e servidores ActiveX. Infelizmente, todos têm em comum algumas propriedades indesejáveis: a) São privativos (você deve comprar os compiladores para utilizar) ou b) Não são multiplataforma.

Mas existem alternativas. O que apresento aqui é uma biblioteca gratuita de acesso a bancos de dados via ODBC (portanto os programas gerados não exigem 5 a 8 disquetes para instalação, como a BDE) e ainda por cima multiplataforma. Não irei ensinar aqui questões sobre como estruturar tabelas, regras de normalidade ou comandos em SQL, pois estes são tópicos amplos e você deverá pesquisar em um bom livro de SGBD.

O nome do nosso jogador é DTL: DatabaseTemplateLibrarie.

2) Conceitos

Será necessário nos situarmos sobre algumas tecnologias antes de prosseguir. Aviso aos navegantes que este artigo exige familiaridade com tópicos avançados da OOP com C++ como templates, interadores e outros. Caso você nem saiba o que é um template, definitivamente as coisas serão mais difíceis.

2.1. A ODBC

No Windows, o padrão do sistema para conexão a bases de dados é através da ODBC (Open Data Base Conectivity). Trata-se de uma engine para possibilitar o acesso a diversos tipos de bancos de dados através de uma interface comum (as mesmas funções e estruturas de dados são utilizadas para acessar arquivos com formato diferentes de diversos bancos de dados).

Esta interface comum é estabelecida por drivers, componentes de software com os procedimentos necessários para conexão com os arquivos físicos de um banco de dados. Apesar de diversos produtos seguirem o modelo relacional (dados em tabelas) a implementação da maneira como estas tabelas são armazenadas em arquivos físicos varia de forma radical de um produto para outro.

Com alguns os drivers da ODBC é possível também criar arquivos novos, alémde consulta e alterações. Assim, com a mesma API (ApplicationProgram Interface), pode-se acessar arquivos com distintas estruturas com o mesmo código. Uma vantagem da ODBC (agora chamada MDAC) é o fato de ser livre de royalties, ou seja por default toda máquina Windows já vem com ela e você pode distribuir aplicativos que fazem uso dela sem pagar taxas.

Já existem versões da ODBC para outras plataformas:

The unixODBC Project: um grupo onde está sendo desenvolvida uma versão da ODBC gratuíta.http://www.unixodbc.org/unixODBC.html

IODBC: outra implementação para unixhttp://www.iodbc.org/

Sua arquitetura básica é a seguinte:

Listagem 1: Arquitetura do ODBC

+------------------+
| Programa ODBC|Seu programa
+------------------+
+------------------+
| Adm. de Driver  |  odbc.dll
+------------------+
+------------------+
|ODBC Driver   | Aqui entra o driver que acessa o arquivo
+------------------+
+------------------+
| Fonte de dados|  O arquivo onde estão os dados (ex: teste.mdb)
+------------------+

2.2. A STL

No final da década de 70, Alexander Stepanov observou como alguns algoritmos comuns (ex: ordenação) poderiam ser implementados sem depender do TIPO estrutura de dados utilizado, mas apenas de certas regras semânticas de acesso aos itens das estruturas.

Sendo estabelecidas certas convenções, é possível utilizar o mesmo algoritmo para ordenar qualquer estrutura de dados, seja um vetor ou uma lista ligada. A palavra chave aqui é abstração, sendo possível atingir estes resultados sem maiores perdas de desempenho.

Assim, Stepanov desenvolveu em 1985 uma biblioteca genérica de algoritmos e estruturas de dados em ADA, sendo posteriormente (1987) convidado a portar este trabalho para o C++. Apenas em 1993 nos laboratórios da HP, com a ajuda de Meng Lee, foi escrita uma grande biblioteca (Standard Template Library). Isto foi possível pois nesta época o C++ já suportava templates como padrão da linguagem.

Ao contrário de mecanismos de polimorfismo dinâmicos como funções virtuais, caracterizadas por uma queda de desempenho, os templates oferecem uma saída de polimorfismo estático criado em tempo de compilação. Por suas características (desempenho, flexibilidade), a STL foi incluída na especificação padrão do C++ em 14 julho de 1994.

Entre inúmeros algoritmos de ordernação/busca, a STL oferece estruturas de dados como tabelas hash, vetores, listas ordenadas e datasets. Todas as estruturas de dados oferecidas podem ser utilizadas nos algoritmos via objetos chamados interadores, pois permitem acesso aos membros das estruturas de dados através de uma interface comum.

A STL possui em sua raiz os seguintes conceitos:

a) Container: um objeto que armazena outros objetos como seus elementos, sendo os tipos manipulados definidos nos parâmetros de inicialização dos templates. Estes possuem funções para inserir, armazenar e eliminar elementos.

b) Interador: é um objeto que funciona semanticamente como um ponteiro, normalmente para um container.

c) Algoritmo: conjunto de operações aplicadas em objetos (ex: sort(), copy(), remove(), etc).

d) Adaptador: é um objeto especial que pode ser conectado a uma classe pré-existente (ou função) para mudar seu comportamento.

2.3. A DTL

Criada por CorwinJoy e Michael Grandman, esta biblioteca oferece acesso a bancos de dados abstraindo a manipulação de tabelas como containers utilizando a ODBC. Graças a estas características, é possível manipular tabelas com comandos como replace, insert e outros, como é definido para qualquer container da STL.

Ainda, é possível criar interadores para tabelas e fazer uso de todos os algoritmos disponíveis na STL. O melhor de tudo: a DTL é gratuíta e bem eficiente.

3. Implementação

Finalmente, após tanta teoria passamos à parte aplicada do artigo. Vamos lá!

3.1) Compilando a biblioteca

Na página citada (http://www.geocities.com/corwinjoy/dtl/) se encontra disponível para download os fontes da biblioteca. A primeira providência para utilizá-la é construir a biblioteca, para isso você deve estar de posse de um compilador C++ com bom suporte para templates.

Ao descompactar o arquivo (dtl.zip) será criada uma rede de diretórios tal qual:

  • #docs: onde se encontra a documentação da biblioteca
  • #example: diretório com arquivos fontes de um programa de example com várias funções de testes implementadas
  • #example_db: diretório com um banco de dados MS-Access para examplos
  • #lib: códigos fontes da biblioteca
  • #profile: pasta com programa de teste de desempenho
  • #testes: um programa com testes

Na pasta lib, se encontra 1 arquivo de projeto do Visual C++ para compilar a biblioteca e um makefile para os felizes usuários do Linux.

NOTAS:

a) é necessário instalar o servicepack 5 no VC++ para ele compilar templates de forma satisfatória.

b) caso for utilizar o gcc, será necessário a versão 3.0 (para verificar a versão digite no terminal o comando: ""gcc -v "").

c) No VC++ é preciso disponibilizar a opção de RTTI (Run Time TypeInformation) nos aplicativos para utilizar a biblioteca: vá em Project->Settings->C++ e na categoria C++ Language selecionando o check box EnableRun Time Information.

Após construir a biblioteca seria conveniente copiar os arquivos de cabeçalho para uma sub-pasta dentro do include padrão do seu compilador (no meu caso foi criado o diretório lib). O arquivo de biblioteca resultante (DTL.lib no Windows e DTL.a no Linux) deverá ser linkado nos aplicativos compilados. Assim, também seria conveniente copiar a biblioteca compilada para o diretório padrão (geralmente libs) do seu compilador.

Para utilizar o banco de dados de exemplo, crie um DSN para o banco de dados colocando as informações:

  • alias: example
  • user: example
  • password: example

Também são oferecidos comandos em SQL para a criação da mesma base dados em qualquer servidor SQL (ex: Oracle, Interbase, etc).

3.2.) O código

O programa abaixo ilustra a utilização da biblioteca com o banco de dados exemplo. Ele abre uma conexão com a ODBC, acessa uma tabela copiando seu conteúdo e insere um registro. Note como é possível passar uma referência a um container na função de criação de registro.

OK, chegamos ao final do artigo. É possível fazer umas observações finais:

a) A DTL é uma alternativa de boa eficiência e baixo custo para acesso a banco de dados.

b) Ela permite ao programador C++ experiente fazer uso dos algorítmos da STL em tabelas mantendo boa utilização do padrão da linguagem sem depender de bibliotecas privadas.

c) Tem seu código fonte livre e atualmente vem sendo atualizada com boa regulariedade.

d) Os aplicativos gerados não dependem de dlls ou bibliotecas extras,pois tudo é linkado no executável.Esta medida facilita a instalação dos programas.

e) É multiplataforma.

Dúvidas? Poste mensagens no forum de programação ou envie mensagens.

Programa

Listagem 2: Programa desenvolvido

/***************************************************
Autor: Adenilson Cavalcanti da Silva _ acsilva@esalq.usp.br
a.k.a. Savago

Propósito: programa Windows 32Bits de teste da biblioteca DTL. Lê o conteúdo da
tabela “DB_EXAMPLES” de um banco de dados registrado na ODBC como “examples”.
Após criar uma conexão com o banco de dados, copia o conteúdo da tabela para
um arquivo texto (pode ser fornecido o path+nome via linha de comando) e finalmente
insere um registro na tabela.
Esta tabela deve ter PELO MENOS os seguintes campos:
“INT_VALUE”: campo do tipo INTEGER ou SMALLINT
“STRING_VALUE”: campo do tipo VARCHAR
“DOUBLE_VALUE”: campo do tipo FLOAT
“EXAMPLE_LONG”: campo do tipo INTEGER
“EXAMPLE_DATE”: campo do tipo DATE

Notas: o programa pode ser facilmente portável para um aplicativo terminal,
bastando substituir WINMAIN por “intmain(intargc, char[] *argv)” e substituir
MESSAGEBOX por “printf” ou “cout”

Obs: a DTL é uma biblioteca de acesso a bancos de dados gratuíta, multiplataforma
(funciona no Unix/Linux bem como no Windows) e foi criada por CorwinJoy
e Michael Gradman (http://www.geocities.com/corwinjoy/dtl/).
Sua base de funcionamento é acesso a banco de dados via ODBC (Open Data Base
Connectivity) e seu design baseado na STL (StandartTemplateLibrarie)
permitindo a utilização dos algoritmos definidos nela (ex: ordenação,
pesquisa, etc) com containers acessando bancos de dados.

Data: 11-03-2002
/****************************************************************/
//Inclusão das bibliotecas básicas da linguagem
#include 
#include 
#include 
#include 

//Biblioteca de acesso a banco de dados, deve estar na pasta
//"lib” dentro do diretório “include” do compilador. Assume-se
//que a DTL já foi instalada no sistema.
#include 

using namespace dtl;
using namespace std;
//Estrutura para guardar data (padrão ODBC)
const TIMESTAMP_STRUCT then = {2002, 3, 11, 0, 0, 0, 0};

/***************** FUNCAO QUE CRIA UM REGISTRO **************
Recebe como argumento um container representando uma

visão para a tabela onde será inserido o registro. Poderia

também trabalhar com um interador de entrada.

*************************************************************/
booltest(DynamicDBView<>&aview)
{
   //Criamos uma string para guardar os nomes dos campos
std::stringcolnames;
   //Interador DTL que insere o registro
DynamicDBView<>::insert_iteratorwrite_it = aview;
//Tipo variante DTL temporário para guardar informações
variant_rowi;
i = write_it.GetDataObj();
//Vetor que aponta os nomes dos campos
vectornames = i.GetNames();

   //Copiamos os nomes do campos para a string de saida
for(vector::iterator i_name = names.begin();
i_name != names.end(); i_name++)
{
colnames += (*i_name);
colnames += “\t”;
}
   //Imprimimos osnomes dos campos da tabela
MessageBox(NULL, colnames.c_str(), “Libraries DTL”, MB_OK);

//Aqui o tipo variante recebe alguns valores (sendo utilizado
   //EXATAMENTE os nomes dos campos da tabela para referenciar
   //as informações) PS: os nomes dos campos poderiam ter sido
   //carregados em tempo de execução do programa!
std::string temp(“teste”);
i[“INT_VALUE”] = 10000;
i[“STRING_VALUE”] = temp;
i[“DOUBLE_VALUE”] = 12.3464;
i[“EXAMPLE_LONG”] = 999999;
i[“EXAMPLE_DATE”] = then;
//O interador insere o registro no container apontado
*write_it = i;
   ++write_it;

//Retornamos da função
returntrue;
};


int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,
intnCmdShow)
{

MessageBox(NULL, “Teste”, “Libraries DTL”, MB_OK);
//Nome do alias do banco de dados teste
charDSN_str[] = “UID=example;PWD=example;DSN=example;”;

try
{

      //Cria um arquivo (caso não tenha sido passado o parametro
      //com o nome do arquivo, cria o arquivo default)
char* path = lpCmdLine;
ofstreamfout;
if(strlen(path)> 2)
fout.open(path);
else
fout.open(“C:\\tabela.txt”);

MessageBox(NULL, “Tentando abrir conexão...”, “Cruze os dedos”, MB_OK);
//Criaconexão com a ODBC
DBConnection::GetDefaultConnection().Connect(DSN_str);

//Cria um container que representa a visão: “SELECT * FROM DB_EXAMPLE”
DynamicDBView<>view(“DB_EXAMPLE”, “*”);

      //Lê todas os registros da tabela e manda para o arquivo
copy(view.begin(), view.end(), ostream_iterator(fout, “\n”));

//Insere um novo registro no banco
if(!test(view))
throwint(10);
//Se correu tudo bem salva as modificações no banco de dados
DBConnection::GetDefaultConnection().CommitAll();

      //Liberamos a conexão
DBConnection::GetDefaultConnection().Release();
MessageBox(NULL, “Conexãofechada”, “Tudobeleza”, MB_OK);
}
catch (RootException&ex)
{
MessageBox(NULL, “Falha na conexão!”, “Erro”, MB_OK);
MessageBox(NULL, ex.what(), “Erros:”, MB_OK);
return 1;
}
catch(...)
{
MessageBox(NULL, “Falha na conexão!”, “Erro”, MB_OK);
return 1;
}

return 0;
}

Bibliografia consultada

Artigos da Internet

  • C. Joy, M. Gradman: Variant Objects for Generic Algorithm Design. Houston 2001.
  • C. Joy, M. Gradman: Introduction to the Database Template Library. Houston 2001.

Livros

  • V.V. Mizrahi: Treinamento em linguagem C++ modulo2. São Paulo: Makron Books 1994. 321p.

Revistas

  • D, Zigmond: STL Algorithms. Dr. Dobb"s Journal, August 1996. p. 32 a 36.
Adenilson Cavalcanti da Silva

Adenilson Cavalcanti da Silva - Adenilson (a.k.a. Savago) desenvolve sistemas há 10 anos, utilizando diversas linguagens de programação e sistemas operacionais. Tendo se especializado em C++, está sempre a procura de novos desafios com características multidisciplinares. Mestre pela USP, possui interesse especial por visão artificial, *nix, programação baixo nível.