lunes, 12 de septiembre de 2011

decltype: ¿De qué tipo es esa expresión?

En mi anterior post sobre C++11 vimos que el uso de auto permite deducir el tipo de una variable, siempre que ésta se vaya a iniciar en el momento en que se define. Esto cubre un cierto número de casos, pero no todos.

hora obten_hora_actual() ;
coordenada obten_posicion_actual() ;

std::map<hora , coordenada> m;
m[obten_hora_actual()] = obten_posicion_actual();
//...

Bien. Parece que no habrá problemas. Bueno, realmente no habrá problemas mientras no se modifique el tipo de retorno de las funciones obten_hora_actual() y obten_fecha_actual. Lo que realmente me gustaría expresar es que m es un mapa que usa como tipo para la clave el tipo de retorno de la primera función y como tipo para el valor el tipo de retorno de la segunda función.

Aquí aparece en nuestra ayuda el operador decltype. Este operador, permite obtener el tipo de una expresión y se puede usar en cualquier contexto en el que se pueda usar un tipo.

hora obten_hora_actual();
coordenada obten_posicion_actual();

std::map<decltype(obten_hora_actual()),
 decltype(obten_posicion_actual())> m;
m[obten_hora_actual()] = obten_posicion_actual();
//...

Originalmente, algunos fabricantes habían implementado una extensión con un operador parecido: typeof. Sin embargo, la semántica de este operador era distinta, y el comité de ISO C++ decidió optar por una palabra reservada distinta. La elección de decltype puede parecerte poco afortunada, pero era la opción que menos afectaba al código ya existente.

Reglas para la evaluación de decltype

La expresión decltype(expr) se puede utilizar en cualquier tipo donde se pueda usar un especificador de tipo. De esta manera, se puede escribir el siguiente código:

int x;
long y;
decltype(x+y) z; // z es long

En este caso la expresión decltype(x+z) equivale al tipo long, puesto que el resultado de sumar un int y un long es un long.

En el caso de que la expresión pasada a decltype sea una variable, la regla es ligeramente distinta y el resultado es el tipo con el que se declaró la variable:

int x;
int & rx = x;
decltype(x) y = x; // int y
decltype(rx) ry = x; // int & y

Esta regla, hace que en este caso la deducción de tipos no funcione exactamente igual con auto que con decltype:

int x;
int & rx = x;
auto y1 = rx; // int y1. y1 es una copia de x
decltype(rx) y2 = rx; // int & y2 = rx. y2 es una referencia a x

Esto también es aplicable a los parámetros de una función:


template <typename T> class X { /*...*/};
void f(int x1, int & x2, const int & x3) {
  X<decltype(x1)> z1; // X<int> z1;
  X<decltype(x2)> z2; // X<int&> z2;
  X<decltype font="" int&>="" x>
  /*...*/
}


También se puede utilizar decltype sobre una invocación a una llamada a función. Es importante tener en cuenta que una invocación a función dentro de decltype no realiza una llamada a la función. Su único objetivo es determinar el tipo de retorno de la función.

string obten_valor(const string & clave, int indice);
void imprime() {
  list<decltype(obten_valor("usuario",0))> l;
  for (int i=0;i
    l.push_back(obten_valor("usuario",i));
  }
}

Una diferencia bastante relevante ocurre en el caso de que decltype se aplique a una variable que se encuentre entre paréntesis. En este caso el tipo determinado por decltype es siempre una referencia.

int x;
decltype((x)) y = x; // int & y = x

De forma general, esto ocurre con cualquier expresión que no sea exactamente un nombre de variable y que pueda actuar como un l-valor.

template <class C>
void f(C & c) {
  decltype(c[0]) t; // Error t es referencia sin iniciador
  // ...
}
//...
vector<string> v = { "uno", "dos", "res" };
f(v);

En este caso, el tipo de t se obtiene evaluando la expresión decltype(v.operator[](int)) que es un l-valor (en este caso una referencia a string). Por tanto el tipo de t acaba siendo string& y la primera línea de la función f() genera un error de compilación porque se estaría declarando una variable de tipo referencia sin darle un valor inicial.

Ahora bien, la mayoría de los ejemplos empleados hasta ahora (aunque no todos) pueden parecer artificiosos y poco útiles. Probablemente, sea cierto. Sin embargo, hay contextos en los que declttype manifiesta su verdadera utilidad como la deducción automática del tipo de retorno de una función o la especificación de excepciones mediante la nueva palabra reservada noexcept.

lunes, 5 de septiembre de 2011

Una nueva vida para auto en C++11


Introducción

En C++11, la palabra reservada auto ha resucitado con un nuevo significado. C++ heredó esta palabra reservada de C, donde originalmente implicaba almacenamiento automático, como contraposición al almacenamiento estático. Sin embargo, al convertirse el almacenamiento automático en el almacenamiento por defecto de las variables, se hizo innecesaria su utilización.

Cuando un lenguaje evoluciona (como es el caso de C++11) se debe poner especial atención en que las modificaciones al lenguaje no afecten al código ya existente. Esto hace que los diseñadores del lenguaje sean muy reticentes a la introducción de nuevas palabras reservadas. Por esta razón, el comité de normalización tomó la decisión de resucitar auto dotándola de nuevas semánticas.

En C++11 se puede dar dos usos a auto:

  • Para indicar la deducción automática de tipos en la declaración de una variable.
  • Para indicar la deducción automática de tipo de retorno en la declaración de una función.


Cada uno de estos usos presenta ventajas en la escritura de código. Hoy presentaré algunos ejemplos del primer caso y dejaré para un próximo post el caso de las funciones con deducción automática de tipo de retorno.

Deducción automática del tipo de variables

Cualquiera que haya escrito código usando la biblioteca estándar de C++ habrá visto alguna vez cosas como la siguiente:

vector<int> v = {1, 2, 3};
for (vector<int>::iterator i=v.begin(), e=v.end();i!=e;++i) {
    cout << *i << endl;
}

Claro que esto puede empeorar:

vector<list<string> > v = { { "Carlos", "Maria"}, {"Niño", "Niña"} };
for (vector<list<string> >::iterator i=v.begin(),e=v.end();i!=e;++i) {
for (list<string>::iterator j=i->begin(), e=i->end(); j!=e; ++j) {
cout << *j << " ";
}
cout << endl;
}

Para complicar las cosas un poco más, todos los contenedores ofrecen variantes de iteradores (como const_iterator) que ocasionan algunos pequeños dolores de cabeza al escribir código.

Sin embargo en los ejemplos anteriores, tener que escribir los tipos de las variables i y j. No es realmente necesario. Se puede simplificar el lenguaje siguiendo una máxima que a mí me gusta mucho: “Deja que el compilador haga todo lo que puede hacer y deja el resto para el programador”.

Antes de entrar en los detalles del uso de auto, veamos los ejemplos anteriores en C++11. Baste por ahora decir que cuando se especifica que el tipo de una variable es auto, el compilador determina su tipo a partir del valor que se usa para iniciar la variable.

Con esto nuestro primer ejemplo queda:

  vector<int> v = {1, 2, 3};
  for (auto i=v.begin(), e=v.end();i!=e;++i) {
    cout << *i << endl;
  }

Y el segundo ejemplo:

  vector<list<string> > v = { { "Carlos", "Maria"}, {"Niño", "Niña"} };
  for (auto i=v.begin(),e=v.end();i!=e;++i) {
    for (auto j=i->begin(), e=i->end(); j!=e; ++j) {
      cout << *j << " ";
    }
    cout << endl;
  }

Deducción de tipos en contextos de declaración de variable

El principal uso de la deducción automática de variables es la declaración de variables.
Probablemente el uso más simple de auto es la declaración de una variable en un bloque (dentro de una función, dentro de un bucle,…) o bien en un alcance de un espacio de nombres.

  auto x = 5; // x es int
  auto z = 2.5;  // z es double
  string s = "Daniel";
  auto lon = s.length(); // el tipo de lon coincide con el tipo de retorno de length

Otros usos equivalentes son la declaración de una variable en una sentencia de iniciación de un bucle for, en una condición de una sentencia de selección (if, switch) o de una sentencia de iteración (while, do, for).

  string s = "Daniel";
  for (auto i=s.length();i>0;--i) {
    cout << s[i-1];
  }
  cout << endl;

Estos usos facilitan la vida del desarrollador permitiendo escribir código más simple. En este casi no es necesario recordar que tipo concreto devuelve la función miembro length(), basta con indicar que la variable i debe tener el mismo tipo.

Algunos pueden ver esta utilización de auto como una simple conveniencia que no mejora la calidad del código. Sin embargo, incluso en estos casos tan sencillos, la deducción automática de tipos aporta ventajas.
Por una parte, permite expresar claramente la intención del desarrollador. Es decir, la variable i debe tener el mismo tipo que el valor devuelto por la función miembro length(). Por otra parte, este estilo permite evitar errores derivados de la conversión automática de tipos. Veamos:

  string s = "Daniel";
  for (short i=s.length();i>0;--i) {
    cout << s[i-1];
  }
  cout << endl;

¿Qué ocurre si el valor devuelto por length() no cabe en un short? Ciertamente, es una situación que puede calificarse como mínimo de desagradable.

Pero cuando auto se vuelve realmente útil es en la escritura de código genérico. En C++03, se hacía necesario recurrir a código innecesariamente largo para escribir una función que imprimiese los elementos de un contenedor.

template <typename C>
void imprime(const C & c) {
  for (typename C::const_iterator i=c.begin(), e=c.end();i!=e;++i) {
    cout << *i << endl;
  }
}

Con C++11 uno no se tiene que volver a preguntar si el tipo concreto del iterador tiene que ser iterator o const_iterator y tampoco hace falta cualificar el tipo con typename para indicar al compilador de que realmente se trata de un tipo dependiente.

template <typename C>
void imprime(const C & c) {
  for (auto i=c.begin(), e=c.end();i!=e;++i) {
    cout << *i << endl;
  }
}

Más sobre la deducción de tipos

Una pregunta que conviene hacerse sobre la deducción de tipos es qué ocurre con las referencias. Es decir:

int x = 3;
int & z = x;
auto t = z;  // ¿int o int&?

Es decir, si una variable declarada como auto si inicia con otra variable de tipo referencia ¿qué tipo se deduce? La respuesta se obtiene, una vez que se observa que lo que se utiliza como iniciador (en nuestro caso z) expresión y por tanto su tipo es int.

¿Y si se desea que t sea una referencia? La solución es simple, puesto que se puede combinar auto con cualquier otro especificador de declaraciones:

int x = 3;
int & z = x;
auto&  t = z;  // int&
const auto u = x; // const int
auto *p = &x;

Otros usos de la deducción automática de tipos

Probablemente, un caso más sorprendente (aunque no debería) es la deducción automática de tipos en expresiones asociadas al operador new.

auto p = new auto(1.5); // p es un double*

Además, se puede usar la deducción automática de tipos con variables miembro estáticas que se inician dentro de la definición de una clase.

class X {
public:
  static const auto n = 3;
};

En resumen

C++11 introduce un mecanismo que permite que el compilador pueda deducir el tipo de una variable a partir de la expresión con la que ésta se inicia. Este mecanismo simplifica la escritura de código genérico. Como complemento, el mecanismo permite que evitar errores comunes de programación derivados de conversiones implícitas no deseadas.

Próximamente comentaré dos características del lenguaje que están íntimamente relacionadas con esta: la deducción automática de tipos de retorno en funciones y la obtención del tipo de un objeto.

viernes, 2 de septiembre de 2011

Nuevas bibliotecas estándar para C++

Cuando digieras las 1400 páginas que tiene el nuevo estándar de C++, comprobarás que una parte bastante importante de la norma la constituye una renovada biblioteca de clases. No obstante, es probable que todavía pienses que faltan cosas.

Bien, las buenas noticias son que se abre la puerta para nuevas propuestas de bibliotecas. El comité ha decidido animar a que se presenten propuestas para una futura extensión de la biblioteca estándar.

Esta es la resolución del comité:

The C++ committee Library Working Group welcomes proposals for library extensions which will be considered starting in the February 2012 meeting. We have not yet set out an overall timeline for future library extensions, but are ready to consider new proposals at this point.
To increase the chances of your proposal being accepted by the committee, we strongly recommend that a committee member willing to champion your proposal (this could be you yourself, or a delegate) attend upcoming meetings to help shepherd your proposal through the process.
La preparación de una propuesta de biblioteca para normalizar debe considerar múltiples aspectos que van desde la utilidad general del componente hasta la implementabilidad en las diversas plataformas usadas y la capacidad de ser expresadas de forma portable.

Como comentaba en mi anterior post, ya tenemos diversas propuestas encima de la mesa, pero si tienes una propuesta interesante este es el momento.

viernes, 19 de agosto de 2011

Estándar C++. Reunión de Bloomington


Hoy hemos terminado la reunión del JTC1/SC22/WG21 (comité de C++ para los amigos). La reunión comenzó con la noticia de que el nuevo estándar ha sido oficialmente aprobado de forma unánime por los países con derecho a voto, como adelantaba en mi anterior post. De hecho, hemos tenido confirmación de ISO de que se van a acelerar los trámites de la publicación oficial del documento, por lo que con casi toda seguridad podemos hablar de C++11 (ISO/IEC 14882:2011) y no de C++ 2012.

Una buena parte de la reunión ha estado dedicada a la resolución de issues tanto de la biblioteca como del propio lenguaje. Si. El estándar se acaba de publicar, pero el comité es consciente de que el documento tiene algunos errores e inconsistencias menores que habrá que resolver. En la resolución de estos defectos se ha avanzado bastante, pero no se ha votado ninguno de ellos. Esto hace prever que la próxima reunión en Marzo de 2012 tendremos una buena lista que aprobar.

Además de esto, dentro del grupo de trabajo de biblioteca hemos visto versiones preliminares de algunas propuestas que podrían añadirse:

  • Sistema de ficheros. Es biblioteca te permitirá olvidarte del API C/POSIX para navegar por directorios, entro otras cosas.
  • Cerrojos compartidos. Básicamente son algunos tipos más de mutex, que no se añadieron al estándar para no retrasarlo más. Estos cerrojos (con nombres tentativos de shared_mutex y upgrade_mutex) son especialmente apropiados para soportar problemas del tipo múltiples lectores/único escritor.
  • Nuevos algoritmos para la generación de distintos tipos de permutaciones y combinaciones que generalizan el existente next_permutation.
  • Entrada salida para tipos que representan duraciones de tiempo (espacio de nombres chrono). De esta manera se podrán imprimir mensajes que en el caso de duraciones se incluya de forma automática la unidad en la que se expresa la duración.
  • Un nuevo tipo para representar fechas (también  a incluir en el espacio de nombres chrono).
Otro aspecto bastante relevante ha sido la discusión sobre el futuro del lenguaje. Aunque el comité no ha cerrado decisiones al respecto, parece que los más probable será que se trabaje por una parte un modificaciones al lenguaje y por otra parte en la extensión de la biblioteca estándar. Estos trabajos se podrán realizar de forma independiente de forma que se hagan públicos con ritmos de trabajo diferentes.

En el caso de la biblioteca, el comité hará publica una petición de propuestas de nuevas bibliotecas en breve. Así que si tienes una buena idea para alguna biblioteca que te gustaría ver en el futuro como parte del estándar, este parece un buen momento.

Nuestra próxima reunión será en Kona, Hawaii en febrero de 2012. Probablemente, en esa reunión dediquemos una parte del tiempo a definir la evolución del lenguaje. En cualquier caso y con toda seguridad dedicaremos tiempo a estudiar propuestas de modificaciones y adiciones a la biblioteca (entre mis favoritos estarán propuestas para mejorar la concurrencia y las comunicaciones a través de red).

Y eso es todo por ahora.

viernes, 12 de agosto de 2011

FDIS de C++ aprobado

Por fin ha finalizado el periodo de voto del FDIS del nuevo estándar de C++ (C++0x para los amigos).

El estándar ha sido aprobado con los votos favorables de: Canadá, China, República Checa, Dinamarca, Finlandia, Francia, Alemania, Irlanda, Italia, Japón, Kenya, Corea, Holanda, Nigeria, Noruega, Pakistán, Federación Rusa, España, Suiza, Ucrania, Reino Unido y Estados Unidos.

No ha habido ningún voto en contra.

Ciertamente para la comunidad de C++ es una gran noticia. Después de más de una década por fin tenemos una nueva norma del lenguaje. Sin duda, esto va a revitalizar mucho el lenguaje con nuevas características largamente esperadas y una biblioteca más completa.

Y desde hoy, empezamos a trabajar en el nuevo estándar. Bueno, realmente desde este próximo lunes 15 de agosto que es cuando nos reuniremos en Bloomington, Indiana.

Evidentemente, la norma es perfecta y seguro que encontraremos defectos y posibles mejoras. Si encuentras algo, no dudes en hacérmelo llegar por correo electrónico.

Pero por ahora disfrutemos de la nueva versión del lenguaje...

martes, 12 de julio de 2011

La informática en Educación Secundaria

Esta mañana he leído un interesante artículo en IEEE Computer con el título "Transforming Computer Science Education in High Schools" (http://dx.doi.org/10.1109/MC.2011.191). La verdad es que el debate sobre la necesidad de formación en IT (a.k.a Tecnologías Informáticas) me parece pertinente. Especialmente teniendo en cuenta las previsibles necesidades en profesionales del sector en todas partes.

Hace unos días leía un informe que constata que en Europa las necesidades de profesionales altamente cualificados en Tecnologías Informáticas no son cubiertas ni de lejos por la oferta. Según el mencionado informe, en 2015 Europa tendrá un déficit de entre 384,000 y 669,000 profesionales que las universidades europeas no habrán sido capaces de haber formado. El informe enfatiza que la conclusión es que la industria necesita de forma desesperada profesionales altamente cualificados en IT y Europa necesita que más jóvenes se conviertan en profesionales de IT para satisfacer los futuros incrementos de la demanda"

La situación en USA no es distinta. Las proyecciones a día de hoy son que el incremento de puestos de trabajo en IT es tan fuerte que supera al resto de disciplinas STEM (Science, Technology, Engineering and Mathematics) de forma conjunta. Sin embargo, el porcentaje de estudiantes norteamericanos que siguen un curso de informática en secundaria ha descendido un 8% entre 2007 y 2009.

La pregunta del millón es ¿Qué está pasando? y ¿Es trasladable a la situación de España?

Lo primero que se identifica es que mientras que disciplinas clásicas como la matemática o la física son enseñadas de forma rigurosa y por profesionales altamente cualificados. La situación de la informática, cuando se enseña es radicalmente distinta.

Veamos el diagnóstico en USA y los posibles paralelismos en España.

En 41 estados la informática es una materia accesoria que no está clasificada como las matemáticas o la física y que no tiene impacto en los criterios de graduación de la educación secundaria. Curiosamente esta situación es equivalente a la situación española, donde la informática es una asignatura optativa que muchos estudiantes no escogen porque no pueden examinarse de ella en las pruebas de acceso a la selectividad.

La materia de informática suele incluir el uso básico del computador y la utilización de paquetes básicos de software. En España la asignatura de Tecnología de la Información (optativa de bachillerato) se limite también al uso básico del computador y al uso de paquetes ofimáticos.

Otro problema de la enseñanza preuniversitaria de la informática en España es la cualificación de los docentes. Si bien existen unas oposiciones específicas para ser profesor de matemáticas o de física, esto no es así para informática.

En un momento en el que recurrentemente se habla la necesidad de cambiar el modelo productivo de España se está olvidando la formación en una de las disciplinas que puede ser motor de ese cambio. Para alcanzar ese cambio de formación sería interesante seguir muy de cerca iniciativas de reformulación del currículum de informática, como por ejemplo la iniciativa Computer Science Principles, que está definiendo un nuevo curso orientado tanto a estudiantes con intereses en la Ingeniería Informática como a aquellos que tienen intereses más generales en ciencia, ingeniería y humanidades.

Si hiciésemos con la Física de Bachillerato lo que estamos haciendo con la Informática, deberíamos dejar de enseñar las ecuaciones de Newton y en su lugar enseñar a conducir coches. Esto es lo que estamos haciendo hoy día con la informática.

miércoles, 6 de julio de 2011

Los 6 trabajos con más potencial en Tecnología Informática (IT)

Leo la siguiente noticia en cio.com:

http://www.cio.com/article/684344/The_6_Hottest_New_Jobs_in_IT?source=CIONLE_nlt_careers_2011-06-21

Parece que los 6 trabajos con más potencial en TI (Tecnología Informática):

  • Business Architect.
  • Data Scientist.
  • Social Media Architect.
  • Mobile Tecnology Expert.
  • Enterprise Mobile Developer.
  • Cloud Architect
Interesante.

Por otra parte el 65% de los ejecutivos encuestados dicen que contratará más personal en el segundo semestre de 2011 que en el primero.