Closures en C y Objective-C

junio 30, 2010

Si, aunque intenteis leer otra vez el t铆tulo del post, vamos a hablar de, las tan de moda, closures para C. 馃檪

Sin duda, dada la naturaleza de C, se hace extra帽o que se a帽adan funcionalidades que normalmente se asocian a lenguajes mas modernos (o a煤n en evoluci贸n) como C#, Java o otros lenguajes de script como Python o Ruby

El nombre elegido para referirnos es “blocks”, y b谩sicamente ha sido un a帽adido de Apple al compilador GCC que usamos en Mac y al futuro CLang que corre sobre el tambi茅n futuro y prometedor LLVM.

Por el momento esta extensi贸n de C no es estandar, y dada la cerrada naturaleza de los est谩ndares de C es complicado que llegue a serlo. Pero, por otro lado, al estar implementado en compiladores como GCC o en el frontend de LLVM Clang, y adem谩s, al ser estos compiladores opensource y portables entre diferentes plataformas, presumiblemente esta extensi贸n se pueda utilizar sin problemas en el futuro, al igual que usamos ciertas extensiones propias de GCC que tampoco forman parte de los estandares de C.

Apple desarroll贸 los blocks como parte de la tecnologia “Grand Central Dispatch”. Esta tecnologia optimiza ciertos bloques de ejecuci贸n, y como podeis esperar, el “empaquetar” los bloques de ejecuci贸n usando esta extensi贸n del lenguaje facilita mucho la gesti贸n por parte del sistema operativo y la programaci贸n por parte de los desarrolladores.

Hasta ahora hemos estado hablando alegremente de closures, b谩sicamente los closures es una forma de encapsular un comportamiento bajo una variable. Usando esta variable podemos manejar el comportamiento (o funci贸n) de la misma manera que manejamos el resto de variables, esto es, por ejemplo, pasarlo como par谩metros a una funci贸n.

Como siempre, lo mejor es verlo con un ejemplo, antes de adentrarnos en C,vamos a ver un ejemplo en Javascript. Lo bueno de este lenguaje es que la sintaxis es muy sencilla y nos va a permitir ver el concepto mas f谩cilmente.

funcion_suma = function(a,b) { return a+b; }
funcion_resta = function(a,b) { return a-b; }

function opera(op1, op2, operacion) {
	return operacion(op1, op2);
}

opera(3, 2, funcion_suma);
  // Imprime 5
opera(3, 2, funcion_resta);
  // Imprime 1
opera(3, 2, function(a,b) {
	return a*b;
});
  // Imprime 6

En el ejemplo creamos dos variables asignadas a funciones, que realizan las diferentes operaciones, y estas variables se las pasamos a la funcion “opera”. Cuando desarrollamos la funcion “opera” no sabemos que operaci贸n vamos a realizar, dejamos esa puerta abierta para que cada cliente de la funci贸n aporte su propia funci贸n de operaci贸n. Como vemos en el 煤ltimo caso, podemos definir la propia funci贸n cuando construimos los par谩metros de la llamada.

La gran ventaja de esta t茅cnica es la extensibilidad, ya que dejamos total libertad para que el desarrollador aporte la funci贸n a la que se va a llamar. Este ejemplo es muy sencillo, pero al final del post veremos puntos de Cocoa donde se estan usando los blocks y veremos de forma mas clara en que son una ventaja.

Centrandonos ya en C, Apple ha escogido el simbolo ^ para marcar un bloque. Es curioso ya que este simbolo se utilizaba en otra extension de C++ por parte de Microsoft para marcar cierto tipo de variables cuando se desarrolla con Managed C++.

Por lo tanto, para definir un bloque en C, usamos el simbolo ^, seguido de la lista de par谩metros entre parentesis y despu茅s el bloque de codigo que conforma el bloque. Para definir el tipo del bloque usamos el tipo de retorno, el nombre del bloque entre parentesis y precedido de ^ y finalmente la lista de par谩metros entre parentesis. Aunque pensandolo bien todo este parrafo me lo podr铆a haber ahorrado, simplemente poniendo un ejemplo 馃槈

int (^funcion_suma)(int, int) = ^(int a, int b) { return a + b; };

Siguiendo la linea anterior, esta ser铆a la forma de declarar una variable de tipo “funcion_suma” al igual que haciamos en javascript. Como vemos, la sintaxis es realmente mas compleja que la de javascript.

int opera(int op1, int op2, int (^funcion_opera)(int, int)) { return funcion_opera(op1, op2); }

opera(3, 2, funcion_suma);
  // Imprime 5
opera(3, 2, ^(int a, int b) {
	return a * b;
})
  // Imprime 6

Y aqu铆 tenemos la anterior versi贸n de la funci贸n “opera” pero en C, como vemos el concepto es similar, pero con una sintaxis mas enrevesada.

Si habeis programado en otros lenguajes como Java, la ultima invocaci贸n os sonara a clases anonimas, y ciertamente se les parece bastante. Al igual que con estas clases los blocks tienen suelen ser un buen sustituto a la hora de crear un callback. Una ventaja con respecto a la forma tradicional de implementar dichos mecanismos, es que pueden acceder a las variables locales del entorno donde se estan ejecutando. Esto es bastante util en numerosas ocasiones, usando blocks nos ahorraremos tener que pasar dichos datos a la funcion u objeto que implemente el callback.

Para marcar que una variable va a ser accedida dentro de un bloque, usamos el modificador __block antes de la variable que va a ser accedida dentro del bloque. Como por ejemplo:


__block int multiplicador = 6;

opera(3, 2, ^(int a, int b) {
	return a * b * multiplicador;
})
  // Imprime 36;

A partir de iOS 4 en el iPhone y OSX 10.6 los bloques son una realidad en Cocoa y Cocoa Touch. Poco a poco van apareciendo m茅todos que soportan un bloque para indicar el comportamiento de una acci贸n.

Un ejemplo, sacado de la documentaci贸n de apple es m茅todo sortedArrayUsingComparator: del NSArray. El uso tradicional de este funcionalidad seria pasandole un puntero a funci贸n que resuelva la comparaci贸n, como podemos ver en sortedArrayUsingFunction:context:. Como hemos visto anteriormente, dada a la limitaci贸n de este m茅todo, Cocoa nos proporciona un puntero a nuestros datos, reflejado en el par谩metro context.

Si usamos bloques en este caso, nos saltamos esta limitaci贸n, y adem谩s, facilita la lectura del c贸digo ya que podemos poner la l贸gica del comparador en la misma llamada de la funci贸n.

Para finalizar el post, vamos a ver un ejemplo de una ordenaci贸n de un Array usando bloques y sin usarlos

Usando el m茅todo tradicional, la ordenaci贸n del array quedar铆a algo como:

NSInteger alphaComparator(id arg1, id arg2, void* arg3) {
  NSString *a = (NSString*)arg1;
  NSString *b = (NSString*)arg2;
  return strcmp([a cStringUsingEncoding:[NSString defaultCStringEncoding]],
				[b cStringUsingEncoding:[NSString defaultCStringEncoding]]);
}

int main (int argc, const char * argv[]) {
  NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

  NSArray *stringsArray = [NSArray arrayWithObjects:
    @"Roberto",
	@"Alfonso",
	@"Ruben",
	@"Mariano",
    @"Juan",
	@"Antonio",nil];

  NSArray* sortedArray = [stringsArray
			sortedArrayUsingFunction:alphaComparator context:nil];
  for ( NSString* cad in sortedArray ) {
    NSLog(@"%@\n", cad);
  }

  return 0;
}

Por otro lado, usando bloques quedar铆a algo como:

iint main (int argc, const char * argv[]) {

  NSArray *stringsArray = [NSArray arrayWithObjects:
   @"Roberto",
   @"Alfonso",
   @"Ruben",
   @"Mariano",
   @"Juan",
   @"Antonio",nil];

  NSArray* sortedArray = [stringsArray sortedArrayUsingComparator:^(id str1, id str2) {
    NSString *a = (NSString*)str1;
	NSString *b = (NSString*)str2;
	return (NSComparisonResult)
			strcmp([a cStringUsingEncoding:[NSString defaultCStringEncoding]],
				  [b cStringUsingEncoding:[NSString defaultCStringEncoding]]);
  }];

  for ( NSString* cad in sortedArray ) {
	NSLog(@"%@\n", cad);
  }

  return 0;
}

Sin duda, bajo mi punto de vista la soluci贸n usando bloques es mucho mas elegante, pese a que el enfrentamiento inicial con los blocks pueda ser algo “dura” 馃榾

Como conclusi贸n, simplemente decir que es muy grato ver como se van a帽adiendo funcionalidades a nuestro lenguaje favorito y, de esta manera, adaptandolo un poco mas a los tiempos en los que vivimos 馃檪

Anuncios

Los peligros de devolver punteros

noviembre 3, 2009

 

Cuando escoges un lenguaje que deja en tus manos la gesti贸n de memoria tienes que tener mucho cuidado con la manera en que gestionas los recursos que est谩n a tu disposici贸n. Si bien es cierto que puedes tener mucha flexibilidad a la hora de manejar la forma en que pasas los datos a tus funciones o m茅todos, esa flexibilidad se convierte en responsabilidad a la hora de liberar de forma correcta lo que en alg煤n momento reclamas.
Por norma, siempre es buena idea que alg煤n punto de nuestro programa se haga responsable de la memoria que el mismo pide, de esta manera sabr谩 como y cuando liberarlo. Por ejemplo, si una funci贸n necesita pasar un espacio de memoria a otra funci贸n para que escriba en ella, la mejor idea es que la funci贸n cree el espacio y se lo pase a la funci贸n de lectura ya creado, de tal maneras que cuando lo use, sea capaz de eliminarlo sin problemas.
void f1()
{
  ...
  char* buffer = new char[100];
  f2(buffer);
  ...
  delete [] buffer;
  ...
}
Uno de los dise帽os que pueden inducir a problemas es el hecho de que una funci贸n devuelva un puntero a una memoria de la que dicha funci贸n pierde la responsabilidad. Por ejemplo
char* f2()
{
  char* buffer = new char[100];
  ...
  return buffer;
}
Este dise帽o, puede ocasionar que la memoria de buffer nunca sea liberada, por ejemplo, si usamos la llamada como parametro de otra funci贸n, esa memoria nunca se liberar谩, o bien por que directamente se nos olvide liberar la memoria que no hemos reservado en la funci贸n original, por ejemplo:
void f1()
{
  int len = strlen(f2()); // la memoria de f2 no es accesible
}
En este ejemplo vemos como la memoria que f2() ha reservado, nunca se libera, pues no hay ninguna variable que la referencie y que podamos usar para liberarla.

 


驴Qu茅 es la metaprogramaci贸n?

octubre 12, 2009
En los 煤ltimos meses he estado cada vez mas ligado a C++, realmente no me pilla de nuevas pero si es cierto que en los ultimos tiempos mi produccion de codigo ha sido casi enteramente en Java.
La vuelta a C++ no me ha sentado nada mal, de hecho, he agradecido el dejar de hacer las mismas t铆picas aplicaciones de “Altas/Bajas/Modificaciones/Consultas”
En el refresco de C++, pude tener acceso a un libro que me prest贸 un compa帽ero de trabajo. El libro en cuesti贸n es “Modern C++ Design: Generic Programming and Design Patterns Applied” (http://www.amazon.com/Modern-Design-Generic-Programming-Patterns/dp/0201704315) de Alexei Alexandrescu. De momento solo he leido algunas paginas pero me ha servido para descubrir un mundo que hasta ahora no hab铆a explorado, la metaprogramaci贸n.
Es dif铆cil simplificar algo como la metaprogramaci贸n en una sola frase, en una primera impresi贸n, y como su propia palabra indica, es la programaci贸n sobre la programaci贸n, o tambi茅n se puede ver como programas que construyen programas. Uno de los principales objetivos de esta t茅cnica es llevar a tiempo de compilaci贸n c谩lculos que habitualmente se hacen en tiempo de ejecuci贸n.
Un claro ejemplo podr铆a ser la construcci贸n de una tabla de resultados, por ejemplo, si en nuestro programa necesitamos hacer una operaci贸n costosa, digamos una funci贸n matem谩tica que exija mucho tiempo de c谩lculo, en lugar de llamar a dicha funci贸n en tiempo de ejecuci贸n, crearemos un c贸digo cuya salida sea los valores de esa funci贸n evaluada para diferentes valores y que sea usable desde nuestro programa, por ejemplo como la inicializaci贸n de un array con dichos valores.
== Ejemplo cl谩sico ==
double funcion_costosa (int valor)
{
/* … */
}
cout << funcion_costosa(3);
== Ejemplo con t茅cnicas de metaprogramaci贸n ==
double[] valores = {1.7862, 1.7652, 1.7890, 1.7675,….}; // C贸digo generado con metaprogramming
cout << valores[3];
Como se puede ver en el ejemplo, si la optimizaci贸n es nuestro objetivo, cuanto vamos a ganar usando t茅cnicas de metaprogramacion en nuestros programas.
Ahora la pregunta es…驴y como puedo hacer eso? Realmente hay muchas t茅cnicas para conseguir generar c贸digo, depende de la plataforma donde nos encontremos. La mayor铆a de estas t茅cnicas recaen en preprocesadores o en los propios compiladores, concretamente en C++ una de las maneras de conseguirlo es usando plantillas.
Si algo me ha descubierto el libro que mencionaba al principio de la entrada es principalmente la flexibilidad y la potencia que tienen los “templates” en C++.
Un ejemplo habitual de metaprogramaci贸n en C++ usando plantillas suele ser el calculo de una serie de n煤meros, como por ejemplo el factorial, la serie de fibonacci o como vemos en el siguiente ejemplo, el desarrollo de la serie para calcular el valor de la funci贸n trigonometrica “seno”

En los 煤ltimos meses he estado cada vez mas ligado a C++, realmente no me pilla de nuevas pero si es cierto que en los ultimos tiempos mi produccion de codigo ha sido casi enteramente en Java.

La vuelta a C++ no me ha sentado nada mal, de hecho, he agradecido el dejar de hacer las mismas t铆picas aplicaciones de “Altas/Bajas/Modificaciones/Consultas”

En el refresco de C++, pude tener acceso a un libro que me prest贸 un compa帽ero de trabajo. El libro en cuesti贸n es “Modern C++ Design: Generic Programming and Design Patterns Applied” de Alexei Alexandrescu. De momento solo he leido algunas paginas pero me ha servido para descubrir un mundo que hasta ahora no hab铆a explorado, la metaprogramaci贸n.

Es dif铆cil simplificar algo como la metaprogramaci贸n en una sola frase, en una primera impresi贸n, y como su propia palabra indica, es la programaci贸n sobre la programaci贸n, o tambi茅n se puede ver como programas que construyen programas. Uno de los principales objetivos de esta t茅cnica es llevar a tiempo de compilaci贸n c谩lculos que habitualmente se hacen en tiempo de ejecuci贸n.

Un claro ejemplo podr铆a ser la construcci贸n de una tabla de resultados, por ejemplo, si en nuestro programa necesitamos hacer una operaci贸n costosa, digamos una funci贸n matem谩tica que exija mucho tiempo de c谩lculo, en lugar de llamar a dicha funci贸n en tiempo de ejecuci贸n, crearemos un c贸digo cuya salida sea los valores de esa funci贸n evaluada para diferentes valores y que sea usable desde nuestro programa, por ejemplo como la inicializaci贸n de un array con dichos valores.

== Ejemplo cl谩sico ==

double funcion_costosa (int valor)
{
   /* ... */
}
cout << funcion_costosa(3);

== Ejemplo con t茅cnicas de metaprogramaci贸n ==

double[] valores = {1.7862, 1.7652, 1.7890, 1.7675,....}; // C贸digo generado con metaprogramming
cout << valores[3];

Como se puede ver en el ejemplo, si la optimizaci贸n es nuestro objetivo, cuanto vamos a ganar usando t茅cnicas de metaprogramacion en nuestros programas.

Ahora la pregunta es…驴y como puedo hacer eso? Realmente hay muchas t茅cnicas para conseguir generar c贸digo, depende de la plataforma donde nos encontremos. La mayor铆a de estas t茅cnicas recaen en preprocesadores o en los propios compiladores, concretamente en C++ una de las maneras de conseguirlo es usando plantillas, y bas谩ndonos en t茅cnicas como la especializaci贸n parcial y la recursividad.

Si algo me ha descubierto el libro que mencionaba al principio de la entrada es principalmente la flexibilidad y la potencia que tienen los “templates” en C++.

Un ejemplo habitual de metaprogramaci贸n en C++ usando plantillas suele ser el calculo de una serie de n煤meros, como por ejemplo el factorial, la serie de fibonacci o como vemos en el siguiente ejemplo, el sumatorio de los N valores:

template <int N>
struct Sumatorio
{
enum { result = N + Sumatorio<N-1>::result };
};
template <>
struct Sumatorio<0>
{
enum { result = 0 };
};
int sumatorio(int n) {
 return (n==0)? 0 : (n + sumatorio(n-1));
}
...
cout << Sumatorio<10>::result
...

Al compilar este ejemplo, el compilador se encargar谩 de realizar la sustituci贸n recursiva, de tal manera que en el c贸digo compilado la expresion Sumatorio<10>::result sera sustituido por “55”. Como vemos, la diferencia con realizar la llamada a la funci贸n sumatorio() es que si usamos templates, en tiempo de ejecuci贸n no se utilizar谩 ni un ciclo de computaci贸n en calcular dicho sumatorio.

Si bien es cierto que mecanismos como estos tienen algunos inconvenientes como que por ejemplo hacen el c贸digo mas complicado de entender, desde el punto de vista del programador entra帽an un bonito reto a acometer, cuyo principal objetivo puede ser la optimizaci贸n. En mi opini贸n estos mecanismos a帽aden un poco de “picante” a la, a veces, mon贸tona labor de programaci贸n.


Los inconvenientes de mezclar dos mundos

septiembre 7, 2009


En Muchas ocasiones cuando vemos las letras C++ o C, realmente no nos damos cuento de cuanto diferentes son ambos mundos. El facil acceso a C desde C++ sin duda es una gran ventaja ya que por una parte nos permite acceder a la gran funcionalidad que C ha adquirido en todos estos a帽os y por otro lado nos permite utilizar el gran n煤mero de librer铆as que existen para el lenguaje de programaci贸n m谩s com煤n.

En el gran y “tedioso” libro sobre C++ de Bjarne Stroustrup, podemos encontrar las razones de porque cuando pensaba en crear un nuevo lenguaje de programaci贸n, pens贸 en C. Sus principales razones fueron: “C puede compilarse y ejecutarse en casi cualquier dispositivo electr贸nico”.

Sin embargo como vamos a ver en este post, C y C++ realmente son mundos muy diferentes.

Esto viene a cuento por un problema con el que me he topado recientemente, y que me ha dado alg煤n que otro quebradero de cabeza.

Supongamos que tenemos una librer铆a dise帽ada para ser usada en C. Dicha librer铆a permite hacer llamadas as铆ncronas, de tal manera que a la funci贸n que realiza el proceso debemos pasarle un puntero a funci贸n con la direcci贸n a la que llamara cuando la accion se termine, lo que normalmente se conoce como callback. Pongamos como ejemplo, una funcion de lectura de datos.

lib_read(int handle, int waitTime, int (*read_callback)(void *buffer, int sizeRead) );

El prototipo de la funci贸n de callback ser铆a algo como:

int my_callback(void *buffer, int size)
{
/* CODIGO */
}

El funcionamiento normal ser铆a realizar la llamada de la siguiente manera:

read(my_handle, 1000 /*ms*/, my_callback);

Hasta aqu铆, todo correcto. Cuando el proceso haya le铆do los datos, se llamara a la funci贸n my_callback con los datos listos para ser procesados.

Os preguntareis, 驴Cual es el problema?
Como dec铆a anteriormente, vamos a introducir a C++ en la ecuaci贸n.
Digamos que tenemos una clase que se encarga de realizar las lecturas de esos datos que hablabamos anteriormente. El ejemplo ser脤a algo como:

class ReadHandler
{
public:
ReadHandler();
vector ProcessData();
private:
char* readData;
int read();
/*Teorica funcion de callback para la libreria de lectura */
int my_callback(void* buffer, int size);

}

El objetivo de esta clase es leer los datos y procesarlos, de tal manera que cuando llamamos a ProcessData nos devolver谩 un array con los datos ya procesados. Gracias al dise帽o orientado a Objectos de C++, los datos est谩n encapsulados en su clase. Y si creamos varias instancias de ReadHandler, los datos no se compartir谩n.

Como hemos visto que la librer铆a anterior se ajusta a nuestras necesidades, por lo que nos planteamos usarla para nuestro programa.
Para ello pensamos que el constructor de la clase puede ser un buen lugar para realizar la llamada asincrona, asi cuando nos llamen a ProcessData, si el callback ya ha rellenado los datos contenidos en readData los podamos procesar y devolver su contenido.

Con todo organizado en nuestra cabeza nos ponemos manos a la obra. A帽adiremos la funci贸n de callback a la clase, y en el constructor la llamaremos

lib_read(handle, 1000, my_callback);

Compilamos y…. error. Todo parece correcto y aun as铆 falla, 驴por qu茅?. Bienvenidos al mundo de C 馃檪

En la funci贸n le estamos pasando un puntero a funci贸n, y en C++, no es posible pasar de esa manera un puntero a una funci贸n miembro de la clase, como my_callback es, por lo que este ser谩 nuestro primer error, seguramente el mas sencillo de solucionar, ya que es el compilador quien se queja.

En C++, para crear un puntero a funci贸n miembro se debe usar el operando & precediendo al nombre, a煤n as铆, el compilador vuelve a fallar, ya que no es posible pasar un puntero a una funci贸n miembro no est谩tica.

Esto nos deja un poco fuera de juego, ya que la librer铆a era perfecta para nuestras necesidades y oye, al fin y al cabo C++ puede usar c贸digo C. El problema real es que la convenci贸n de llamadas a funciones en C y en C++ es diferente, en C++ aunque no se vea a ojos del programador, el primer par谩metro de todas las funciones es un puntero a la instancia sobre la cual llamamos la funci贸n, lo que se conoce como la convenci贸n thiscall.

La siguiente aproximaci贸n puede pasar por abandonar temporalmente C++ y ya que la funci贸n necesita como callback, una funci贸n est谩tica o una funci贸n que no sea de C++, pues podr铆amos usar una funci贸n de C.

El problema de esta aproximaci贸n es volver al “mundo” de C++, si el callback seria una funci贸n fuera de cualquier clase, no tendr铆amos una referencia a la instancia que llamo originalmente a la funci贸n que realiza la lectura as铆ncrona.

Llegados a este punto, hemos visto la problem谩tica que tiene la situaci贸n, y de hecho este problema no tiene una soluci贸n elegante, pues se necesita que la librer铆a proporcione un medio para pasar la instancia que le esta llamando.

Una soluci贸n bastante poco elegante pero que puede salvar alguna situaci贸n limite es usar una variable global para almacenar la instancia que llama a la funci贸n, y as铆 poder hacer la llamada de vuelta.
En c贸digo ser铆a algo como:

ReadHandler* global;
ReadHandler::ReadHandler()
{
global = this;
lib_read(…., callback_c);
}

int callback_c(…)
{
this->my_callback(…);
}

Sin duda, esta soluci贸n esta plagada de problemas, por ejemplo, 驴Qu茅 pasar铆a si varios hilos crean instancias de ReadHandler?, pues como podemos predecir, habr铆a un conflicto ya que la variable global no tendr铆a un valor valido en cada llamada.

Dado a este problema y ya que es habitual utilizar librer铆as de C en C++, el dise帽o de este tipo de llamadas de forma correcta es cr铆tico, y la mayor铆a de ellas habilita una variable donde se puede enviar la instancia que realiza la llamada. Por ejemplo, en la API de Windows nos encontramos con funciones con este prototipo

HANDLE WINAPI CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId );

Esta funci贸n crea un Hilo, llamando a la funci贸n en el parametro lpStartAddress, y pasandole como parametro el que le indicamos en lpParameter. El protipo del callback es:

DWORD WINAPI ThreadProc(LPVOID lpParameter );

Usando esta convenci贸n, en nuestro ejemplo:

ReadHandler::ReadHandler()
{
CreateThread(….,callback_compatible, (LPVOID) this, …. );
}

DWORD WINAPI callback_compatible(LPVOID lpParameter)
{
ReadHandler* rh = (ReadHandler) lpParameter;
this->my_callback(……);
}

Muchas librer铆as ya tienen esto en cuenta, y si estas desarrollando una librer铆a deberias tenerlo en cuenta, ya que no es ningun esfuerzo extra y ganaras la compatibilidad con otros lenguajes como c++.