Archive
Enlazado dinámico en C++ (dynamic linking) III: Carga dinámica de objetos
Hasta ahora, en varios artículos referentes a este tema: Introducción a dynamic linking, Carga dinámica de bibliotecas, hemos visto como, en lenguaje C, podemos escribir bibliotecas de funciones y, sin necesidad de conocer su código fuente, ejecutar funciones encerradas dentro de esas bibliotecas en nuestros proyectos, así como cargar dicho código al vuelo.
Pero todo se complica un poco cuando queremos hacer lo mismo con C++, y en concreto, cuando estamos exportando clases completas, ya que de primeras necesitamos una forma de traernos algo parecido a un tipo, el nombre de la clase, y con dlopen() y dlsym(), nos podemos traer sólo una referencia de memoria, por lo que junto con la clase que programemos en C++ tendremos que incluir una función que llame al constructor. Por otra parte, cuando compilamos en C++, las referencias a las funciones se guardan de una forma especial y tendremos que exportarlos como símbolos de C (en realidad siempre que hagamos shared objects en C++ debemos hacerlo.
Por otra parte, cuando estemos enlazando clases dinámicamente, debemos conocer qué tiene la clase, a la hora de acceder a sus métodos y atributos, antes de la compilación se debe conocer qué contiene la clase. Esto, en principio puede limitarnos mucho: podremos ahorrar memoria, estaremos limitando la estructura de la clase.
Desarrollaremos un ejemplo de una agenda personal, en la que aceptamos el nombre y el mail de una persona. La clase Person se desarrollará en un shared object que el programa principal instanciará, este shared object será capaz de exportar la información para su salida en pantalla (luego crearemos otro shared object que la exporte en XML).
Primero vamos a construir una interfaz de Person (la llamaremos _Person) con los métodos del programa principal que vamos a necesitar en nuestra aplicación (en el caso de querer que otras personas desarrollen bibliotecas que nuestro programa vaya a aceptar, esta interfaz sería la parte que debemos dar a los desarrolladores).
_person.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | /* @(#)_person.h */ #ifndef _PPERSON_H #define _PPERSON_H 1 #include <string> using namespace std; class _Person { public: virtual ~_Person() { }; // Importante no poner =0; Este método tiene que implementarse, aunque sea vacío virtual void setNombre(string nombre)=0; virtual void setEmail(string email)=0; virtual string getData()=0; }; #endif /* _PPERSON_H */ |
Como interfaz, todos los métodos que encontramos en esta clase son virtuales. Esta clase no podrá ser instanciada, sólo podrá ser utilizada como punto de partida para la construcción de otras clases.
Ahora procederemos creando la clase Person (que exportará a pantalla el nombre y el mail de la persona introducida)
person.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | /* @(#)person.h */ #ifndef _PERSON_H #define _PERSON_H 1 #include "_person.h" #include <string> using namespace std; class Person: public _Person { public: Person(string nombre, string email); ~Person(); void setNombre(string nombre); void setEmail(string email); string getData(); private: string nombre; string email; }; #endif /* _PERSON_H */ |
Ahora procederemos a la creación del archivo cpp que incluirá dos funciones en C (una instanciará la clase y nos devolverá la referencia de memoria de dicho objeto, y otra destruirá el objeto)
person.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | #include "person.h" using namespace std; Person::Person(string nombre, string email):nombre(nombre),email(email) { } Person::~Person() { } void Person::setNombre(string nombre) { this->nombre=nombre; } void Person::setEmail(string email) { this->email=email; } string Person::getData() { return "Nombre: "+nombre+"\nEmail: "+email+"\n\n"; } extern "C" { Person* create(string nombre, string email) { return new Person(nombre, email); } void destroy(Person *p) { delete p; } } |
En este archivo vemos, que para exportar en formato C, utilizamos la palabra clave extern “C”. Nuestras funciones se llamarán create() y destroy(), y éstos son los métodos a los que llamará nuestro programa principal.
Para compilar la clase Person, hacemos lo siguiente:
$ g++ -fPIC -shared -o person.so person.cpp
El programa principal, conocerá el prototipo _Person, el prototipo de la función create(string, string) y el prototipo de la función destroy(_Person*), con lo cual ya tenemos lo suficiente para crear una instancia, destruirla y utilizarla:
agenda.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | #include <cstdlib> #include <iostream> #include "_person.h" #include <dlfcn.h> #include <string> using namespace std; typedef _Person* (*_PersonCreate)(string nombre, string email); typedef void (*_PersonDestroy)(_Person* p); int main(int argc, char *argv[]) { void *personlib = dlopen("./person.so", RTLD_LAZY); _PersonCreate personCreate = (_PersonCreate)dlsym(personlib, "create"); if (dlerror()!=NULL) { cerr <<"ERROR"<<endl; exit(1); } _PersonDestroy personDestroy = (_PersonDestroy)dlsym(personlib, "destroy"); if (dlerror()!=NULL) { cerr <<"ERROR"<<endl; exit(1); } _Person *persona = personCreate("Gaspar Fernandez", "email@midominio.com"); cout << persona->getData(); personDestroy(persona); dlclose(personlib); return EXIT_SUCCESS; } |
Para compilar agenda.cpp hacemos lo siguiente:
$ g++ -o agenda agenda.cpp -ldl
Ahora sólo queda imaginar una forma de poder hacer el programa principal más sencillo, aunque complicaremos un poco una parte del programa, en concreto vamos a hacer una clase estática llamada PersonLoader que nos ayudará cada vez que tengamos que crear una instancia de _Person; si sólo vamos a crear una instancia, una vez no pasa nada, pero si complicamos el programa, sí que nos será útil:
personloader.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | /* @(#)personloader.h */ #ifndef _PERSONLOADER_H #define _PERSONLOADER_H 1 #include "_person.h" #include <dlfcn.h> #include <string> using namespace std; class PersonLoader { public: typedef enum { ERROR_EXCEPTION, ERROR_EXIT, ERROR_RETURN, ERROR_IGNORE } ErrorAction; /* Error handling */ static void setErrorAction(ErrorAction a); /* Library loading */ static void setLib(string libFile); static bool loadLib(string libFile); static bool loadLib(); static bool unloadLib(); /* Object creation/destruction */ static _Person *create(string nombre, string email); static bool destroy(_Person *p); /* Debugging */ static void setDebugging(int value); private: static bool error (string msg); static void exitError(string msg); static void debugMsg(string msg); static string libFile; static void *libperson; static int debugging; static ErrorAction errorAction; }; #endif /* _PERSONLOADER_H */ |
personloader.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | #include "personloader.h" #include <iostream> #include <cstdlib> #include <stdexcept> using namespace std; void *PersonLoader::libperson = NULL; PersonLoader::ErrorAction PersonLoader::errorAction=ERROR_EXCEPTION; string PersonLoader::libFile = "./person.so"; int PersonLoader::debugging=1; void PersonLoader::setErrorAction(PersonLoader::ErrorAction a) { PersonLoader::errorAction=a; } void PersonLoader::setLib(string libFile) { PersonLoader::libFile=libFile; PersonLoader::debugMsg("Library file set to "+libFile); } bool PersonLoader::loadLib() { if (libFile!="") { if (libperson!=NULL) return true; // Library already loaded libperson = dlopen(libFile.c_str(), RTLD_LAZY); if (libperson==NULL) return PersonLoader::error("Library "+libFile+" could not be loaded"); PersonLoader::debugMsg("Library file "+libFile+" loaded successfully."); } else return PersonLoader::error("Empty library filename"); return true; } bool PersonLoader::loadLib(string libFile) { PersonLoader::setLib(libFile); return PersonLoader::loadLib(); } bool PersonLoader::unloadLib() { if (libperson==NULL) return true; int err = dlclose(libperson); libperson=NULL; if (err!=0) return error("Couldn't unload "+libFile+" properly"); PersonLoader::debugMsg("Library file "+libFile+" unloaded successfully."); return true; } _Person* PersonLoader::create(string nombre, string email) { char *err; if (!loadLib()) { PersonLoader::error("Library not loaded"); return NULL; } _PersonCreate personCreate = (_PersonCreate)dlsym(libperson, "create"); if ((err=dlerror())!=NULL) { PersonLoader::error("Error finding create Symbol: "+(string)err); return NULL; } PersonLoader::debugMsg("Symbol create loaded."); return personCreate(nombre, email); } bool PersonLoader::destroy(_Person *p) { char *err; if (!loadLib()) return (PersonLoader::error("Library not loaded"))?true:false; _PersonDestroy personDestroy = (_PersonDestroy)dlsym(libperson, "destroy"); if ((err=dlerror())!=(char*)NULL) return (PersonLoader::error("Error finding destroy Symbol: "+(string)err))?true:false; PersonLoader::debugMsg("Symbol destroy loaded."); personDestroy(p); return true; } bool PersonLoader::error(string msg) { switch (errorAction) { case ERROR_EXCEPTION: throw runtime_error(msg); break; case ERROR_EXIT : exitError(msg); break; case ERROR_RETURN : return false; break; case ERROR_IGNORE : return true; break; default: return false; } } void PersonLoader::exitError(string msg) { cerr << "ERROR: "<<msg<<endl; exit(1); } void PersonLoader::setDebugging(int value) { PersonLoader::debugging=(value>0)?1:0; } void PersonLoader::debugMsg(string msg) { if (debugging) cerr << msg << endl; } |
De esta clase hay muchas cosas que podemos quitar, pero me ha parecido interesante incluir:
- Un modo depuración (debugging)
- Elección del tipo de respuesta a los errores (desde una excepción, a salir del programa o hasta ignorarlo)
- Posibilidad de carga y descarga de la biblioteca a mano
Por otra parte, esta clase nos puede servir como base para otros cargadores de otras bibliotecas compartidas, podremos fácilmente hacer que los métodos create y destroy sean abstractos, dependiendo de los parámetros que les especifiquemos o haya que especificarle a la función create() de la biblioteca compartida. Por supuesto podemos, para un programa definitivo, poner debugging a 0 para no mostrar mensajes en pantalla.
Ahora, el programa principal agenda.cpp quedará así:
agenda.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #include <cstdlib> #include <iostream> #include <string> #include "personloader.h" using namespace std; int main(int argc, char *argv[]) { PersonLoader::setErrorAction(PersonLoader::ERROR_IGNORE); _Person *persona=PersonLoader::create("Gaspar Fernandez", "email@midominio.com"); cout << persona->getData(); PersonLoader::destroy(persona); PersonLoader::unloadLib(); } |
Y para compilarlo hacemos:
$ gcc -o agenda agenda.cpp personloader.cpp -ldl
Como vemos, ahora es mucho más sencillo trabajar con los shared objects desde uno de nuestros programas.
Foto: Hey Paul (Flickr) CC-A a 02/03/2013
Preparando nuestro entorno para Arduino sin Java
Como comenté en el post anterior, la plataforma Arduino viene con un IDE hecho en Java, no es muy completo, pero nos hace el apaño, aunque, como es mi caso, estoy acostumbrado a Emacs. Aunque esta guía no estará limitada a este editor, ni a Ubuntu (o basados en él).
El objetivo, es usar nuestro IDE o editor preferido para trabajar con estos pequeños bichos programables.
En principio tenemos que instalar las herramientas para compilar los programas para la plataforma (esto es común con todas las instalaciones). Los Arduino utilizan chips Atmel AVR, por lo que usaremos el compilador AVR-GCC para esta plataforma. Como construir el ejecutable es un proceso más o menos costoso (de hacer a mano) utilizaremos la herramienta de construcción scons con la que podremos generar el ejecutable gracias a un script en python.
Ubuntu y derivados (en mi caso Linux Mint)
Lo primero es instalar los programas necesarios:
$ sudo apt-get install gcc-avr avr-libc scons avrdude python-serial
En este caso:
- gcc-avr: Es el compilador
- avr-libc: Son las librerías para la plataforma
- scons: La herramienta de construcción
- avrdude: Utilidad para programar chips avr
- python-serial: Es un módulo de python para trabajar con el puerto serie (nos servirá para subir los programas al Arduino).
Sabayon Linux / Gentoo también
Ya que recientemente he instalado Sabayon en mi ordenador, quisiera extender esto un poco más:
En este caso, tendremos que instalar, ya sea vía Entropy (forma gráfica) o equo (en consola) los paquetes crossdev, scons y pyserial:
$ equo install crossdev scons avrdude pyserial
$emerge crossdev scons avrdude pyserial # Sólo para Gentoo
Tras ello tenemos que descargar y construir las bibliotecas y el entorno para compilar para AVR:
$ USE=”-openmp” crossdev -t avr -s4 -S –without-headers
(y esto tardará un rato)
Parte común y manual
Tras ello, descargamos la plataforma de aquí [ 64bit , 32bit versión 0022, última a 31 de Julio de 2011 (web de descarga con más opciones) ], por ahora bien podíamos estar instalando la plataforma completa, ya que el archivo que hemos descargado contiene el IDE Arduino. Ahora introduciremos una variación.
Debemos descomprimir el archivo que hemos descargado, yo he creado un directorio en mi home: /home/gaspy/proyectos/Arduino:
$ mkdir -p ~/proyectos/Arduino
$ cd ~/proyectos/Arduino
$ tar xvzf arduino-0022.tgz
Luego, debemos descargar el arma secreta, es un script de construcción para scons y Arduino. Podemos encontrarlo en la página oficial del proyecto arscons. Lo podéis descargar de aquí:
- Sitio oficial
- SConstruct (8Kb)
Este archivo tiene que estar en el directorio de cada proyecto. Nuestro primer proyecto será ej1
$ mkdir ej1
$ wget http://arscons.googlecode.com/svn/trunk/SConstruct
Antes de continuar debemos hacer alguna modificación de rutas en el archivo SConstruct, por lo que es una buena idea dejar un archivo SConstruct operativo y copiarlo dentro de nuestros proyectos. Las modificaciones serían para las variables:
- ARDUINO_HOME_DEFAULT, que debe contener el directorio donde estan instaladas las herramientas de Arduino, lo que acabamos de descomprimir. En mi caso /home/gaspy/proyectos/Arduino/arduino-0022/
- ARDUINO_BOARD, debe ser el modelo de nuestra placa.
- ARDUINO_CONF, nos dice dónde está la configuración de las placas Arduino, donde buscará las especificaciones de la nuestra.
Vemos aquí la modificación de las variables en su contexto:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | if platform == 'darwin': # For MacOS X, pick up the AVR tools from within Arduino.app ARDUINO_HOME_DEFAULT = '/home/gaspy/proyectos/Arduino/arduino-0022/' ARDUINO_PORT_DEFAULT = getUsbTty('/dev/tty.usbserial*') elif platform == 'winnt': # TODO: add Windows port. assert(False) # not supported.. yet. ARDUINO_HOME_DEFAULT = None ARDUINO_PORT_DEFAULT = None else: # For Ubuntu Linux (9.10 or higher) ####################### ESTA ES LA LÍNEA IMPORTANTE ARDUINO_HOME_DEFAULT = '/home/gaspy/proyectos/Arduino/arduino-0022/' ####################### FIN DE LA LÍNEA IMPORTANTE ARDUINO_PORT_DEFAULT = getUsbTty('/dev/ttyUSB*') AVR_BIN_PREFIX = 'avr-' ARDUINO_HOME = ARGUMENTS.get('ARDUINO_HOME', ARDUINO_HOME_DEFAULT) ARDUINO_PORT = ARGUMENTS.get('ARDUINO_PORT', ARDUINO_PORT_DEFAULT) ####################### OTRA LÍNEA IMPORTANTE ARDUINO_BOARD = ARGUMENTS.get('ARDUINO_BOARD', 'diecimila') ####################### FIN DE OTRA LÍNEA IMPORTANTE ARDUINO_VER = ARGUMENTS.get('ARDUINO_VER', 20) # Arduino 0020 RST_TRIGGER = ARGUMENTS.get('RST_TRIGGER', None) # use built-in pulseDTR() by default EXTRA_LIB = ARGUMENTS.get('EXTRA_LIB', None) # handy for adding another arduino-lib dir ARDUINO_CORE = pathJoin(ARDUINO_HOME, 'hardware/arduino/cores/arduino') ARDUINO_SKEL = pathJoin(ARDUINO_CORE, 'main.cpp') ####################### OTRA LÍNEA IMPORTANTE ARDUINO_CONF = pathJoin(ARDUINO_HOME, '/home/gaspy/proyectos/Arduino/arduino-0022/hardware/arduino/boards.txt') ####################### FIN DE OTRA LÍNEA IMPORTANTE |
Dentro del directorio ej1 (de nuestro primer proyecto con Arduino) tiene que haber un archivo llamado ej1.pde que contendrá el código fuente del programa que queremos compilar y subir a nuestro Arduino. ¡ A trabajar ! Podemos coger este código de ejemplo para hacer que un led parpadee. Sólo copiar y pegar.
NOTA para Gentoo/Sabayon: Hay algún pequeño bug en esta distribución y al compilar no funcionará porque no existen los ficheros de biblioteca, el caso es que sí existen, pero no los encuentra. Para eso, modificaremos algunas líneas más de SConstruct:
1
2
3
4
5
6
7
8
9
10
11 envArduino.Append(BUILDERS = {'Processing':Builder(action = fnProcessing,
suffix = '.cpp', src_suffix = '.pde')})
######## IMPORTANTE, LE PONEMOS LA RUTA DONDE TIENE QUE BUSCAR avr5.x
envArduino.Append(BUILDERS={'Elf':Builder(action=AVR_BIN_PREFIX+'gcc '+
'-Wl,-dT /usr/lib/binutils/avr/2.20.1/ldscripts/avr5.x -mmcu=%s -Os -Wl,--gc-sections -o $TARGET $SOURCES -lm'%MCU)})
######## IMPORTANTE, LE PONEMOS LA RUTA DONDE TIENE QUE BUSCAR avr5.x
envArduino.Append(BUILDERS={'Hex':Builder(action=AVR_BIN_PREFIX+'objcopy '+
'-O ihex -R .eeprom $SOURCES $TARGET')})
# add arduino core sources
VariantDir('build/core', ARDUINO_CORE)También falla otra cosa, no se encuentra el archivo crtm168.o (mi placa Diecimila, este es el chip que usa), el archivo está en /usr/avr/lib/avr5/crtm168.o , pero lo busca en /usr/avr/lib/ podemos copiarlo como root, hacer un enlace,
root$ ln -s /usr/avr/lib/avr5/crtm168.o /usr/avr/lib/
o copiarlo como usuario en el directorio del proyecto, total, son 2Kb.
Para compilar el proyecto debemos, en el mismo directorio actual, ejecutar:
$ scons
y él se encarga de todo. Cuando estemos listos para subir al Arduino, lo conectamos y ejecutamos:
$ scons upload
Lo realmente bueno de esto es que podemos utilizar el editor o IDE que queramos, incluso podemos asignar una tecla rápida para compilar+subir
Mejoras para Emacs
Si utilizas Emacs, te interesará introducir lo siguiente en tu ~/.emacs :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | (defun run-scons () "run scons" (interactive) (shell-command "scons")) (defun run-scons-upload () "run scons upload" (interactive) (shell-command "scons upload")) ; Ejecuta scons y scons upload con C-x SPC y C-x C-SPC (global-set-key (kbd "C-x SPC") 'run-scons) (global-set-key (kbd "C-x C-SPC") 'run-scons-upload) ; Asigna la extensión .pde al modo c++-mode (setq auto-mode-alist (cons '("\\.pde$" . c++-mode) auto-mode-alist)) |
Aunque hay formas más elegantes de hacerlo, esta es la forma más rápida de asignar una tecla a scons; puede que utilicemos make para otras y no queramos modificar nuestro constructor. Otra cosa, las teclas elegidas no son las mejores, tengo que perfeccionar esto.
Por otra parte, es importante que los archivos .pde se abran directamente con el modo c++ y así podemos aprovechar sus ventajas.
Otros editores
Si usáis kate, podéis mirar este artículo para coger inspiración.
Si usáis gedit, podéis leer esto. (fuera de Poesía Binaria).
Sabayon, a medio camino entre la cocina y la oficina

El sabayón es un postre italiano del siglo XVI… bueno y también una distribución Linux bastante completa, aunque no se oiga mucho hablar de ella, ni ha salido en las listas anuales de mejor distribución del año ni nada, creo que es una de las que tenemos que tener en cuenta.
Es cierto, que en el perfil de usuario de esta distribución no entra cualquier persona, ya que, aunque te da muchas cosas hechas, también hay muchas otras que hay que retocar (nada es a gusto de todo el mundo).
Llevo poco tiempo como usuario de esta distribución. Y llevo 6 años siendo usuario de Gentoo, cosa que, aunque me ha dado mucha lástima abandonar era un paso un poco lógico para mi. El hecho es que fácilmente podemos considerar Sabayon como un paso más allá, trae gran parte de la configuración hecha y paquetes precompilados por lo que, levantar un sistema con Sabayon nos llevará muchísimo menos tiempo que con Gentoo.
¿Por qué tomé la decisión de cambiarme? Para hacer el mantenimiento más rápidamente, ya que lo utilizaba en mi equipo de trabajo, no tenía tiempo para dejar el ordenador dos días compilando paquetes para que luego me fallara una flag de gcc no soportada, no se terminara de compilar y tuviera que estar otros dos días. Llegó el momento que me tocaba actualizar la distribución después de haber instalado algún que otro paquete nuevo que se cargaba algunas dependencias de otro paquete y, en fin, me junté con una cantidad tremenda de paquetes obsoletos cuya limpieza sería terrible.
Aunque me queje mucho de Gentoo y su emerge he de decir públicamente que esa es la razón por la que instalé Sabayon, y es que los paquetes precompilados están muy bien, pero a veces me gusta poder personalizar alguna instalación, o probar alguna cosa que se considera insegura y que no tenga que prestar mucha atención mirando las dependencias, jugando con el configure y todo eso.
Pero bueno, no es sólo una distribución en la que puedes fácilmente ponerte a compilar paquetes, o coger los paquetes binarios e instalarlos; además, por defecto, no carga demasiados servicios. Por ejemplo, una de mis mayores quejas sobre Ubuntu o Linux Mint (de la que también soy usuario) es que trae muchas cosas activadas por defecto y a medida que instalamos software carga más servicios de arranque: Samba, Saned, Nfs, Cups, Bluetooth, Virtualbox, Pcmcia, Servidores varios, Cron varios, etc; y muchas veces, estos servicios prefiero arrancarlos manualmente o que me pregunte (Ubuntu sólo lo hace alguna vez); Sabayon es ligera, muy ligera, casi tanto como mi antigua Gentoo (sé que optimizar en tiempo de compilación en ocasiones está sobrevalorado, otras veces se nota, pero pocas veces, aunque para esas veces tengo la posibilidad de hacer emerge).
Aunque no todo iba a ser bueno, en algo menos de tres semanas con la distribución he encontrado tres bugs (aunque reportado 1, los otros ya se habían reportado), aunque no han durado más de dos días; el equipo de desarrollo es rápido, aunque el bug que reporté dejó KO mi sistema durante unas horas, por lo que hay que andar con ojo. Por otra parte, de algunos paquetes no encontramos las últimas versiones lo cual puede ser bueno y malo a la vez.
De todas formas nos encontramos ante una distribución muy personalizable que podemos utilizar para mil cosas y en la que nos podemos basar y confiar para muchos de nuestros proyectos.
Más info en Wikipedia
Página oficial
Foto: Wouter Hagens
WiFi USB Conceptronic C150RUSM (RT3070) Editando el módulo para que funcione
Si habéis adquirido este adaptador Wifi USB, veréis que Linux, al menos hasta la fecha, no lo detecta, es más, pasa de nosotros, y aunque carguemos el driver a mano con modprobe; seguirá sin pasar.
Afortunadamente, no es nada difícil, y es algo que me hace estar un poco más feliz por utilizar software libre, y es que si el driver no reconoce el dispositivo como suyo, voy a presentárselo.
En principio, no sabía qué driver utilizaba el dispositivo. Busqué en los drivers para Windows, y se instalaba el rt2870, por lo que todo el proceso que diré a continuación lo hice con dicho driver, y no conseguí resultados, así que envié un e-mail a Conceptronics, que afortunadamente tardaron menos de 10h en contestar donde me dijeron que el driver era ralink 3070. Así que fui a la página de descarga de drivers para Linux de Ralink. Una vez ahí descargué el driver correspondiente.
Ahora tenemos que descomprimirlo y buscar en qué archivo están la asociación de dispositivos. (Tendremos que buscar la macro USB_DEVICE):
~/temporal/ $ tar xvjf 2011_0107_RT3070_RT3370_Linux_STA_v2.5.0.1_DPO.tar.bz2
$/temporal/ $ cd 2011_0107_RT3070_RT3370_Linux_STA_v2.5.0.1_DPO/ # Para qué está el tabulador!
~/temporal/2011_0107_RT3070_RT3370_Linux_STA_v2.5.0.1_DPO $ egrep -R ‘USB_DEVICE’ *
Vemos que es el archivo common/rtusb_dev_id.c el que tiene todos los dispositivos.
Ahora, con lsusb miramos cuál es el dispositivo USB que tenemos. En algunas versiones no da descripción del dispositivo, o puede que tengamos demasiados dispositivos conectados, podemos probar ejecutar el comando con el dispositivo enchufado y desenchufado, y comparar, así vemos cuál falta.
A mí me detectaba el dispositivo como un Edimax, en la dirección 7392:3734.
En el archivo common/rtusb_dev_id.c, no encontramos ese dispositivo. Tendremos que buscar la línea {USB_DEVICE(0x7392,0x3734)} y dado que no la encontramos, la creamos, al final de todos los dispositivos.
Sólo queda hacer un make && make install y ya tenemos el módulo funcionando. Antes de trabajar con este dispositivo es necesario levantarlo:
$ ifconfig ra0 up
Aunque la mayoría de los gestores de red lo harán bien. El driver no está del todo completo para este dispositivo, pero nos puede hacer el apaño mientras sale un driver mejor. Y así, le vamos perdiendo un poco de miedo al kernel.
La importancia de conocer atajos de teclado en nuestro IDE/Editor favorito
Siempre aconsejo a alguien que esté aprendiendo a programar que lo primero es sentirte bien con el entorno o IDE que manejas. A veces, en muchos centros de enseñanza se impone un entorno, por ejemplo Dev-Cpp o Borland C, pero cuando nos toca ponernos a programar algo en serio, ya sea para nosotros o para trabajar lo primero es elegir un IDE con el que nos encontremos cómodos.
Tanto para personas que están empezando como para expertos suelo hacer que prueben alguno de estos tres: Eclipse, Netbeans o Code::Blocks y que echen un rato intentando familiarizarse con el entorno, sus caprichos y su metodología.
Aunque si ya es importante familiarizarse con el entorno, ahora tenemos que hacer que valga la pena estar programando con un IDE frente a un editor de texto plano. Los IDEs tendrán muchas opciones que nos facilitarán la tarea como programadores, por ejemplo, lo primero que destacamos es que la mayoría de ellos colorean el código que escribimos; es posible que a algunas personas (conozco algún ejemplo) odien que aparezca el código en colores, incluso no lo entiendan; pero es una de las grandes herramientas que tenemos para detectar errores de un vistazo y para saber si una palabra es función/variable/texto/etc.
Pero a medida que vamos programando, tenemos la necesidad de hacer las cosas más rápido, y para ello están los atajos de teclado, por ejemplo:
- Compilar un programa o ejecutarlo (aunque tengamos un botón para eso, tardamos mucho más tiempo en coger el ratón y hacer click que en pulsar una tecla o una combinación).
- Copiar/cortar/pegar/salvar, es una tarea muy común, aunque no sea específica de programación, pero aún veo programadores experimentados que hacen click con el derecho o buscan el menú editar y escogen la opción que necesitan. Lo más común aquí es Control+X (cortar), Control+C (copiar), Control+V (pegar), Control +S (salvar), aunque depende del editor.
- Auto-indentado, también llamado auto-anidado, auto-sangrado y nos permite insertar espacios o tabulaciones delante de cada línea para el correcto sangrado de cada línea de código; en ocasiones cuando copiamos o analizamos código de diversas fuentes, tal vez no esté correctamente indentado, también nos puede valer, en el proceso de aprendizaje, para ver cómo sería la forma correcta.
- Alineado automático. A veces separamos la inicialización de un array, o los argumentos que pasamos a una función en varias líneas, pero queremos que queden todos alineados. Algunos lo hacen automáticamente, otros necesitan pulsar una tecla o elegir una opción. A veces es la misma que el auto-indentado.
- Búsqueda de texto. Es importante localizar rápidamente un texto (variable, función, macro, etc) dentro de nuestro código y luego regresar al punto donde estábamos.
- Ir a una línea determinada. El hecho de dirigirnos a una línea determinada es importante a la hora de ver los errores producidos y a la hora de comentar el código con otras personas
- Comentarios automáticos. Una buena opción cuando se trata de comentar una región de texto o introducir comentarios al lado de una línea de código ya escrita.
- Rellenado automático. Muchos IDEs permiten completar automáticamente lo que queremos escribir gracias a lo que ya hemos escrito o a los archivos / librerías que incluimos, esto nos ayuda a escribir menos y hacerlo todo más rápido.
- División de pantalla. Algunos IDEs permiten dividir la pantalla y editar varios ficheros a la vez, o varias zonas de un mismo archivo.
- Borrado de texto. En ocasiones nos hace falta borrar una línea entera o una palabra. Podemos seleccionar y borrar, aunque a veces lo podemos hacer con una sola pulsación de teclado.
- Navegación de texto. Es importante familiarizarnos con las teclas Av-pag (Page Up), Re-pag (Page Down), Inicio, Fin, las flechas, y esto nos vale tanto para navegar como seleccionar texto; aunque en ocasiones, en muchos sistemas podemos utilizar Control para modifcar el comportamiento de estas, por por ejemplo, lo más común es:
- Control + (izq/der): Navegamos entre palabras
- Control + (arriba/abajo): Navegamos entre bloques de texto (párrafos)
- Control + Inicio: Inicio del documento
- Control + Fin: Fin del documento
En muchos casos no he incluido las teclas en cuestión ya que depende del IDE utilizado. Al principio puede que gastemos un tiempo investigando las teclas rápidas (muchos IDEs nos dicen la tecla rápida cuando vamos a acceder a la opción vía menú o vía comando), y tal vez al principio cuando las empezamos a usar también nos cueste trabajo, pero a la larga ahorraremos tiempo y programaremos mucho más cómodamente.
Tecla rápida para compilar con Kate
Aunque tenemos a nuestra disposición gran cantidad de IDEs para programación, a veces viene bien un programa muy rápido y que tenga capacidad para compilar y ejecutar pequeños programas.
Aunque tenemos la posibilidad de cargar una consola integrada, pero no es suficiente.
Nos dirigimos a la configuración del programa…
Elegimos herramientas externas y escogemos Nuevo, para crear una nueva herramienta. Ahí encontraremos la siguiente ventana:
Rellenamos los campos y en script escribimos lo siguiente:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | xterm -name xt2 -e sh -c 'cd "%directory"; fichero="%filename"; ejecutable=${CF%.*}; extension=${CF#*.}; extension=`echo $extension | awk '\''{print tolower($0)}'\''`; rm $ejecutable 2> /dev/null; case $extension in "c") gcc -o $ejecutable "%filename" 1>&2 2>/tmp/ktc_err ;; "cpp") g++ -o $ejecutable "%filename" 1>&2 2>/tmp/ktc_err ;; *) echo "Extensión no reconocida" ;; esac ./ejecutable echo -e "\n-----------------"; echo "Compilación: "; cat /tmp/ktc_err; echo -e "--------------------\n"; printf "%s" "Pulsa INTRO para continuar"; read intro' |
Este script básicamente compilará el programa actual ya sea en c o c++ dependiendo de la extensión y lo ejecutará, al mismo tiempo veremos un pequeño informe de la compilación al final de la ejecución.
Ahora, vamos a establecer una tecla rápida para la acción, para ello, guardamos el script que hemos hecho y aceptamos la ventana de opciones. Ahora vamos a Preferencias > Configurar accesos rápidos y buscamos nuestra acción externa Compilar y Ejecutar:
Personalizamos el acceso rápido de acción y establecemos una nueva tecla rápida y aceptamos. En mi caso elegí Control+F9 que tienen algunos IDEs.
Actualización: Si en lugar de Kate eres usuario de gedit, puedes dirigirte a este post.














Últimos comentarios