miércoles, 21 de julio de 2010

Internacionalización con QT

La librería QT está desarrollada de tal forma que las aplicaciones
que estén hechas usando su API pueden internacionalizarse de forma bastante
fácil.

En primer lugar todas las cadenas de la aplicación deben sustituirse por llamadas
a la función tr.

QObject::tr("cadena");

La cadena que pasemos a la función tr servirá de índice para las cadenas
definitivas en los distintos idiomas.

Después se debe añadir una entrada en el fichero de proyecto (.pro) con los ficheros
que se deben generar para cada idioma de las traducciones, con extensión .ts.

TRANSLATIONS = en.ts es.ts

Los ficheros se generarán al ejecutar el comando:

lupdate proyecto.pro

Estos ficheros .ts se pueden editar con QT Linguist, una vez introducidas todas las
traducciones, se pueden exportar mediante la opción Release, esto generaráa
unos ficheros .qm.

Posteriormente hay que crear un fichero de recursos que contenga los ficheros .qm
de traducciones.

Para obtener el idioma del equipo en el que se está ejecutándo la aplicación se puede
hacer lo siguiente

QString locale = QLocale::system().name();

Para que las traducciones se carguen hay que instanciar la clase QTranslator y pasársela
a la aplicación.

QTranslator translator;
translator.load(locale, ":/");
a.installTranslator(&translator);

La ruta ":/" se refiere al prefijo introducido en el fichero de recursos.

Tras estos pasos las cadenas de la aplicación aparecerán traducidas dependiendo del idioma
del equipo en el que se esté ejecutándo.

jueves, 15 de julio de 2010

Como implementar interfaces en C++

C++ por si solo no trata las interfaces como un concepto diferenciado. Sin embargo se puede trabajar con interfaces tipo Java o C# aprovechándose de la heréncia múltiple creando clases abstractas.

En el siguiente ejemplo las clases ClassA y ClassB implementan la interfaz Interface, para ello tan solo tienen que redefinir el método method.

class Interface{
public:
virtual int method(int param) = 0;
virtual ~Interface(){};
};

class ClassA : public Interface{
public:
int method(int param){return param+1;}
};

class ClassB : public Interface{
public:
int method(int param){return param+2;}
};

Todos los usos que se le dan normalmente a las interfaces siguen siendo válidos con esta aproximación, con una salvedad, si queremos pasar la interfaz como parámetro a una función normalmente se haría como sigue.

std::string function(Interface i){
std::string str;

int number = i.method(1);
if(number==2){str = "ClassA";}
else if(number==3){str = "ClassB";}

return str;
}

Sin embargo, si hacemos esos el compilador protestará con un error similar a: "cannot declare parameter 'i' to be of abstract type 'interface'".

¿Por qué?
Esto ocurre porque por defecto en C++ los parámetros se pasan por valor, el objeto 'i' es imposible de crear ya que es de un tipo abstracto, el compilador no puede instanciarlo.

¿Qué solución hay?
Pasarlo como referencia, con una definición como la siguiente.

std::string function(Interface &i)

Si queremos asegurarnos que el parámetro no se modifica, habría que pasarlo como referencia constante.

std::string function(const Interface &i)

Esto plantea un nuevo problema, generando un error de compilación similar a: "passing 'const Interface' as 'this' argument of 'virtual int Interface::method(int)' discards qualifiers".

No se puede llamar a una función no constante desde un objeto constante. Para solucionarlo los métodos de la interfaz deberían ser constantes, quedando como sigue.

class Interface{
public:
virtual int method(int param) const = 0;
virtual ~Interface(){};
};

class ClassA : public Interface{
public:
int method(int param) const{return param+1;};
};

class ClassB : public Interface{
public:
int method(int param) const{return param+2;};
};

lunes, 5 de julio de 2010

Definición de funciones parciales

La definición de funciones parciales (currificación) es una técnica de la programación funcional poco extendida.

A una función parcial con dos parámetros, se le puede pasar tan solo una variable, obteniendo como resultado de la ejecución otra función con una variable a la que ya se le a aplicado el primer parámetro. Por ejemplo, scala tiene una sintaxis muy simple para llevar a cabo esta técnica.

def function(x:Int)(y:Int) = x+y

val partial = function(3)_

partial(4)

Como resultado de la evaluación parcial (es obligatorio en scala acabarla con '_') se obtiene una función que solo necesita el segundo parámetro para ser evaluada.

Esta técnica puede servir para ahorrar código, modelar inyección de dependencia o reducir interfaces de un conjunto de funciones haciéndolas más sencillas.

miércoles, 30 de junio de 2010

Creación de servidores en javascript

Aunque javascript está diseñado para ejecutarse en el cliente al cargar páginas web, no está limitado a este ámbito.
Existe un proyecto llamado CommonJS que amplia la funcionalidad del lenguaje haciéndole poder interactuar con el sistema como un lenguaje más.

node.js es un framework para la creación de servidores que se apoya en la librería CommonJS.
Está construido completamente siguiendo la filosofía "event-loop" y todas las operaciones I/O se realizan de manera asíncrona para una buen aprovechamiento de la concurrencia del sistema.

Así pues, node.js permite crear servidores optimizados de forma sencilla desde un lenguaje dinámico como javascript, un ejemplo muy simple de un servidor creado con node.js es el siguiente:

var sys = require('sys');
var net = require('net');

var acc = 0;

net.createServer(function (socket) {
socket.setEncoding("utf8");
socket.addListener("connect", function () {
socket.write("Accumulator server\r\n");
socket.write("Accumulator value: " + acc + '\n');
});
socket.addListener("data", function (data) {
var input = parseInt(data);
if(!isNaN(input)){acc += parseInt(data);}
socket.write("Accumulator value: " + acc + '\n');
});
socket.addListener("end", function () {
socket.end();
});
}).listen(8888, "127.0.0.1");

Se puede consultar en mayor profundidad el API oficial.

miércoles, 16 de junio de 2010

Nuevos tipos de formularios en html5

Con el salto a la nueva versión de html se van añadir nuevos tipos de entradas posibles a los formularios.
Por ejemplo, crear un 'slider' para seleccionar un rango se limitará a dar el tipo range a un input.

<input type="range" min="0" max="100" step="2">

Este componente enriquece la interfaz de usuario, hay otros aún más útiles, por ejemplo el siguiente indica que la entrada debe ser de tipo numérico, esto puede ayudar a dispositivos móviles con pantallas reducidas a desplegar tan solo un teclado con números, aprovechando mejor el espacio.

<input required type="number" min="0" step="1" >

También sirve el tipo email para idénticos propósitos. Además de esta forma los navegadores pueden añadir mayor integración (si conocen que el tipo de entrada es email pueden sugerir los de nuestra libreta de direcciones).

Otro tipo de entradas muy comunes son las de fecha, en buscadores de reservas de hoteles, vuelos, etc. La mayoría de webmaster solucionan el problema de mostrar un calendario con javascript.
En html5 se deja este la responsabilidad de representar estos tipos de entradas a los navegadores, habiendo inputs de tipo date, month, week, time...

Para más información.

domingo, 13 de junio de 2010

Hot swapping en erlang

Hot swapping en un entorno con múltiples procesos distribuidos se refiere a la actualización de uno de esos procesos sin parar en ningún momento el sistema.

Si se encuentra algún error en el software, o se necesita añadir nueva funcionalidad, el proceso afectado encola las peticiones nuevas que van llegando, actualiza su código y con el nuevo código sigue atendiendo a las peticiones que tenía encoladas. Los demás procesos del sistema no notan nada de este cambio.

En muchas de las tecnologías usadas hoy en día, todo este proceso es bastante complejo, sin embargo, en erlang es trivial ya que lo soporta de forma nativa el lenguaje.

Supongamos dos procesos, uno que sirve datos.

-module(data).
-export([get/0, version/0]).

get() -> 42.

version() -> 1.

Y otro que los consume.

-module(consumer).
-compile(export_all).

start() -> spawn(fun() -> loop() end).

loop() ->
sleep(),
io:format("Value: ~p at version: ~p~n", [data:get(), data:version()]),
loop().


sleep() ->
receive
after 1000 -> true
end.

El proceso consumidor irá mostrando cada segundo los datos que le manda el otro proceso. En el momento que se necesite cambiar la funcionalidad del proceso creador de datos, simplemente se recompila y el consumidor el único cambio que notará es el valor que tienen los nuevos datos.

1> c(data).
{ok,data}
2> c(consumer).
{ok,consumer}
3> consumer:start().
<0.53.0>
Value: 42 at version: 1
Value: 42 at version: 1
Value: 42 at version: 1
Value: 42 at version: 1
Value: 42 at version: 1
...
c(data).
{ok,data}
Value: 43 at version: 2
Value: 43 at version: 2
Value: 43 at version: 2
Value: 43 at version: 2
...

En entornos críticos o en los que el sistema completo tarda mucho tiempo en arrancar, esto permite actualizar código sin que haya que mantener el servicio parado mientras se actualilza.

jueves, 10 de junio de 2010

Código Thread-Safe vs Código reentrante

Estos dos conceptos son habitualmente confundidos y, aunque están relacionados se corresponden a propiedades distintas.

Una función es thread-safe cuando se le puede llamar desde múltiples hilos concurrentemente, la ejecución terminará correctamente sin que unos hilos interfieran con otros.

Una función reentrante es aquella a la que se le pasa como parámetro todos los valores que usa, no utiliza ningún objeto global (variable global, singleton), no tiene efectos secundarios y no llama a ninguna función no reentrante.

Una función reentrante será siempre thread-safe, si no hace ningún cambio en el estado global de un programa no hay posibilidad que interceda con el código de otra hebra.

¿Y al contrario? Una función thread-safe no tiene porqué ser reentrante, ya que puede modificar el estado global del programa siempre que lo haga ordenadamente mediante exclusión mutua.

Una función reentrante cumplirá además con la transparencia referencial. Transparencia referencial es una propiedad que se cumple cuando, al cambiar una función con sus parámetros por el valor que devuelve, el programa se comporta de la misma forma.

Este tipo de funciones (reentrantes) tienen la ventaja de que se puede seguir su funcionamiento matemáticamente y que admiten memoization como optimización. Un ejemplo aquí.