Mapeo del IDL en lenguajes de programación CORBA ➨ Lenguajes: – – » – C Java C++ Ada-95, SmallTalk, COBOL, Lisp, Phyton, IDLscript Angel García Baños Escuela de Ingeniería de Sistemas y Computación Universidad del Valle 27 de Noviembre de 2002 CORBA 3 AGB Mapeo del IDL en lenguaje C++ Mapeo del IDL en lenguaje C++ ■ CLIENTE STUB en C++ Especificación de la interfase en IDL ■ Compiladores ■ Object Request Broker (ORB) ■ SKELETO N en C++ ■ IMPLEMENTACIÓN DEL OBJETO 3 AGB 2 Mapeo del IDL en lenguaje C++ (Tipos básicos) IDL C++ short long long long unsigned short unsigned long unsigned long long float double long double char wchar string wstring boolean octet any CORBA::Short CORBA::Long CORBA::LongLong CORBA::UShort CORBA::ULong CORBA::ULongLong CORBA::Float CORBA::Double CORBA::LongDouble CORBA::Char [(un)signed] char CORBA::WChar int o wchar_t char * // Por mantener compatibilidad con C CORBA::WChar * CORBA::Boolean bool o [(un)signed] char CORBA::Octet [(un)signed] char CORBA::Any AGB 5 Usualmente se mapea a Es muy eficiente, aunque a la vez es largo y complejo de entender. Se prefirió así, ya que siempre se puede diseñar una capa superior (wizards, etc.) que facilite las cosas. Es consistente. Todo lo que se aprenda para strings es válido para otros tipos de longitud variable. Tiene chequeo de tipos. No se requieren moldes (casts) y la mayoría de los errores de tipos se detectan al compilar. Es fácil de memorizar, ya que aunque cada clase tiene muchas funciones miembro, la mayoría no las usa el programador, sino que son para conversiones automáticas de tipos. Proporciona facilidades para compiladores que no sean estrictamente ANSI C++. AGB 4 Mapeo del IDL en lenguaje C++ (Identificadores) ■ Los identificadores se preservan al pasar de IDL a C++. Solo en caso de conflicto con palabras reservadas, se les añade el prefijo _cxx_. enum colores {if, while, class}; // IDL enum colores {_cxx_if, _cxx_while, _cxx_class}; // C++ ■ No se deben usar identificadores con doble subrayado (ej: hola__mundo), ya que pueden colisionar con palabras reservadas. AGB 6 Mapeo del IDL en lenguaje C++ (Módulos) ■ Los module de IDL se mapean en namespace de C++ (si el compilador no soporta namespace, entonces se mapean en class). Mapeo del IDL en lenguaje C++ (Interfases) ■ module A // IDL { module B { ... }; }; – class Nombre_ptr puntero simple al objeto. puntero al objeto que hace el manejo – class Nombre_var de memoria de forma automática. Se convierte en: namespace A // C++ { namespace B { ... }; }; ■ ■ 7 AGB Mapeo del IDL en lenguaje C++ (Interfases) ■ Está prohibido manejar directamente la interfase a un objeto, excepto a través de esas dos clases. ■ ■ ■ Los _ptr son punteros normales, que apuntan a objetos remotos. Al acabar de usarlos hay que liberarlos con CORBA::release() Los _var son punteros inteligentes: Cuando salen fuera de ámbito, su destructor libera la memoria referenciada. En ambos casos (_ptr y _var) se debe usar el operador flecha -> para acceder a los miembros de la interfase. ■ 9 Mapeo del IDL en lenguaje C++ (Tipos de longitud variable) La forma como internamente se hace el manejo de memoria con parámetros de longitud variable: ■ SERVIDOR char *copyright() { obj->invoke(“copyright”); } ■ while(!done) { op = get_request(); char *s = invoke(op); Se pueden usar los dos mapeos, con la ventaja de que el _var se encarga de liberar la memoria usada cuando se sale la variable fuera de su ámbito. Si la estructura, la unión o el array son de tamaño fijo, también se genera la clase _var correspondiente, pero entonces no libera memoria. 10 AGB Los strings pueden ser limitados o ilimitados, pero es responsabilidad del programador no salirse de los límites. Está prohibido usar new y delete para manejar la memoria de strings dinámicos. En su lugar, usar: { // ... static static static static static static } } send(s); CORBA::string_free(s); CORBA::string_free(s); AGB clase wrapper _var CORBA::String_var CORBA::Any_var class foo_var class foo_var class foo_var class foo_var class foo_var namespace CORBA char *copyright() { char *s = string_dup(...); return s; unsigned len = recv_len(); char *s = string_alloc(len); recv(s); return s; C++ char * CORBA::Any class foo_ptr struct foo class foo class foo typedef X foo[10] Mapeo del IDL en lenguaje C++ (strings) CLIENTE Objet_var obj = ...; char *s = obj->copyright(); 8 Se crean clases _var para los tipos de longitud variable, para facilitar el manejo de memoria: IDL string any interface foo struct foo union foo typedef sequence<X> foo typedef X foo[10] AGB ■ Se pueden usar cualquiera de los dos tipos, incluso ambos a la vez. Con las referencias se pueden hacer las operaciones duplicate(), release(), is_nil() y narrow() que veremos AGB mas adelante. Mapeo del IDL en lenguaje C++ (Tipos de longitud variable) Nombre a; // MAL Nombre *a; // MAL Nombre &a; // MAL Nombre_var a; // BIEN Nombre_ptr a; // BIEN ■ Las interfases se convierten en clases. Cuando se obtiene una referencia a un objeto, lo que se tiene desde el punto de vista del programador es un puntero a su interfase. Estos punteros se manejan como clases de dos tipos (suponiendo que la interfase se llame Nombre): 11 } char * string_alloc(Ulong leng); // Reserva 1 char mas para el NULL char * string_dup(const char *); void string_free(char *); WChar * wstring_alloc(Ulong leng); // Reserva 1 WChar mas WChar * wstring_dup(const WChar *); void wstring_free(WChar *); AGB 12 Mapeo del IDL en lenguaje C++ (strings) ■ Mapeo del IDL en lenguaje C++ (strings) Ejemplo de clase String_var (es similar para otros tipos distintos al string): ■ CORBA::String_var s; cout << s; // Core dump (string NULL) Class String_var { public: String_var(); // inicializa el string a NULL String_var(char *); // Toma posesión del string (shallow copy) String_var(const char *); // No toma posesión (deep copy) String_var(const String_var &); // No toma posesión (deep copy) ~String_var(); // Destruye el string propio // ... private: char *s; HOLA MUNDO\0 }; AGB ■ CORBA::String_var s(CORBA::string_dup(“Hola”)); } // OK. No hay memory leak ya que ~String_var llama a string_free() ■ Otro ejemplo: const char *mensaje = “Hola”; { CORBA::String_var s(mensaje); } // OK. ~String_var libera solo su copia interna 13 AGB Otro: 14 Mapeo del IDL en lenguaje C++ (strings) ■ CORBA::String_var destino; destino = CORBA::string_dup(“Hola”); // destino toma propiedad CORBA::String_var origen; origen = CORBA::String_dup(“Mundo”); // origen toma propiedad destino = origen; // libera “Hola” y toma propiedad (deep copy) de “Mundo” ■ Otro ejemplo: { Mapeo del IDL en lenguaje C++ (strings) ■ Ejemplo: Los ejemplos anteriores funcionan bien si el compilador es ANSI C++ estricto. En caso contrario, se pueden evitar “core dumps” de tres maneras distintas: CORBA::String_var s1((const char *)”Hola”); // BIEN, pero poco elegante CORBA::String_var s2 = CORBA::string_dup(“Hola”); // BIEN const char *p = “Hola”; CORBA::String_var s3; s3 = p; // BIEN Si el compilador no es ANSI estricto, suele tomar “Hola” por char * (en vez de const char *), con lo cual String_var toma propiedad y al finalizar tratará de liberar un string estático: CORBA::String_var s1(“Hola”); // MAL si el compilador no es ANSI CORBA::String_var s2 = “Hola”; // MAL si el compilador no es ANSI CORBA::String_var s3; s3 = “Hola”; // MAL si el compilador no es ANSI AGB 15 AGB Mapeo del IDL en lenguaje C++ (strings) ■ Se pueden asignar String_var a punteros, pero con cuidado: AGB Mapeo del IDL en lenguaje C++ (strings) ■ CORBA::String_var s1 = CORBA::string_dup(“Hola”); const char *p1 = s1; // OK. p1 apunta al string interior de s1 char *p2; { CORBA::String_var s2 = CORBA::string_dup(“Mundo”); p2 = s2; // Shallow copy s1 = s2; // Libera “Hola”, hace deep copy a “Mundo” } cout << p1; // MAL: p1 apunta al vacío cout << p2; // MAL: p2 apunta al vacío 16 Como se debe pasar un string como parámetro de entrada a una función: void print_string(const char *s) // Se debe pasar como const char * { cout << s << endl; } int main() { CORBA::String_var msj = CORBA::string_dup(“Hola”); print_string(msj); // BIEN print_string(“Mundo”); // BIEN } 17 AGB 18 Mapeo del IDL en lenguaje C++ (strings) ■ String como parámetro de entrada-salida de una función: Mapeo del IDL en lenguaje C++ (strings) ■ void cambiar_string(const char *&s) // Se debe pasar como const char *& { // para que pueda apuntar a un nuevo espacio de memoria CORBA::string_free(s); s = CORBA::string_dup(“Nuevo string”); } int main() { CORBA::String_var msj = CORBA::string_dup(“Hola”); cambiar_string(msj); // BIEN cout << msj; // BIEN char *p = CORBA::string_dup(“Hola”); // Prohibido usar new cambiar_string(p); // BIEN cout << p; // BIEN CORBA::string_free(p); // Obligatorio } AGB CORBA::String_var s(CORBA::string_dup(“Hola”); print_string(s); // Puede fallar en compiladores no ANSI print_string(s.in()); // Funciona con cualquier compilador cambiar_string(s); // Puede fallar en compiladores no ANSI cambiar_string(s.inout()); // Funciona con cualquier compilador 19 AGB Mapeo del IDL en lenguaje C++ (strings) ■ El paso de string a una función podría fallar si el compilador no es ANSI C++ estricto. Para evitarlo, la clase String tiene varias funciones de ayuda (in, out, inout, _retn): out() es ligeramente distinto a inout(), ya que primero libera la memoria antes de entrar a la función. Por ejemplo: 20 Mapeo del IDL en lenguaje C++ (strings) ■ void leer_string(char * &s) { s = CORBA::string_dup(...); // Lee una línea de texto de algún sitio } _retn() se usa para retornar un string cediendo su propiedad. Por ejemplo: for(int i = 0 i < num_lineas; i++) { CORBA::String_var linea = get_line(); // Lee una linea de un archivo cout << linea; } // El destructor de linea libera el string Funciona con: Siendo la función: CORBA::String_var linea; leer_string(linea.out()); // La primera línea se lee y se ignora leer_string(linea.out()); // Libera la primera línea y lee la segunda char * get_line() { CORBA::String_var s = CORBA::string_dup(...); // Lee string de archivo if(error()) // Si se lanza una excepción, las variables locales se throw Exception(); // destruyen (“s” libera la memoria de su string) return s._retn(); // Aquí “s” cede la propiedad de su string } 21 AGB Mapeo del IDL en lenguaje C++ (strings) ■ Si hay strings dentro de otro tipo mas complejo (estructuras, secuencias, excepciones y arrays), entonces se mapea a String_mgr, que es idéntico a String_var, excepto que el constructor por defecto crea un string de longitud cero (en vez de inicializarlo como puntero nulo). PUNTERO NULO p ■ ■ ■ AGB 23 Los números fixed se mapean en la clase Fixed de C++, que contiene muchos constructores. Ejemplo: Fixed Fixed Fixed Fixed Fixed Fixed Fixed Fixed Fixed 0 Ello facilita las cosas al programador (no necesita inicializar los strings internos). Recordar que está prohibido pasar punteros nulos a través de una interfase IDL. El tipo String_mgr es para uso interno. No debe usarse directamente por el programador. 22 Mapeo del IDL en lenguaje C++ (Fixed) PUNTERO A STRING VACÍO q 0 AGB ■ a; // Constructor por defecto (1 dígito entero, 0 dígitos decimales) b = 45; // Equivale al tipo IDL fixed<2,0> c = 21.0; // Equivale al tipo IDL fixed<2,0> d = 21.3; // Equivale al tipo IDL fixed<3,1> e = 0.1; // Equivale al tipo IDL fixed<2,1> pero podría ser fixed<18,17> f = 1E30; // Equivale al tipo IDL fixed<31,0> g = 1E32; // Excepción DATA_CONVERSION h = “-123.45”; // Equivale al tipo IDL fixed<5,2> c = “0.1”; // Equivale al tipo IDL fixed<2,0>. Aquí no hay error de redondeo La clase Fixed proporciona además sobrecarga para todas las operaciones aritméticas (+, -, *, /, ++, --, =, +=, etc.), de comparación (>,<,>=,<=,==,!=) y de entrada-salida en flujos (<<,>>). AGB 24 Mapeo del IDL en lenguaje C++ (Estructuras) ■ Si la estructura contiene únicamente tipos de longitud fija, se comporta como cabe esperar: struct Dato { double x; long y; }; ■ Mapeo del IDL en lenguaje C++ (Estructuras) ■ // IDL struct Persona // IDL { string nombre; long edad; }; Se convierte en: ■ struct Dato // C++ { CORBA::Double x; CORBA::Long y; // Otras funciones miembro, para uso interno de CORBA }; AGB 25 AGB Uso estático: ■ Persona jefe, empleado; jefe.nombre = CORBA::string_dup(“Jose”); jefe.edad = 40; empleado.nombre = CORBA::string_dup(“Pepe”); empleado.edad = 25; jefe = empleado; // Copia profunda (deep copy) } // Aquí se destruye jefe y empleado y automáticamente también se destruyen // sus campos nombre AGB Se usa así: { // C++ const char * colores[] = { “blanco”, “rojo”, “azul”, “amarillo” }; Mezcla mezcla; mezcla.length(4); // Crea una secuencia de 4 strings vacíos (“” “” “” “”) for(CORBA::Ulong i = 0; i < mezcla.length(); i++) mezcla[i] = colores[i]; mezcla[2] = CORBA::string_dup(“verde”); // primero libera “azul” mezcla.length(6); // Hace crecer la secuencia a 6 (añade 2 strings vacíos) mezcla[4] = CORBA::string_dup(“verde”); mezcla[5] = CORBA::string_dup(“gris”); mezcla.length(3); // Trunca la secuencia a 3 (libera “amarillo”, “verde” y “gris”) } // La secuencia se destruye liberando sus elementos “blanco”, “rojo” y “verde” 27 AGB Mapeo del IDL en lenguaje C++ (Secuencias ilimitadas) ■ Ejemplo con manejo dinámico de memoria: ■ Genera en C++: // C++ 28 Mapeo del IDL en lenguaje C++ (Secuencias ilimitadas) typedef sequence<string> Mezcla; // IDL class Mezcla { ... }; Las secuencias son mas complejas, pues tienen mas funciones miembro de ayuda (veremos solo las mas relevantes). Ejemplo: typedef sequence<string> Mezcla; // IDL ==> genera en C++ ==> class Mezcla; Uso dinámico: Persona *jefe = new Persona; jefe->nombre = CORBA::string_dup(“Jose”); jefe->edad = 40; //... delete jefe; // Automáticamente se destruye el campo nombre 26 Mapeo del IDL en lenguaje C++ (Secuencias ilimitadas) { ■ Se convierte en: struct Persona // C++ { CORBA::String_mgr nombre; // Se inicializa automáticamente a “” CORBA::Long edad; // Otras funciones miembro, para uso interno de CORBA }; Mapeo del IDL en lenguaje C++ (Estructuras) ■ Si la estructura contiene también datos de longitud variable, se inicializan automáticamente evitando punteros nulos, y se destruyen también automáticamente: ■ Y se usa así: Resumiendo, se puede aumentar o disminuir el tamaño de la secuencia, así como averiguar su longitud, con la función miembro length(). Existen otros constructores: Mezcla mezcla(6); // Crea una secuencia de 6 strings const char * colores[] = { “blanco”, “rojo”, “azul”, “amarillo” }; Mezcla * mezcla = new Mezcla; mezcla->length(4); // Crea una secuencia de 4 strings vacíos (“” “” “” “”) for(CORBA::Ulong i = 0; i < mezcla->length(); i++) (*mezcla)[i] = colores[i]; // ... delete mezcla; // Libera todos los strings internos AGB ■ ■ 29 Los elementos de una secuencia no tienen por qué estar contiguos en memoria. La inserción (o truncamiento) de nuevos elementos se hace siempre por el final. AGB 30 Mapeo del IDL en lenguaje C++ (Secuencias limitadas) ■ Las secuencias limitadas son similares a las ilimitadas, pero hay que especificar el número máximo de elementos: Mapeo del IDL en lenguaje C++ (Arrays) ■ typedef float Valores[4]; // IDL struct Alumno { string nombre; float nota; }; typedef Alumno Estudiante[45]; typedef sequence <string, 45> Estudiantes; ■ ■ Hay que usar siempre typedef en el IDL para definir arrays: Con la función length() se puede acortar o alargar la secuencia, pero siempre sin sobrepasar ese número máximo. El comportamiento es indefinido si se intenta aumentar ese número máximo (posible core dump). ■ Y luego se pueden usar así (uso normal y corriente): Valores x = { 1.0, 4.8, -3.2, 0.001 }; // C++ x[0] = x[2]; Estudiante asignatura; asignatura[0].nombre = CORBA::string_dup(“Luisa”); asignatura[0].nota = 3.8; AGB 31 AGB Mapeo del IDL en lenguaje C++ (Arrays) ■ Si se desean manejar los arrays dinámicamente no se debe usar new y delete sino Nombre_slice, Nombre_alloc(), Nombre_free(), Nombre_dup() y Nombre_copy(): AGB Mapeo del IDL en lenguaje C++ (Uniones) ■ ■ Valores_slice *p = Valores_alloc(4); // Pide memoria. Si no hay retorna null p[3] = -0.1; Valores_slice *q = Valores_dup(p); // Copia Valores x; // Estático Valores_copy(&x, p); // Copia profunda para arrays estáticos y/o dinámicos Valores_free(p); // Libera memoria Valores_free(q); // Libera memoria ■ ■ Ejemplo: union U switch(char) { case ‘L’: long numero; case ‘c’: case ‘C’: char letra; default: string mensaje; }; Las uniones IDL no se mapean a uniones C++ debido a que son discriminadas. Se mapean en clases de C++. No hay garantías de que todos los miembros de la unión se mapeen en el mismo lugar de memoria. Cada ORB podría hacerlo a su manera. De modo que una union CORBA no sirve para hacer conversión de tipos, como en C o C++. De todos modos, solo es lícito acceder a un miembro, si su discriminante lo indica como activo. Para acceder al discriminante se usa la función miembro _d() 33 AGB Mapeo del IDL en lenguaje C++ (Uniones) ■ 34 Mapeo del IDL en lenguaje C++ (Uniones) ■ // IDL 32 Uso: U z; // La unión z no está inicializada z.numero = 47; // Activa numero if(z._d() == ‘L’) // Verifica discriminante a ver si está activo numero cout<<z.numero(); // Imprime numero z.letra(‘H’); // Activa letra, pero el discriminante puede ser ‘c’ o ‘C’ z._d(‘C’); // Impone que sea ‘C’ z._d(‘L’); // Ilegal. No se puede cambiar el discriminante si ello activa o // desactiva miembros. z.mensaje(CORBA::string_dup(“Hola”)); // El discriminante no queda bien definido, // por tratarse del miembro default AGB 35 AGB 36 Mapeo del IDL en lenguaje C++ (Operaciones) ■ Ejemplo: ■ interface Banco // IDL { void depositar(in unsigned long cantidad); unsigned long balance(); }; ■ Mapeo del IDL en lenguaje C++ (Operaciones) Banco_ptr banco = ... ; // Obtiene una referencia a Banco banco->depositar(100); cout << banco->balance(); CORBA::release(banco); ■ Genera: 37 AGB AGB Mapeo del IDL en lenguaje C++ (Atributos) Ejemplo: ■ ■ entrada (in) salida (out) entrada-salida (inout) retorno Y a su vez hay dos formatos para hacer el paso de parámetros: 39 AGB AGB Mapeo del IDL en lenguaje C++ (Paso de parámetros) A bajo nivel es así: IDL simple enum fixed string wstring any objref sequence struct,fijo union,fijo array,fijo struct,variable union,variable array,variable de de de de – A bajo nivel (cada tipo de parámetro para cada forma es distinto) – Usando las clases _var (homogeneización: el paso de parámetros es igual para todos los tipos) class A // C++ { public: virtual CORBA::Short B() = 0; virtual void B(CORBA::Short) = 0; virtual CORBA::Char C() = 0; }; ■ Los parámetros se pueden pasar de 4 formas: – – – – Se convierte a: 38 Mapeo del IDL en lenguaje C++ (Paso de parámetros) interface A // IDL { attibute short B; readonly attribute char C; }; ■ O bien: Banco_var banco = ... ; // Obtiene una referencia a Banco banco->depositar(100); cout << banco->balance(); class Banco // C++ { public: virtual void depositar(CORBA::Ulong cantidad) = 0; virtual CORBA::ULong balance() = 0; // ... }; ■ Y se usa así: in inout simple simple & enum enum & const Fixed & Fixed & const char * char * & const WChar * WChar * & const Any & Any & objref_ptr objref_ptr & const sequence & sequence & const struct & struct & const union & union & const array array_slice * const struct & struct & const union & union & const array array_slice * AGB out simple & enum & Fixed & char * & WChar * & Any * & objref_ptr & sequence * & struct & union & array_slice * struct * & union * & array_slice * & return simple enum Fixed char * WChar * Any * objref_ptr sequence * struct union array_slice * struct * union * array_slice * 41 40 Mapeo del IDL en lenguaje C++ (Paso de parámetros) ■ Usando clases _var es así: IDL string wstring any objref sequence struct union array ■ in const const const const const const const const String_var & WString_var & Any_var & objref_var & sequence_var & struct_var & union_var & array_var & inout/out String_var & WString_var & Any_var & objref_var & sequence_var & struct_var & union_var & array_var & return String_var WString_var Any_var objref_var sequence_var struct_var union_var array_var Para los tipos simples, las enumeraciones y los fixed no se generan clases _var. Se manejan como en la tabla anterior que, para ellos, es homogénea a este nuevo formato. AGB 42 Mapeo del IDL en lenguaje C++ (Paso de parámetros) ■ ■ La mejor táctica es usar tipos _var, con lo cual es casi imposible que haya “memory leaks”. Sin embargo, hay que tener cuidado con una situación: si la función remota retorna un parámetro variable, siempre hay que asignarlo. Ejemplo: interface Foo // IDL { string get(in long valor); }; ■ Y se usa: obj->get(7); // Error: memory leak String_var s = obj->get(7); // OK AGB 43