Geração de Código em C para Simples



Documentos relacionados
EXERCÍCIOS SOBRE ORIENTAÇÃO A OBJETOS

Orientação a Objetos

Profº. Enrique Pimentel Leite de Oliveira

Polimorfismo. Prof. Leonardo Barreto Campos 1

Java. Marcio de Carvalho Victorino

Lista de Contas: Assinatura. Lista de Contas. Listas de Contas: Descrição. Listas de Contas: Descrição. Listas de Contas: Descrição

Programação Orientada a Objetos em Java

Guia de Fatores de Qualidade de OO e Java

Programação Orientada a Objetos em java. Polimorfismo

Java 2 Standard Edition Como criar classes e objetos

Orientação a Objetos com Java

Introdução a POO. Introdução a Linguagem C++ e POO

PROGRAMAÇÃO ESTRUTURADA. CC 2º Período

Aula 06 - Funções. O que é uma Função - Comando return - Protótipos de Funções - Tipos de Funções - Escopo de Variáveis - Passagem de parâmetros

Algoritmos e Programação Estruturada

Padrões de Projeto. Singleton

Reuso com Herança a e Composiçã

Capítulo 14. Herança a e Polimorfismo. Rui Rossi dos Santos Programação de Computadores em Java Editora NovaTerra

Implementando uma Classe e Criando Objetos a partir dela

Programação Orientada a Objetos em Java. Herança

7 RTTI e Interfaces. Desenvolvimento OO com Java. Vítor E. Silva Souza (vitorsouza@inf.ufes.br)

UML Aspectos de projetos em Diagramas de classes

Introdução à Programação

Estrutura da linguagem de programação C Prof. Tiago Eugenio de Melo tiago@comunidadesol.org

Prof. Jhonatan Fernando

Linguagem de Programação III

ATRIBUTOS PRIVADOS 6. ENCAPSULAMENTO MÉTODOS PRIVADOS MÉTODOS PRIVADOS

Técnicas de Programação II

DEFINIÇÃO DE MÉTODOS

Tipos de Dados, Tipos Abstratos de Dados Estruturas de Dados

Introdução à orientação a objetos. João Tito Almeida Vianna 25/05/2013

Especificação do 3º Trabalho

PHP INTRODUÇÃO CLASSES E OBJETOS

Exercício 1. Tabela 1: Cadastro de usuários, senhas e privilégios (exemplo). Login Senha Privilégio Armamento

Programação Orientada a Objetos - 3º semestre AULA 08 Prof. André Moraes

3. PARADIGMA ORIENTADO A OBJETOS

struct LISTA item quant

Tabela de Símbolos. Análise Semântica A Tabela de Símbolos. Principais Operações. Estrutura da Tabela de Símbolos. Declarações 11/6/2008

17 - Funções e Procedimentos em C Programação Modular

Programação Orientada a Objetos (DPADF 0063)

Introdução à Programação. Interface, Polimorfismo e Dynamic Binding

Herança. Alberto Costa Neto DComp - UFS

BUSCA EM LISTAS LISTAS SEQÜENCIAIS, LISTAS SIMPLESMENTE E DUPLAMENTE ENCADEADAS E LISTAS CIRCULARES

O Processo de Programação

Técnicas de Programação Avançada TCC Profs.: Anselmo Montenegro Conteúdo: Introdução à Orientação a Objetos

INF 1005 Programação I

Módulo 06 Desenho de Classes

Diagrama de Classes. Um diagrama de classes descreve a visão estática do sistema em termos de classes e relacionamentos entre as classes.

INF 1005 Programação I

Algoritmos de Busca em Tabelas

Linguagem de Programação III

UNIVERSIDADE FEDERAL DO RIO GRANDE DO SUL INSTITUTO DE INFORMÁTICA INFORMÁTICA APLICADA

Programação Orientada a Objetos Classes Abstratas Técnico em Informática. Prof. Marcos André Pisching, M.Sc.

Estruturas de Dados. Prof. Gustavo Willam Pereira Créditos: Profa. Juliana Pinheiro Campos

Programação Concorrente em java - Exercícios Práticos Abril 2004

Análise e Projeto de Sistemas

Capítulo 2: Introdução à Linguagem C

AULA 4 VISÃO BÁSICA DE CLASSES EM PHP

Comandos de repetição For (inicialização; condição de execução; incremento/decremento) { //Código }

Programação Estruturada. Programação Estruturada. Idéias Básicas da Programação Estruturada

PROGRAMAÇÃO ORIENTADA A OBJETOS -TRATAMENTO DE EXCEÇÕES. Prof. Angelo Augusto Frozza, M.Sc. frozza@ifc-camboriu.edu.br

Busca. Pesquisa sequencial

Curso de PHP. FATEC - Jundiaí. A programação orientada a objetos (object-oriented oriented programming

Programação aplicada de computadores Andréa Maria Pedrosa Valli

Relacionamentos entre objetos. Relacionamentos entre objetos. Relacionamentos entre objetos. Relacionamentos entre objetos

ARRAYS. Um array é um OBJETO que referencia (aponta) mais de um objeto ou armazena mais de um dado primitivo.

Slide 1 Deitel/Deitel, 8e. Java Como programar Copyright 2010 Pearson Education

Projeto de Software Orientado a Objeto

2. OPERADORES ALGORITMOS, FLUXOGRAMAS E PROGRAMAS FUNÇÕES... 10

Linguagem e Técnicas de Programação I Programação estruturada e fundamentos da linguagem C

Entendendo como funciona o NAT

Prof. Yandre Maldonado - 1 PONTEIROS. Prof. Yandre Maldonado e Gomes da Costa

Engenharia de Software

2 Orientação a objetos na prática

2ª LISTA DE EXERCÍCIOS CLASSES E JAVA Disciplina: PC-II. public double getgeracaoatual() {return geracaoatual;}

Introdução a Java. Hélder Nunes

O que é um programa? Programa é uma lista de instruções que descrevem uma tarefa a ser realizada pelo computador.

Construção de novas Classes em Java. Classes Atributos Métodos Herança...

Coleções. Conceitos e Utilização Básica. c Professores de ALPRO I 05/2012. Faculdade de Informática PUCRS

Universidade da Beira Interior Cursos: Matemática /Informática e Ensino da Informática

INF 1007 Programação II

3 Classes e instanciação de objectos (em Java)

Introdução a Programação. Ponteiros e Strings, Alocação Dinâmica

LP II Estrutura de Dados. Introdução e Linguagem C. Prof. José Honorato F. Nunes honorato.nunes@ifbaiano.bonfim.edu.br

Encapsulamento de Dados

Protocolo TCP/IP. Neste caso cada computador da rede precisa de, pelo menos, dois parâmetros configurados:

ARQUITETURA DE COMPUTADORES. Rogério Spindula Rosa

Um objeto é uma instância de uma 'classe'. Você define uma classe utilizando o trecho de código abaixo;

NetBeans. Conhecendo um pouco da IDE

Sobre o Professor Dr. Sylvio Barbon Junior

Tópicos da Aula. Classes e Objetos. Classe Pública em Java. Classes em Java. Assinatura de Métodos. Corpo de Classes e Métodos. Conceitos de classe

Para desenvolver a atividade a atividade desta aula utilizaremos o ambiente de desenvolvimento integrado NetBeans.

Programação Orientada a Objetos Prof. Rone Ilídio UFSJ/CAP

Capítulo 4. Packages e interfaces

INF 1620 P1-10/04/02 Questão 1 Nome:

PROGRAMAÇÃO AVANÇADA -CONCEITOS DE ORIENTAÇÃO A OBJETOS. Prof. Angelo Augusto Frozza, M.Sc. frozza@ifc-camboriu.edu.br

Esta apresentação ensinará os conceitos de Orientação a Objetos com C++, do mais básico para o mais avançado. É suposto que o aluno já tenha

Transcrição:

Geração de Código em C para Simples José de Oliveira Guimarães Departamento de Computação UFSCar - São Carlos, SP Brasil e-mail: jose@dc.ufscar.br December 1, 2004 Este artigo descreve a tradução dos programas de Simples para C. Os nomes de alguns identificadores do programa em Simples, como classes e métodos, terão seus nomes alterados na tradução. Os nomes de variáveis locais são mantidos. Contudo, se o tipo da variável pa no programa em Simples for A, o tipo em C será o de um ponteiro para class_a, definida abaixo. Todas as variáveis cujos tipos são classes serão traduzidos para ponteiros em C. Isto será assumido no texto que se segue. Um objeto de uma classe A possui todas as variáveis declaradas em A mais um ponteiro para um vetor de métodos. Como exemplo, a Figura 1 mostra um objeto da classe A da Figura 2. 1 Todos os objetos possuem um ponteiro, que chamaremos de vt, que aponta para a tabela de métodos públicos da classe. Cada classe possui um vetor de ponteiros onde cada entrada aponta para um dos métodos públicos da classe. Todos os objetos de uma classe apontam, via ponteiro vt, para a mesma tabela de métodos (TM) da classe. Assim, se pa referir-se a um objeto da classe A, pa->vt[0] (já traduzindo pa para um ponteiro em C) apontará para um método público de A (neste caso, A::get()). O compilador, ao compilar a classe A, transforma-a em uma estrutura conto vt na primeira posição e as variáveis de instância de A em seguida: struct St_A Func *vt; int A_i; class_a; 1 Usaremos C::m para designar método m da classe C. Figure 1: Representação de um objeto da classe A 1

class A private: var i : integer; public: proc get() : integer return self.i; proc put( p_i : integer ) self.i = p_i; Figure 2: Uma classe em Simples O tipo Func é definido como void (*Func)(); Isto é, um ponteiro para uma função. Cada método de A, seja ele público ou privado, é convertido em uma função que toma como parâmetro um ponteiro para class A e cujo nome é formado pela concatenação do nome da classe e do método: int A_get( class_a *self ) return self->a_i; void A_put( class_a *self, int p_i ) self->a_i = p_i; O nome do primeiro parâmetro é sempre self, e é através dele que são manipuladas as variáveis de instância da classe, que estão declaradas em class A. A codificação dos métodos privados é exatamente igual à dos métodos públicos. Agora, a tabela de métodos públicos da classe A é declarada e inicializada com as funções acima: Func VTclass_A[] = A_get, A_put ; De fato, a declaração acima possui erros de tipo, pois o tipo de A get (ou A_put) é diferente do tipo de Func, mas não nos preocuparemos com isto por enquanto. Func é o tipo de cada um dos elementos do vetor VTclass_A. Como dissemos anteriormente, cada objeto de A aponta para um vetor de métodos públicos de A, que é VTclass A. Assim, quando um objeto de A é criado, deve-se fazer o seu campo vt apontar para VTclass_A. O compilador transforma pa = A.new(); /* Simples */ em pa = new_a(); /* C */ onde new A é definida como 2

class_a *new_a() class_a *t; if ( (t = malloc(sizeof(class_a)))!= NULL ) t->vt = VTclass_A; return t; Observe que a ordem dos métodos em VTclass_A é a mesma da declaração da classe A. A posição 0 é associada a get e 1, a put. Uma chamada de um método público j = pa.get(); é transformada em uma chamada de função através de pa->vt: j = (pa->vt[0])(pa); O índice 0 foi usado porque 0 é o índice de get em VTclass_A. O primeiro parâmetro de uma chamada de métodos é sempre o objeto que recebe a mensagem. Neste caso, pa. De fato, a instrução acima possui um erro de tipos: pa->vt[0] não admite nenhum parâmetro e estamos lhe passando um, pa. Isto é corrigido colocando-se uma conversão de tipos: j = ( (int (*)(class_a *) ) pa->vt[0] )(pa); O tipo int (*)(class_a *) representa um ponteiro para função que toma um class_a * como parâmetro e retorna um int. Como mais um exemplo, pa.put(12) é transformado em (pa->vt[1])(pa, 12) ou melhor, em ( (void (*)(class_a *, int )) pa->vt[1] )(pa, 12) Com as informações acima, já estamos em condições de traduzir um programa de Simples para C. O programa Simples da Figura 3 é traduzido para o programa C mostrado nas Figuras 4 e 5. Neste programa estão colocadas as conversões de tipo (casts) necessárias para que o programa compile. Como definido pela linguagem, a execução do programa começa com a criação de um objeto da classe Program, que é seguida do envio da mensagem run para este objeto. Considere agora a classe B da Figura 6. Esta classe possui vários aspectos ainda não examinados: 1. o método print envia a mensagem get para self. O método get é público. 2. o método put de B chama o método put de A através de super.put(p_i); 2 3. o acréscimo de métodos nas subclasses (print, inc e getlastinc); 4. a redefinição de métodos nas subclasses (put); 5. adição de variáveis de instância nas subclasses (lastinc); 6. definição de métodos privados (add); 7. chamada a métodos privados usando self: (self.add(1)). Veremos abaixo como gerar código para cada uma destas situações. 1. self.get() é traduzido para (self->vt[0])(self) 2 Observe que esta herança está errada. Métodos de subclasses não podem restringir valores de parâmetros. 3

class A private: var i : integer; public: proc get() : integer return self.i; proc put( p_i : integer ) self.i = p_i; class Program public: proc run() var a : A; k : integer; a = A.new(); a.put(5); k = a.get(); write(k); Figure 3: Um programa em Simples 4

#include <alloc.h> #include <stdlib.h> #include <stdio.h> void (*Func)(); struct St_A Func *vt; int A_i; class_a; class_a *new_a(); int A_get( class_a *self ) return self->a_i; void A_put( class_a *self, int p_i ) self->a_i = p_i; Func VTclass_A[] = ( void (*)() ) A_get, ( void (*)() ) A_put ; class_a *new_a() class_a *t; if ( (t = malloc(sizeof(class_a)))!= NULL ) t->vt = VTclass_A; return t; Figure 4: Tradução do programa da Figura 3 para C 5

struct St_Program Func *vt; class_program; class_program *new_program(); void Program_run( class_program *self ) class_a *a; int k; a = new_a(); ( (void (*)(class_a *, int)) a->vt[1] )(a, 5); k = ( (int (*)(class_a *)) a->vt[0] )(a); printf("%d ", k ); Func VTclass_Program[] = ( void (*)() ) Program_run ; class_program *new_program() class_program *t; if ( (t = malloc(sizeof(class_program)))!= NULL ) t->vt = VTclass_Program; return t; void main() class_program *program; /* crie objeto da classe Program e envie a mensagem run para ele */ program = new_program(); ( ( void (*)(class_program *) ) program->vt[0] )(program); Figure 5: Continuação da tradução do programa da Figura 3 para C 6

class B subclassof A private: var lastinc : integer; proc add( n : integer ) self.lastinc = n super.put( super.get() + n ); public: proc print() write( self.get() ); proc put( p_i : integer ) if p_i > 0 then super.put(p_i); if proc inc() self.add(1); proc getlastinc() : integer return self.lastinc; Figure 6: Herança de A por B com acréscimo e redefinição de métodos 7

Figure 7: Objeto e tabela de métodos de B ou melhor, ( (int (*)(class_a *)) self->vt[0]) ( (class_a *) self ) Se o método é público, a ligação mensagem/método é dinâmica é necessário utilizar a tabela de métodos mesmo o receptor da mensagem so self. 2. A chamada super.put(p_i) especifica claramente qual método chamar: o método put de A. 3 Portanto, esta instrução resulta em uma ligação estática, que é: A_put( self, p_i ) ou A_put( (class_a *) self, p_i ) com conversão de tipos. Como o destino do envio de mensagem put não é especificado (com em a.put(5)), assume-se que ele seja self. Observe que em A_put( (class_a *) self, p_i ) é necessário converter um ponteiro para class_b, que é self, em um ponteiro para class_a. Isto não causa problemas porque class_b é um superconjunto de class_a, como foi comentado acima. Note que o protótipo de A_put é A_put( class_a *, int ) 3. O acréscimo de métodos em subclasses faz com que a tabela de métodos aumente proporcionalmente. Assim, a tabela de métodos para B possui três entradas a mais do que a de A, para print, inc e getlastinc. 4. A redefinição de put em B faz com que a tabela de métodos de B refira-se a B::put e não a A::put. A classe B herda o método get de A, redefine o put herdado e adiciona o método print. Assim, a tabela de métodos de B aponta para A::get, B::put e B::print, como mostrado na Figura 7. 5. A declaração de class_b é struct St_B Func *vt; int A_i; int B_lastInc; class_b; 3 O compilador faz uma busca por método put começando na superclasse de B, A, onde é encontrado. 8

As variáveis da superclasse aparecem antes das variáveis da subclasse. 6. A geração de código para um método privado é exatamente igual à de um método público. Porém, métodos privados não são colocados na tabela de métodos (como VTclass_B). 7. A chamada self.add(1) especifica qual método chamar: add da classe corrente. So add um método privado, sabemos exatamente de qual método estamos falando. Então a chamada em C é estática: B_add(self, 1) Não há necessidade de converter self pois o seu tipo é class_b e add é declarado como void B_add( class_b *self, int n )... Chamadas a métodos privados nunca precisarão de conversão de tipo para self. Recordando, envios de mensagem para self resultam em ligação dinâmica (usando vt) se o método for público ou em ligação estática se o método for privado. Na tabela de métodos de B, a numeração de métodos de A é preservada. Assim, a posição 0 da TM de B aponta para o método get porque esta mesma posição da TM de A aponta para get. Isto acontece somente porque B herda A. As numerações em classes não relacionadas por herança não são relacionadas entre si. A preservação da numeração em subclasses pode ser melhor compreida se considerarmos um método polimórfico f: class X public: proc f( a : A ) a.put(5); Este método é transformado em void X_f( class_x *self, class_a *a ) ( (void (*)( class_a *, int )) a->vt[1] )(a, 5); É fácil ver que a chamada t = A.new(); x.f(t); causa a execução de A::put, já que t->vt e a->vt 4 apontam para VTclass_A e a posição 1 de VTclass_A (que é a->vt[1]) aponta para A::put. Como B é subclasse de A, objetos de B podem ser passados a f: t = B.new() x.f(t); Agora, t->vt e a->vt apontam para VTclass_B e a posição 1 de VTclass_B aponta para B::put. Então, a execução de f causará a execução de B::put, que é apontado por a->vt[1]. f chama put de A ou B conforme o parâmetro seja objeto de A ou B. Isto só acontece porque B::put foi colocado em uma posição em VTclass_B igual a posição de A::put em VTclass_A. Esclarecemos melhor este ponto através de um exemplo. Se VTclass_B fosse declarado como Func VTclass_B [] = A_get, 4 a é o parâmetro formal de f. 9

B_print, B_put, B_inc, B_getLastInc ; a execução da função f chamaria B_print. A transformação da classe B para linguagem C é mostrada nas Figuras 8 e 10. Desempenho O código abaixo compara o tempo de execução de uma chamada de função (com corpo vazio e um ponteiro como parâmetro) com o tempo de execução de chamadas indiretas de função. 5 class C public: virtual void f() ; void (*Func)(); struct Func *vt; class_a; void f( class_a * ) Func VTclass_A[] = (void (*)()) f ; int i, j; class_a a, *pa = &a; C c, *pc = &c; void (*pf)(class_a *) = f; void main() a.vt = VTclass_A; for (i = 0; i < 1000; i++ ) for (j = 0; j < 1000; j++ ) // ; /* 0.21 */ // f(pa); /* 0.37-0.21 = 0.16 */ // pf(pa); /* 0.41-0.21 = 0.20 */ // ( ( void (*)(class_a *) ) pa->vt[0]) (pa); /* 0.46-0.21 = 0.25 */ // pc->f(); /* 0.59-0.21 = 0.38 */ 5 Estes dados foram obtidos em uma estação SPARC com Unix. 10

struct St_B Func *vt; int A_i; int B_lastInc; class_b; class_b *new_b(); void B_add( class_b *self, int n ) self->b_lastinc = n; A_put( (class_a *) self, A_get( (class_a *) self ) + n ); void B_print ( class_b *self ) printf("%d ", ((int (*)(class_a *)) self->vt[0])( (class_a *) self)); void B_put( class_b *self, int p_i ) if ( p_i > 0 ) A_put((class_A *) self, p_i); void B_inc( class_a *self ) B_add(self, 1); int B_getLastInc( class_b *self ) return self->b_lastinc; Figure 8: Tradução da classe B para a linguagem C parte I 11

Func VTclass_B[] = (void (*) () ) A_get, (void (*) () ) B_put, (void (*) () ) B_print, (void (*) () ) B_inc, (void (*) () ) B_getLastInc ; class_b *new _B() class_b *t; if ((t = malloc (sizeof(class_b)))!= NULL) t->vt = VTclass_B; return t; Figure 9: Tradução da classe B para a linguagem C parte II f(pa) 1 (*pf)(pa) 1.25 pa->vt[0](pa) 1.56 pb->f() 2.4 Figure 10: Tabela comparativa dos tempos de chamada de função A tabela de tempos é sumarizada na Figura 10. Observe que o envio de mensagem pb->f() é implementado pela própria linguagem C++ e utiliza um mecanismo mais lento do que a implementação descrita nesta seção. Cada objeto possui um campo vt que aponta para uma tabela (vetor) de ponteiros onde cada ponteiro aponta para um dos métodos da classe do objeto. Todos os objetos de uma mesma classe apontam para a mesma tabela de métodos. Um envio de mensagem a.m(), onde o tipo de a é A, é transformado em a->vt[0](a). O método m é invocado através de um dos elementos da tabela (neste caso, elemento da posição 0). O objeto é sempre o primeiro parâmetro. As classes são transformadas em estruturas conto as variáveis de instância e mais um primeiro campo vt que é um ponteiro para um vetor de funções (métodos). Os métodos são transformados em funções adicionando-se como primeiro parâmetro um ponteiro chamado self para objetos da classe. Chamadas a métodos da superclasse (como em super.put) são transformadas em chamadas estáticas. 12