quinta-feira, 19 de março de 2009

Sobre APIs

Umas das coisas mais importantes em programação, sem a qual ninguém consegue desevolver, é API.

API – Interface de Programação de Aplicativos – é o conjunto de recursos de que o programador dispõe para desenvolver suas aplicações.

Este artigo faz uma comparação – na verdade, mais uma demonstração – de cinco APIs muito usadas: IEEE 1003, STL, OpenStep, GLib e Java.

Para a demonstração será usado um trecho de código para ler a primeira linha de um arquivo.

Por que não a API do Windows?


Antes de começar, é preciso esclarecer por que não a API do Windows.

O objetivo de uma API é oferecer um ambiente homogêneo de programação, para que o programador não seja obrigado a reaprender a mesma coisa diversas vezes. Para facilitar isso, muitas APIs ainda podem ser usadas em diversas linguagens diferentes, outras em apenas uma.

No entanto uma API que funciona apenas em um único sistema operacional – ou ainda no grupo de sistemas operacionais de uma única empresa – não está fazendo seu trabalho, por mais usado que esse(s) sistema(s) possa ser.

Assim a API do Windows não faz mais do que se espera da interface de um sistema operacional.

É claro que é necessário que o Windows possua uma API, mesmo que seja exclusivamente para conexão de outras APIs mais universais.

IEEE 1003 (POSIX)


O padrão POSIX, definido pela IEEE, oferece uma interface completa para sistemas compatíveis com UNIX. Alguns exemplos são IBM AIX, Solaris, GNU (e daí também Linux), BSD, Mac OS X, MS Xenix e muitos outros.

O trecho de código em C para ler a primeira linha de um arquivo:
FILE *fd = fopen(filename, "r");

if (fd) {
while (!eof(fd)) {
char buf;
fread(&buf, sizeof(char), 1, fd);
printf("%c", buf);
if (buf == '\n')
break;
}

fclose(fd);
}


Observação1: é preciso incluir o cabeçalho stdio.h ou, em C++, cstdio:
#include <stdio.h>


Observação2: a variável filename é do tipo const char *.

Observação3: foi preciso procurar ocorrência do carácter \n pois POSIX não ofereça uma função de alto nível para ler uma linha.

Observação4: o padrão POSIX é completamente procedimental – as demais APIs citadas aqui são orientadas a objetos –, sendo um pouco desajeitado para algumas operações.

STL


A linguagem de programação C++ possui uma biblioteca padrão própria bastante completa chamada STL – Standard Library.

Para o exemplo, é preciso incluir o cabeçalho fstream para a manipulação de arquivo e o cabeçalho iostream para a exibição dos dados na tela:
std::ifstream fd(filename);

if (fd.is_open()) {
char buf[1024];
fd.getline(buf, 1023);

std::cout << buf << std::endl;
fd.close();
}


Observação1: como citado acima, é preciso incluir pelo menos o cabeçalho fstream ao manipular arquivos:
#include <fstream>


Observação2: a variável filename precisa ser do tipo const char *std::ifstream não suporta std::string.

OpenStep


A empresa NeXT de Steve Jobs, ora adquirida pela Apple, abriu a API de seu sistema operacional em 1992 num acordo com a SUN Microsystems, sob o nome de OpenStep, o que permitiu, além da criação de aplicações de terceiros para NeXTSTEP, também o desenvolvimento de outros sistemas operacionais compatíveis com sua API e APIs de programação para sistemas já existentes, tornando-os compatíveis com OpenStep.

Exemplos de sistemas compatíveis com essa API são Mac OS X e GNUstep (portável para Windows e qualquer plataforma compatível com POSIX).

A API OpenStep funciona exclusivamente em Objective-C, apesar de haver alguns portes para outras linguagens, como C++, Smalltalk e Guile Scheme.

Vamos ao exemplo em Objective-C:
id fd = [NSFileHandle fileHandleForReadingAtPath: filename];

if (fd != nil) {
char buf = 0;
[fd seekToEndOfFile];
unsigned long long eof = [fd offsetFile];
[fd seekToFileOffset: 0];

while (([fd offsetInFile] < eof) && (buf != '\n')) {
[[fd readDataOfLength: 1] getBytes: &buf length: 1];
printf("%c", buf);
}

[fd closeFile];
}


Observação1: é preciso importar o cabeçalho Foundation.h:
#import <Foundation/Foundation.h>


Observação2: a variável filename precisa ser do tipo NSString.

Observação3: eu não conheço um método para ler uma linha de um arquivo em OpenStep, portanto usei um esquema similar ao usado acima em POSIX.

GLib


A GLib começou como parte do toolkit gráfico Gtk+ na versão 2.0, tendo evoluído para se tornar uma API multiplataforma orientada a objetos completa e independente da interface gráfica.

Você pode usar GLib em qualquer sistema POSIX e em Windows.

A maior curiosidade da GLib é que ela deixa explícita o equívoco – ou a falácia – de quem crê que não seja possível programar orientado a objetos em uma linguagem não orientada a objetos: é escrita em C e completamente orientada a objetos.

Aliás a base da GLib é o GObject que, curiosamente, não é compatível com a estrutura de orientação a objetos de C++ e outras linguagens clássicas, apesar de haver portes para C++ e outras.

O exemplo a seguir é um trecho de código em Vala, linguagem criada expecificamente para a GLib:
var file = File.new_for_path(filename);

if (file.query_exists(null)) {
try {
var fd = new DataInputStream(file.read(null));
stdout.printf("%s\n", fd.read_line(null, null));
fd.close(null);
} catch (Error e) {
// Ignora exceções
}
}


[update 2009-03-19]Faltou o close.[/update]

Observação1: é preciso «usar» GLib:
using GLib;


Observação2: a variável filename é do tipo string – equivalente em C a GString e gchar *.

Observação3: é preciso usar o pacote (--pkg) gio-2.0, que importa GIO, recursos de I/O da GLib.

Java


Em sua linguagem de programação, a SUN Microsystems optou por criar uma API própria (bem complicada e verborrágica em relação às demais, diga-se).
BufferedReader fd = null;
try {
fd = new BufferedReader(new FileReader(filename));
System.out.println(fd.readLine());

} catch (Exception e) {
// Ignora exceções

} finally {
try {
if (fd != null)
fd.close();
} catch (IOException e) {
// Ignora exceções
}
}


Observação1: as classes usadas estão no pacote java.io:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;


Observação2: a variável filename é do tipo String.


**

Fica aqui a dica de três APIs interessantes.

[]'s
Cacilhas, La Batalema

9 comentários:

LKRaider disse...

for line in open(filename):
    print line,

:P

La Batalema disse...

LKRaider,

Isso cheira a Python. =)

Mas esse trecho de código tem um problema: não fecha o arquivo, além de exibir todas as linhas, em vez de somente a primeira.

with open(filename) as fd:
    sys.stdout.write(fd.readline())

Agora sim! ;-)

[]'s
Cacilhas, La Batalema

Tiago Albineli Motta disse...

ruby:

> File.open(a).readline

La Batalema disse...

Legal…

Mas e para fechar? File de Ruby já fecha automaticamente (como o with de Python) ou você larga desleixadamente o arquivo aberto?

Realmente eu poderia ter mostrado como fazer em outras linguagens, mas a ideia do artigo não era mostrar como cada linguagem lida com abertura de arquivo – apesar de ter mostrado algumas APIs exclusivas de uma ou outra linguagem.

A ideia foi demostrar APIs multiplataforma, de preferências as que possam ser usadas em mais de uma linguagem. Só mostrei STL (de C++) e a API de Java porque não há como escapar dessas duas linguagens.

Porém é interessante também mostrar as APIs específicas de linguagens de scripting e os exemplos de Python e Ruby ficaram legais.

[]'s
Cacilhs, La Batalema

Tiago Albineli Motta disse...

Claro que não deixaria aberto o arquivo. Só foi uma contribuição de adição de mais uma api. Sem qualquer pretensão de comparação se uma API é melhor ou pior que a outra. Em ruby fechando o arquivo:

arquivo = File.open(a)
puts arquivo.readline
arquivo.close

Isso também ignorando possiveis exceções.

j disse...

Salve, Cacilhas!

Realmente é muito importante conhecer as APIs que se utiliza, pois além de facilitar muito o trabalho, evita que o programador reinvente a roda e ajuda a criar códigos mais padronizados. Isso sem tocar na questão de desempenho e segurança, já que os componentes (classes, funções etc.) da API normalmente são testados à exaustão e documentados.
Quando eu comecei a programar, usando Pascal, eu achava lindo ficar criando os "tijolos". Muitas vezes eu fazia uma função que logo depois eu descobria já estar implementada numa bibliteca X. Hoje em dia eu ainda gosto de criar ferramentas para uso futuro (criar os tijolos para construir as casas depois), mas sempre tento ficar ligado sobre algo que já exista, para que eu possa utilizar futuramente.

Muito bom o seu artigo!
Parabéns!
Abraço!

La Batalema disse...

Tiago,

Eu senti que estava faltando alguma coisa… =)

A comparação entre APIs de linguagens diferentes é sempre meio complicada. No final das contas acaba sendo «aqui é assim, ali é assado».

Mas ainda assim é interesse.


J,

Também já reinventei muito a roda, e era muito chato quando eu descobria que já existia o que eu havia feito.

Até que finalmente virei devoto de São Google, depois de muito apanhar – de 1987 até 1996/1998 não tinha Google… eu tinha de me virar mesmo.

Mas às vezes também descobrimos que determinada funcionalidade de uma biblioteca não satisfaz nossa necessidade, o que nos leva a reinveintar a roda quando ela é quadrada.

Valeu pelo elogio e pelo apoio. Eu realmente gosto muito de programar, gosto pra caramba de APIs e amo apaixonadamente escrever.

[]'s
Cacilhas, La Batalema

Danilo Oliveira disse...

Sobre a api "verborrágica" do Java:

nada que um Ctrl + Space não resolva.

La Batalema disse...

Salve Danilo!

Seu comentário merece uma réplica. =)

Quando você fala em C-Space, entrou em outra discussão, que não API de linguagem…

Aí já estamos falando de IDEs.

Outras linguagens também podem ter suas IDEs, com suas próprias vantagens. Autocompletar é um recurso de algumas IDEs independente de linguagem.

Portanto citar C-Space como recurso de Java é uma descontextualização.

A API de Java não deixa de ser verborrágica por causa do autocompletar e o autocompletar também pode ser usado para outras linguagens.

;-)

[]'s
Cacilhas, La Batalema