2010-10-28 20 views
7

Me gustaría implementar un tipo de clase para mi pequeño lenguaje, pero lo que al principio pensé que no sería demasiado difícil me ha dejado perplejo. Tengo el analizador en su lugar y es el lado de generación de código de las cosas con las que estoy teniendo problemas. ¿Alguien puede arrojar alguna luz sobre la mejor/correcta forma de hacerlo? Específicamente, me gustaría hacer esto en LLVM, así que, aunque necesito saber las generalidades de esto, cualquier código LLVM específico con el que debería trabajar sería fantástico.¿Cómo se implementan las clases en los compiladores

Gracias T.


N.B. La experiencia que tengo con LLVM es básicamente lo que proviene de los tutoriales de Kaleidoscope y un poco más de jugar con él, pero estoy lejos de tener una comprensión completa de las API de LLVM.

+0

No creo que haya suficiente información aquí para responder la pregunta. ¿Qué es lo que quieres lograr con LLVM? (Por cierto, sé muy poco acerca de LLVM y probablemente no responda). – Qwertie

+0

@Qwertie. El objetivo final es implementar un compilador para mi propio pequeño lenguaje y en este momento estoy tratando de implementar clases de estilo C++. Entonces, cualquier información sobre cómo hacerlo sería agradable, pero creo que tal vez necesito comprender mejor la API LLVM primero, así que, como digo, cualquier idea sobre dónde ir/qué hacer para seguir aprendiendo después de completar los tutoriales de Kaleidoscope sería una ayuda también – tjm

+0

@tjm: Esta pregunta es bastante vaga. ¿Ya tiene una idea de cómo implementaría las clases en general y solo necesita ayuda para implementarlo con LLVM? ¿O quieres saber cómo se pueden implementar las clases en general? – sepp2k

Respuesta

6

Una visión general muy, muy incompleta:

Clase es una estructura (ya sabes C/C++ no?)

Métodos son funciones de otro modo ordinario, excepto que reciben un extra implícita argumento: el objeto mismo. Este argumento generalmente se llama 'esto' o 'yo' dentro de la función. Los símbolos de alcance de clase pueden ser accesibles (C++, JavaScript) o no (PHP, Python) por defecto dentro de los métodos.

La herencia es esencialmente pegar juntas las estructuras y posiblemente también fusionar tablas de símbolos, ya que normalmente los símbolos de la clase base son accesibles por defecto desde los métodos de una clase que ahora está analizando. Cuando encuentre un símbolo (campo o método) dentro de un método, debe hacer una búsqueda ascendente, comenzando desde la clase actual que sube en la jerarquía. O puede implementarlo para que lo busque solo en una tabla de símbolos que es el resultado de una fusión.

Los métodos virtuales se llaman indirectamente. En algunos idiomas, todos los métodos son virtuales por defecto. La implementación dependerá de si se trata de un lenguaje completamente dinámico, en cuyo caso siempre busca un nombre de función dentro de una clase en tiempo de ejecución y, por lo tanto, todos sus métodos se vuelven virtuales automáticamente; o en el caso de los compiladores de lenguajes estáticos usualmente construyen las llamadas tablas de métodos virtuales. No estoy seguro si necesita esto en absoluto, así que no entraré en detalles aquí.

Constructores son métodos especiales que se llaman ya sea en la construcción de un nuevo objeto (generalmente con 'nuevo') o de otra manera son llamados como parte de la cadena de llamada constructor desde dentro constructores descendientes. Muchas implementaciones diferentes son posibles aquí, una es que un constructor toma un argumento 'this' implícito, que puede ser NULL si aún no se ha creado un objeto, y lo devuelve también.

Destructiors son métodos normales que normalmente se llaman implícitamente cuando un objeto sale del alcance. De nuevo, debe tener en cuenta la posibilidad de una cadena de llamadas ascendente para destructores.

Las interfaces son complicadas a menos que, de nuevo, su idioma sea completamente dinámico.

+0

+1 Buen resumen, en general estoy de acuerdo. Excepto que las interfaces no son más difíciles que la herencia en general. – delnan

+0

@delnan gracias. En general, en las lenguas estáticas las interfaces son más complicadas que las llamadas virtuales ordinarias: para cada método de interfaz, se requiere un paso adicional para encontrar el VMT de esa interfaz en particular. Una persona que llama puede o no conocer el tipo exacto de un objeto a través del cual se realiza esta llamada.Pero como dije en los lenguajes dinámicos, básicamente no hace nada, excepto tal vez algunas verificaciones de compatibilidad en tiempo de ejecución. – mojuba

+0

Como dice la famosa cita, "Todos los problemas en la informática pueden resolverse mediante otro nivel de indirección";) Por supuesto, se necesita un paso adicional, pero no es realmente complicado. – delnan

4

Usted debe comprar Stan Lippmann, Dentro El C++ Object Model. Todo lo que necesitas está allí.

+0

¿Quiere decir Lippman, Inside the C++ Object Model? No puedo encontrar nada en un Lipschitz con un nombre similar. Si no, me disculpo por mi ignorancia. – tjm

+0

Eso es todo, bien hecho, enmendé mi respuesta. – EJP

+0

Gracias, veo una visita a la biblioteca en mi futuro cercano. – tjm

1

Probablemente hay varias estrategias para hacer realidad esto, aquí es una:

Un vtable (tabla virtual) es una estructura de compilación en tiempo-constante con los punteros de función. (Todos los valores se conocen en tiempo de compilación.)

(se puede llamar el puntero a una vtable una "interfaz", si lo desea.)

Una programación orientada a objetos de clase en un idioma sin ninguna posibilidad de herencia es una estructura que contiene un puntero const a su vtable como primera variable de miembro. Este puntero se usa para identificar exactamente el tipo de objeto, y con herencia múltiple el aspecto/vista (¿cómo se convierte?) En ese objeto.

Si desea tener una herencia múltiple, debe poder (static_) convertir el puntero a una clase derivada a su clase principal, corrigiendo la dirección de bytes sobre la marcha. Esto podría realizarse con una función virtual o (mejor) con un valor de desplazamiento firmado almacenado en el vtable.

Una conversión (dinámica_) del puntero a una clase padre al puntero a una clase derivada implica una búsqueda en una estructura de datos probablemente grande (matriz, hashtable, lo que sea) o también se realiza a través de una función virtual.

Cada llamada a una función desde el vtable necesita que el puntero del objeto sea convertido al tipo, que es apropiado para la función. Esto podría ser hecho por la persona que llama, leyendo el desplazamiento firmado (correspoinding a la función) desde el vtable, o por el destinatario, que entonces es solo un proxy de la función original.

En algunos idiomas (especialmente los idiomas funcionales) puede definir referencias a objetos (sin tipo) que crean una lista de interfaces/clases de tipos, válida para ese objeto. Tal referencia contiene un puntero al objeto base y una lista de punteros a los vtables relevantes.

Cuestiones relacionadas