lunes, 13 de mayo de 2013

C++ 11 y su nuevo estilo, explicado por Bjarne Stroustrup

En una serie de charlas muy interesantes del evento GoingNative2012 organizado por Microsoft, el mísmisimo creador de C++, Bjarne Stroustrup, dio una charla sobre el nuevo estilo de programación introducido por el nuevo estándar C++ 11.

El tema de la charla no son las nuevas funcionalidades del lenguaje, sino cómo usarlas correctamente; eso es lo que Bjarne denomina "estilo". A continuación, un resumen de los puntos esenciales:

  • Type-rich interfaces: Se puede usar sufijos para indicar unidades, por ejemplo (ver la nueva keyword constexpr).
  • Usar RAII para recursos: memoria, archivos, sockets, hardware, recursos del sistema operativo (threads, mutexes, semáforos, etc).
  • Evitar el uso de punteros, especialmente en interfaces (mantener esos usos acotados a implementaciones de funciones). Usar smart pointers como unique_ptr y shared_ptr. No usar este último si no se está compartiendo el recurso. Y antes de considerar usar punteros, considerar usar referencias, o aún mejor, crear los objetos en stack. Todo esto cuando hablamos de usar punteros para expresar la pertenencia de un recurso. Si sólo queremos apuntar a un objeto que no es nuestro y no podemos usar una referencia, usar un puntero es aceptable.
  • Evitar arrays nativos; usar contenedores STL. Si es necesario pasar un array nativo a una API de terceros, por ejemplo, se puede usar un std::vector, que garantiza que toda la memoria sea contigua, igual que en un array nativo. A la función de la API se le pasa &v[0], donde v es el std::vector.
  • Cuando tenemos objetos grandes (por ejemplo, una clase Matrix que usamos para matrices de 10000 x 10000), y queremos hacer una función que devuelva una instancia de esa clase (un operator+, por ejemplo), tenemos el problema de que creamos una matriz local y devolvemos una copia. Es mucho overhead; una nueva funcionalidad clave de C++ 11 son las "move semantics". En vez de copiar, "muevo". En el ejemplo dado, devolvemos una Matrix por valor e implementamos un "move constructor", que define cómo se hace el "move". Más detalles sobre esto en un post futuro. Baste con aclarar que todos los contenedores de la STL implementan esto (excepto std::array), con lo cual la performance sube sólo por compilar para C++11 si ya usábamos contenedores STL.
  • Usar siempre que sea posible.
  • El código de bajo nivel no siempre es más eficiente (por ej: el sort de C es más lento que el qsort de C++). Siempre medir ante la duda y evitar micro-optimizaciones prematuras. Bajo nivel implica escribir más código, lo cual implica mayor posibilidad de bugs y mayor costo de mantenimiento.
  • C++ 11 ofrece clases para manejo de concurrencia: std::thread y std::mutex, entre ellas. Incluso mejor, std::async permite decidir al runtime si usar otro thread o no, dejándole toda la complejidad del manejo de la concurrencia.
  • También se agrega soporte para expresiones lambda.

Por último, acá hay un cuadro con qué funcionalidades de C++ 11 implementa cada compilador (MSVC = Visual Studio)

miércoles, 24 de abril de 2013

Visual Studio 2010 SP1 and C++/CLI strong naming bug

This particular Visual Studio version has a bug which breaks strong naming signing for C++/CLI assemblies. Said bug is documented and has a workaround which requires editing a .Targets file. Details here. In case the link dies, I'll copy the steps below:
  1. Go to %ProgramFiles(x86)%\MSBuild\Microsoft.Cpp\v4.0\Platforms\Win32
  2. Open Microsoft.Cpp.Win32.targets with a text editor (this might require administrator rights)
  3. Search for <Target Name="LinkEmbedManifest"
  4. Inside that tag, in the DelaySign attribute value, change % to $
  5. Do the same for the KeyFile attribute

Visual Studio 2010 SP1 y strong naming para C++/CLI

Este Visual tiene un bug que rompe la firma de assemblies C++/CLI con strong naming. Dicho bug está documentado y tiene una solución que requiere tocar un archivo .Targets del Visual Studio. Los detalles aquí. Por si el link se muere, copio los pasos a seguir:
  1. Ir a %ProgramFiles(x86)%\MSBuild\Microsoft.Cpp\v4.0\Platforms\Win32
  2. Abrir Microsoft.Cpp.Win32.targets con un editor de texto (esto puede requerir derechos de administrador)
  3. Buscar el texto <Target Name="LinkEmbedManifest"
  4. Dentro de ese tag, en el valor de la property DelaySign, cambiar % por $
  5. Lo mismo para la property KeyFile

jueves, 29 de noviembre de 2012

The Horror: Uninstalling SQL Server 2008 R2 and his friends

It's common knowledge that even though Microsoft claims it's possible to have multiple versions of SQL Server Express side by side, that's generally considered a bad idea. So I set out to remove every trace of SQL Server in order to later install the latest R2 version.

After many visits to Control Panel->Add or Remove Programs, in which I removed anything which had SQL Server in its name, four (4) creatures were left standing:

  • Microsoft SQL Server 2008
  • Microsoft SQL Server 2008 Setup Files
  • Microsoft SQL Server 2008 R2
  • Microsoft SQL Server 2008 R2 Setup Files

If I tried to remove the setup files, I was told that I had to remove 2008 or 2008 R2 before. If I tried removing one of those, after running the whooooooole process without a hitch, the entry was still there. On a closer examination, R2 was always saying it was going to delete Microsoft Sync Framework, but it didn't do so (silently). So I tried nuking the sync framework myself, but then I was warned that many programs needed it, including Visual Studio 2010. Since I use that every day, I ran away in terror.

At this point, I realized I should have followed some guide when nuking the various SQL components and helper. But it was too late, so I decided to destroy all traces of SQL Server with a vengeance. This implied deleting the whole C:\Program Files\Microsoft SQL Server directory, and deleting every registry key associated, as explained in the answer to this question on Social MSDN.

But even after all that, there were still three entries on Add or Remove Programs (2008 Common Files, 2008 and some other I don't remember). After another long and fruitless look at the registry, I stumbled upon a Microsoft Fixit that saved my life..

After running it thrice, once per zombie entry, every trace of SQL had seemingly vanished. Then, I ran the SQL Server 2008 R2 installer, and I got several errors. But then I just ran the uninstaller from Add or Remove Programs and tried again, as suggested in this post. And I finally succeeded.

Horror: Desinstalando SQL Server Express 2008 y sus amigos

Es sabido que esto siempre trae problemas. En mi caso, me vi obligado a meterme en este infierno porque no podía hacer restore de una base de datos porque el backp había sido hecho con una versión más nueva de SQL Server 2008 Express R2 SP1 (10.50.sarasa, yo tenía 10.0.sarasa).

Es sabido también que aunque Microsoft dice que es posible tener varias versiones de SQL Server Express side by side, eso está conceptuado como una mala idea. Así que me dispuse a eliminar todo rastro de todo SQL Server para luego instalar la última versión del 2008 R2.

Luego de varias visitas a Panel de Control->Agregar o quitar Programas, durante las cuales fui eliminando todo lo que tuviera SQL Server en su nombre, quedaron cuatro (4) criaturas en pie:

  • Microsoft SQL Server 2008
  • Microsoft SQL Server 2008 Setup Files
  • Microsoft SQL Server 2008 R2
  • Microsoft SQL Server 2008 R2 Setup Files

Si intentaba eliminar los setup files, me decía que debía eliminar 2008 o 2008 R2 primero. Si intentaba eliminar uno de esos, corría tooodo el proceso a la perfección pero la entrada seguía ahí. Mirando más de cerca a R2, cada vez que corría decía que iba a eliminar el Microsoft Sync Framework pero no lo hacía. Cuando fui a borrar ese bicho a mano, Windows me avisó que varios programas lo necesitaban, incluyendo Visual Studio 2010. A ése último lo uso todos los días, así que huí despavorido.

Fue en este punto donde me percaté de que debería haber seguido alguna guía al eliminar los otros componentes. Llegado este punto, me decidí por borrar todo violentamente, lo cual implicó borrar el directorio C:\Program Files\Microsoft SQL Server y toda clave del registro asociada a SQL Server, como explican en la respuesta de este thread de Social MSDN.

Pero incluso luego de hacer todo eso, seguía habiendo tres entradas en Agregar o Quitar Programas (2008 Common Files, 2008 y otra que no recuerdo). Luego de una búsqueda minuciosa e inútil en el registry, me topé con un Fixit de Microsoft que me salvó la vida.

Luego de correrlo tres veces, realmente todo rastro de SQL había desaparecido. Luego de eso, corrí el instalador y me tiró errores, pero como recomiendan en este post, corrí el desinstalador de 2008 R2 desde Agregar o quitar Programas, corrí el instalador de nuevo y finalmente tuve éxito.

miércoles, 27 de junio de 2012

Parse enums and convert them to other types elegantly

Quite often, we need to to read a value from some source (file, socket, database) and convert it to an enum. In order to define the conversion in a single place, in C++, we usually define a function like this one:


//enum MyEnum{ VALUE_A, VALUE_B, VALUE_C };

MyEnum ParseMyEnum( const std::string& sMyEnum ){
    //Conversion
}

But where do we define this function? Our first idea is to make it a private method of the class which uses the enum, but this is not possible if the enum is used by more than one class. In those cases, we have no choice but to define the function as a free function inside a namespace.

C#'s enums can have methods and extension methods, which would be the most natural and object oriented solution. Nevertheless, since we're talking C++ now, we can use templates to solve this problem in a generic way, for every enum, and use template specialization to define the specific mapping for each enum. This can be done by defining the following template class:

#pragma once

#include <map>
#include <string>

template <typename EnumType, typename TypeToParse=std::string>
class EnumParser{
 std::map<TypeToParse, EnumType> m_EnumMap;
public:
 EnumParser(){}

 EnumType ParseSomeEnum( const TypeToParse &value ){
  std::map<TypeToParse, EnumType>::const_iterator iValue = m_EnumMap.find(value);
  if ( iValue  == m_EnumMap.end() )
    throw std::runtime_error("");
  return iValue->second;
 }
};

When we run into an enum we wish to parse, we specialize EnumParser's constructor and fill m_EnumMap with the desired mapping:

EnumParser<CSerialPort::StopBits, int>::EnumParser(){
        //Windows convention (see DCB structure)
 m_EnumMap[0] = CSerialPort::STOP_BITS_ONE;
 m_EnumMap[1] = CSerialPort::STOP_BITS_ONEPOINTFIVE;
 m_EnumMap[2] = CSerialPort::STOP_BITS_TWO;
}

EnumParser<GeneratorLib::EFilter>::EnumParser(){
 m_EnumMap["Molybdenum"] = GeneratorLib::FLT_MOL;
 m_EnumMap["Rhodium_25"] = GeneratorLib::FLT_ROD_25;
 m_EnumMap["Rhodium_50"] = GeneratorLib::FLT_ROD_50;
}

Finally, when we wish to perform the parsing, we instantiate EnumParser and invoke ParseSomeEnum:


EnumParser<CSerialPort::StopBits,int> enumParser;
 CSerialPort::SConfig sConfig( iBaudRate, enumParser.ParseSomeEnum(iStopBits), iParity, iDataBits );

If we wanted to implement the reverse mapping, that is, convert the enum to a certain type (usually std::string), we can add a second std::map with the inverse mapping to EnumParse (or define it in a subclass of EnumParser). That would make sense if the mapping were asymmetrical; if it's symmetrical, using just a boost::bimap (like a std::map but values can be used as keys to get the keys) would be ideal.

Parsear enums y convertirlos a otros tipos de forma elegante

Muchas veces queremos leer algún valor de cierta fuente (archivo, socket, base de datos) y convertirlo a un enum. Para no andar repitiendo el código de esa conversión, típicamente creamos una función de este estilo:


//enum MyEnum{ VALUE_A, VALUE_B, VALUE_C };

MyEnum ParseMyEnum( const std::string& sMyEnum ){
    //Conversion
}

¿Y dónde ponemos esta función? La primera idea es ponerla como método privado de la clase que usa el enum, pero esto no sirve si el enum es usado por más de una clase. En esos casos, no nos queda más alternativa que definir la función fuera de toda clase, dentro de un namespace.

Los enums de C# pueden tener métodos, lo cual sería la solución más natural y orientada a objetos. Sin embargo, en C++ podemos usar templates para resolver este problema de forma genérica para todo enum, y usar especialización de templates para definir el mapeo específico de cada enum. Para ello, definimos el siguiente template:

#pragma once

#include <map>
#include <string>

template <typename EnumType, typename TypeToParse=std::string>
class EnumParser{
 std::map<TypeToParse, EnumType> m_EnumMap;
public:
 EnumParser(){}

 EnumType ParseSomeEnum( const TypeToParse &value ){
  std::map<TypeToParse, EnumType>::const_iterator iValue = m_EnumMap.find(value);
  if ( iValue  == m_EnumMap.end() )
    throw std::runtime_error("");
  return iValue->second;
 }
};

Cuando nos topemos con un enum que querramos parsear, primero definimos una especialización del constructor de EnumParser e inicializamos m_EnumMap con el mapeo deseado:

EnumParser<CSerialPort::StopBits, int>::EnumParser(){
        //Windows convention (see DCB structure)
 m_EnumMap[0] = CSerialPort::STOP_BITS_ONE;
 m_EnumMap[1] = CSerialPort::STOP_BITS_ONEPOINTFIVE;
 m_EnumMap[2] = CSerialPort::STOP_BITS_TWO;
}

EnumParser<GeneratorLib::EFilter>::EnumParser(){
 m_EnumMap["Molybdenum"] = GeneratorLib::FLT_MOL;
 m_EnumMap["Rhodium_25"] = GeneratorLib::FLT_ROD_25;
 m_EnumMap["Rhodium_50"] = GeneratorLib::FLT_ROD_50;
}

Cuando querramos parsear, instanciamos EnumParser y llamamos a ParseSomEnum:


EnumParser<CSerialPort::StopBits,int> enumParser;
 CSerialPort::SConfig sConfig( iBaudRate, enumParser.ParseSomeEnum(iStopBits), iParity, iDataBits );

Si deseamos implementar también el mapeo inverso, o sea convertir un valor del enum a cierto tipo (típicamente std::string), podemos agregar otro std::map con el mapeo inverso a EnumParser, si es que el mapeo es asimétrico, o bien usar boost::bimap (es como un map donde los valores pueden usarse como claves también, puede simularse con dos std::map) si el mapeo es simétrico.