Archive

Posts Tagged ‘compilar’

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

3 agosto, 2011 7 comments

Arduino Emacs

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:

  1. gcc-avr: Es el compilador
  2. avr-libc: Son las librerías para la plataforma
  3. scons: La herramienta de construcción
  4. avrdude: Utilidad para programar chips avr
  5. 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í:

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

Zabaglione
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

c150rusm_prodpiczm_1Si 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

29 noviembre, 2010 No comments

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

9 septiembre, 2010 4 comments

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.

confkateNos dirigimos a la configuración del programa…

captElegimos herramientas externas y escogemos Nuevo, para crear una nueva herramienta. Ahí encontraremos la siguiente ventana:

capt1Rellenamos 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&gt; /dev/null;
case $extension in
"c") gcc -o $ejecutable "%filename" 1&gt;&amp;2 2&gt;/tmp/ktc_err
;;
"cpp") g++ -o $ejecutable "%filename" 1&gt;&amp;2 2&gt;/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:

capt2Personalizamos 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.

Enseñando a programar

Desde hace tiempo, me ofrezco como profesor particular de programación en C/C++ (entre otros), he conocido bastantes alumnos, y metodologías de varios profesores. Este artículo es una opinión personal de mi experiencia.

En principio tengo que decir que muchos de mis alumnos, sólo venían para sacarse una asignatura, no tenían demasiado interés, aunque fuera una de las asignaturas claves de sus estudios, y algo que les ayudaría el día de mañana; aunque es cierto que no todo el mundo puede aprender a programar desde cero y con soltura en 3 ó 4 meses, requiere un entrenamiento, dedicación, mucho tiempo y enfrentarse con problemas una y otra vez, y si le sumamos a esto poco interés estamos perdidos.

Es cierto que igual que en todas las ciencias e ingenierías tiene un aspecto recursivo y es que lo más sencillo se enlaza con aspectos muy complicados y es difícil introducir a alguien en este mundo sin que queden preguntas sin resolver y huecos que en el futuro se rellenarán, pero algunas insaciables mentes quieren ir más deprisa, y cuando las respuestas a sus preguntas empiezan a sonar a chino, abandonan.

Es por esto, que como profesor hay que ofrecer algún valor adicional al alumno, y aunque no es complicado ofrecer en un grupo reducido, existen muchos centros de enseñanza en los que ni siquiera se intenta; no podemos pretender enseñar C a una persona con Borland C, un IDE del año 1992 (sí, ya es mayor de edad y aunque hace años luz de su salida, se sigue usando). Los alumnos de hoy en día no valoran que hace 16 años flipábamos haciendo un entorno en modo texo, y estábamos acostumbrados a los 80 caracteres x 25 líneas.
Hoy en día ver sólo 80 letras por línea y tan pocas líneas, agobia un poco, eso de no poder cambiar el tipo de letra, el tamaño y elegir entre una gran variedad de colores parece que lo hace todo un tanto inútil, y da la impresion de que nunca se van a utilizar esas cosas.

Soy consciente de que es difícil enseñar a programar prescindiendo del modo texto, ya que la entrada/salida es muy fácil de implementar y de probar, y si estamos empezando es normal que compilemos mil millones de veces sin éxito, y luego tengamos errores de en tiempo de ejecución y ocurran sucesos extraños en nuestros pequeños programas. Por lo que en principio propondría un poco de renovación de software, es cierto que los centros donde se sigue enseñando con Borland C y programas de la época llevan haciéndolo así durante años, pero aunque C va a seguir siendo el mismo, se pueden probar otros IDEs que nos permitan por lo menos hacer el tema un poco más ameno para el alumno. (Dev-C++, Netbeans, Anjuta, o utilizar Kate y compilar a mano, también soy consciente de que éstos también se utilizan en muchos sitios).

Por otra parte, me gustaría hablar sobre la programación amena, es cierto que al principio, tenemos que limitarnos a enseñar las posibles herramientas de las que disponemos, con paciencia, ejemplos, y viendo lo que es capaz de hacer cada herramienta; pero propongo hacer programas libres a corto plazo, es decir, embarcarnos en pequeños proyectos (siempre viendo lo que está dentro de las posibilidades de lo enseñado), con lo que se pueda practicar y seguir aprendiendo. Por ejemplo:

  • Si el alumno está interesado en la economía, se puede hacer un sencillo gestor de economía personal, o un pequeño simulador FOREX (Mercado de divisas) con unos pocos datos generados.
  • Si le gusta la fotografía, podemos empezar (con algunas funciones prediseñadas) a leer y escribir archivos BMP o JPG. Aunque suene complicado símplemente se pueden entregar un .h y un .c y una pequeña documentación de cómo llamar a esas funciones.
  • Está bien el hecho de hacer juegos, pero meternos en un juego directamente es una tarea un tanto complicada, ¿por qué no empezar con las tres en raya? O un juego de sumas y restas, un ahorcado…
  • Está bien empezar también con algo tangible y que todos conocemos, por ejemplo una máquina expendedora, un programa que diga una frase, tipo Facebook, un programa de envío de email (aunque tengamos que entregar algunas funciones extra).

Por esto, siempre empiezo preguntando a mis alumnos por algo que les guste, es verdad que para una clase particular es fácil, un grupo grande es mucho más difícil de llevar, sobre todo si cada uno hace un trabajo diferente, pero se puede guiar por lo que más o menos les interese a todos. Y una cosa importante, que hayamos hecho algo mil veces, no nos quita de currar, y de intentar hacerlo siempre lo mejor que podemos

Propongo unas pistas/consejos, para hacer la programación mucho más emocionante:

  • system() no sólo vale para hacer system(“PAUSE”); para que en Windows no se nos cierre la ventana; podemos ejecutar cualquier cosa, desde un programa que reproduce sonidos, hasta otro para mostrar una imagen, como un navegador web, lo que sea… podemos usarlo para más cosas.
  • Hay librerías fáciles de usar que nos pueden hacer la vida un poco más amena, por ejemplo xosd, con lo que podremos presentar mensajes en pantalla
  • Kdialog, dialog, Xdialog, Zenity… son programas que nos ayudarán a mostrar mensajes en pantalla de forma fácil, usémoslos.
  • Los alumnos se acostumbrarán antes a llamar código de terceros si les proporcionamos las librerías para ello, a lo mejor se ve demasiado complicado, pero obtener un texto de una página web, o que se vea que un mensaje se ha sacado de Google o Twitter, cuando acabas de empezar te anima un poco.
  • No podemos pedir ejercicios muy simples, y de repente un ejercicio extremadamente complicado, que nadie va a ser capaz de resolver, debemos ir progresivamente y pensar que aún no se tiene una mentalidad de desarrollador, tenemos que desarrollarla nosotros, no hacer que abandone.
  • Lo más importante, paciencia, nosotros tenemos años de experiencia y tratamos con personas que no tienen ninguna, y empiezan ahora, tenemos que hacer un seguimiento de lo que están haciendo, aunque es cierto que algún día hay que aprender a buscarse la vida, tenemos que hacerlo poco a poco y escoger el momento.

Casi todo este post, se ha basado en mi experiencia de programación en C/C++, y es verdad que en otros lenguajes (Visual Basic por ejemplo), se realizan otro tipo de ejercicios algo más interactivos y que generalmente gustan más; es cierto que nos deja muchas cosas hechas y no tenemos que recorrer tanto camino para llegar a algo complicado, aunque también es cierto que hay muchos más lenguajes cuya sintaxis se parece más a C que a Basic, por lo que considero uno de los grandes pilares de la programación.

Me gustaría dedicar un par de párrafos al pseudolenguaje o pseudocódigo, es cierto que a muchas personas les ha ayudado en la comprensión del código y el diseño de los algoritmos, así como cuando se quiere decir algo en un idioma diferente al idioma natal, primero lo pensamos en nuestro idioma (más o menos) y luego traducimos en dos o tres iteraciones (primero normal, luego cambiamos verbos de sitio, luego adjetivos…); aunque a la hora de programar, primero tenemos en mente nuestra idea de lo que queremos hacer, y luego diseñamos el algoritmo en pseudolenguaje, tras ello, traducimos línea por línea, en un proceso que puede llegar a durar varios minutos.

Es una expresión en lenguaje natural, que resulta de todo menos natural, y realmente lo veo un tanto pérdida de tiempo; para aprender a programar y a crear algoritmos uno de los procesos fundamentales es compilaro y probarlo hasta que salga, y con el pseudolenguaje no tenemos forma de hacerlo (sí, hay algún compilador, pero prácticamente no se utiliza). ¿Por qué no enseñamos a programar directamente? Así podemos probar lo que hacemos en tiempo real y sobre todo, saber que progresamos. Sí está bien utilizar diagramas de flujo y tratar de entender el algoritmo, hacer esquemas, etc; pero me refiero a intentar aprender una sintaxis nueva que en tres o cuatro meses que suele haber para aprender la materia se puede atragantar y evita que avancemos.

Top