Quantcast
Channel: Planeta Código
Viewing all articles
Browse latest Browse all 2721

Header Files: Argumentos expresivos

$
0
0

Una de las tareas a las que más me enfrento cuando leo código heredado, o mi propio código antiguo (donde antiguo puede ser de hace un par de semanas), es saber qué es cada parámetro de una función: en Rectangle::computeArea(4.5, 3.1)¿qué representa 4.5 y qué 3.1? ¿área y altura, o al revés? O en car.setSpeed(50), ¿son kilómetros por hora, millas por hora, metros por segundo?

Para el segundo caso, C++11 viene en nuestra ayuda con literales definidos por el usuario, al que otro día igual dedicamos un tiempo. Mientras tanto os recomiendo esta presentación de Bjarne Stroustrup al respecto (aunque luego habla de más cosas interesantes, como el extenso uso del RAII en C++ moderno).

Por otro lado, en el primer ejemplo no me refiero a métodos para fijar propiedades (setVisible(bool), setWidth(int)), sino a esos argumentos cuyo significado sólo se puede saber mirando la declaración de la función (y rezando para que tenga un nombre con sentido), por ejemplo: void showAnalysisWidget(bool read_only, bool maximized), donde una llamada showAnalysisWidget(true, true) poco nos dice.

Adicionalmente, tenemos algunas posibles fuentes de errores, como intercambiar los nombres de los argumentos en la clase base durante un refactoring, pero olvidarse de hacerlo en las subclases; problema que no sería detectado por ningún compilador y que de seguro pasaría inadvertido en muchos escenarios de prueba. Además, podríamos toparnos con conversiones implícitas de enteros o punteros a booleanos.

Posibles soluciones

Lenguajes como Python ayudan en este problema mediante la posibilidad de usar el nombre del argumento en la llamada. De hecho, se quería que dicha funcionalidad fuese incluida en C++20, pero al final no ha entrado en el estándar (de momento). La sintaxis propuesta era similar a showAnalysisWidget(.read_only=true, .maximized=true).

En C++ podemos atacar el problema con una combinación de tipeado fuerte y de bloquear las conversiones implícitas. Como ejemplo tomaré el caso de argumentos booleanos, donde el problema se reduce en poder indicar si el argumento es verdadero o falso. En este caso además, interesa poder dar contexto a la vez que no añadimos demsiado ruido a nuestro código.

Los siguientes dos artículos de FluenCpp y Andrzej’s abordan el problema en cuestión con diferentes técnicas (los comentarios también aportan algunas interesantes).

Enumeraciones

Una de las ténicas que más se usan es la de definir enumeraciones con dos posibles valores Falsey True, y usar el tipo de dicha enumeración en lugar del booleano. Por ejemplo

enumclassReadOnly{False,True};enumclassMaximized{False,True};voidshowAnalysisWidget(ReadOnlyread_only,Maximizedmaximized);// ...showAnalysisWidget(ReadOnly::True,Maximized::False);

Esto documentaría muy bien el contexto de cada argumento, evitaría confusiones de tipo así como conversiones implícitas. Las pocas pegas son que si queremos usar el argumento en un condicional debemos hacer una comparación “tipográficamente más larga”: if (read_only == ReadOnly::True) o if (static_cast<bool>(read_only)), o al querer convertir una expresión booleana en argumento de nuestra función. Un ejemplo en vivo puede verse acá.

Clase TrueFalse

Esta solución también es bastante corta y sencilla de recordar, y no tiene los inconvenientes antes vistos con los castings, y particularmente es mi preferida. Básicamente se trata de construir una pequeña clase que sólo pueda ser construida con un booleano y que se convierte implícita en booleano si hace falta. Además, una sencilla macro nos facilita la vida a la hora de declarar nuevos tipos.

structTrueFalse{constboolvalue;explicitTrueFalse(boolvalue):value{value}{}operatorbool()const{returnvalue;}};#define DEF_TRUE_FALSE(name) struct name : TrueFalse { using TrueFalse::TrueFalse; }
DEF_TRUE_FALSE(ReadOnly);DEF_TRUE_FALSE(Maximized);voidshowAnalysisWidget(ReadOnlyread_only,Maximizedmaximized);// ...showAnalysisWidget(ReadOnly{true},Maximized{false});

Ahora bien, puede que nos interese deshabilitar las conversiones de otros tipos a booleano, para ello simplemente eliminamos dichos constructores:

explicitTrueFalse(intvalue)=delete;explicitTrueFalse(constvoid*value)=delete;explicitTrueFalse(doublevalue)=delete;

Una versión completa de este código puede ser probada acá.

Esta técnica además puede ser replicada para crear tipos básicos como la mencionada “velocidad”, que unido a los literales definidos por el usuario, dotan a nuestro código de una expresividad y robustez casi insuperables.

Como nota final, documentándome mientras escribía esta entrada me topé con la biblioteca explicit, que entre otras cosas, tiene una variante de esta solución algo más completa (tagged_bool).


Viewing all articles
Browse latest Browse all 2721

Trending Articles


FORECLOSURE OF REAL ESTATE MORTGAGE


Girasoles para colorear


mayabang Quotes, Torpe Quotes, tanga Quotes


Tagalog Quotes About Crush – Tagalog Love Quotes


OFW quotes : Pinoy Tagalog Quotes


Long Distance Relationship Tagalog Love Quotes


Tropa Quotes


Best Crush Tagalog Quotes And Sayings 2017


“BAHAY KUBO HUGOT”


Vimeo 10.7.0 by Vimeo.com, Inc.


Pokemon para colorear


Sapos para colorear


Tagalog Love Quotes – Nagmamahal


Break up Quotes Tagalog Love Quote – Broken Hearted Quotes Tagalog


Patama Quotes : Tagalog Inspirational Quotes


Tagalog Quotes To Move on and More Love Love Love Quotes


5 Tagalog Relationship Rules


Long distances monthsary message tagalog


Re:Mutton Pies (lleechef)


Vimeo 10.7.1 by Vimeo.com, Inc.