2012-10-03 74 views
5

Tengo una pregunta sobre "buenas prácticas de diseño" en C++. Estoy escribiendo una biblioteca numérica en C++ 11 y uso una gran cantidad de metaprogramación y técnica basada en plantillas. Pero tengo una pregunta muy básica:Diseño C++: funciones con opciones booleanas

Considere una función que puede tener dos comportamientos muy cercanos a excepción de una opción que se puede activar mediante un indicador booleano. Considero solo una bandera que puede ser configurada/no configurada por el desarrollador y no una bandera que se puede establecer/desactivar en el tiempo de ejecución. Hay 3 posibilidades de diseño:


1) escribir dos funciones con la opción explícita en su nombre:

myFunctionFlag1(...); 
myFunctionFlag2(...); 

2) utilizar un parámetro de plantilla:

template<bool Flag> myFunction(...); 

3) Use un parámetro variable:

myFunction(..., const bool flag); 

En términos de buenas prácticas de diseño, ¿qué solución es aceptable/inaceptable? Si hay una mejor solución, ¿cuál es y por qué? Si hay una peor solución, ¿cuál es y por qué?

EDITAR: para las funciones consideradas, la sobrecarga de tiempo de ejecución podría considerarse insignificante, por lo que este no es el punto más crítico.

EDIT 2: Sé que los tres funcionan. Pero como mi biblioteca tendrá usuarios, debe tener un diseño confiable/bueno.

¿Es la opción 2 común (porque me parece que es un buen compromiso)?

+0

http://codereview.stackexchange.com –

+0

Recomiendo este artículo: http://www.drdobbs.com/conversationstruth-or-consequences/184403845 –

Respuesta

11

Ninguna de las anteriores. Los indicadores booleanos son terribles para la legibilidad del código. Si la funcionalidad controlada por la bandera es lo suficientemente pequeña como para que tenga sentido usar una sola función, entonces use una sola función, pero no use bool como el tipo de la bandera. En su lugar, utilice una enumeración con encuestadores que tienen nombres útiles:

enum class MyFunctionMode { EnableFoo, DisableFoo }; 

void myFunction(..., MyFunctionMode mode); 

Este patrón hace que sea fácil de entender en el lugar llamado las opciones que se están proporcionando a la función. Se pueden combinar múltiples opciones usando banderas.

+0

¿Cuál es la diferencia entre tener un parámetro bool poco claro y, por ejemplo, algo así como 'new Rectangle (50, 50, 100, 100)'? Sí, podríamos usar constantes para eliminar números mágicos, pero entonces la responsabilidad recae sobre el usuario de Rectangle, mientras que la clase en cuestión aplica la solución enum para bools. ¿Debería el constructor Rectangle tomar argumentos de tipo Point and Dimension (o Point and Point)? O tal vez typedef argumentos? –

+2

@sftrabbit: Sí, creo que el constructor 'Rectangle' debería tomar un par de' Point's, o algo similar. Si lo hace, el código será más fácil de entender y no habrá sobrecarga en el tiempo de ejecución (al menos no con un buen y moderno compilador de optimización). Además, es mejor codificar tanta información como sea posible usando tipos (por ejemplo, en lugar de tener _cuatro enteros_, tienes _dos puntos 2-D_). Los errores de tipo se encuentran en tiempo de compilación y los errores de compilación son mucho mejores que cualquier tipo de error de tiempo de ejecución porque no pueden pasar desapercibidos. –

2

Mi recomendación sería utilizar la opción 1.

Comentarios:

  1. El más simple y clara. No tiene tiempo de ejecución.
  2. No veo suficiente justificación (que podría estar presente si se proporcionan más detalles) para utilizar este enfoque. Es más complejo que la opción 1.
  3. Esta variante tiene una sobrecarga de tiempo de ejecución. Todavía se puede usar en un código que no sea de tiempo/CPU crítico. Si su función es grande (más de 100 líneas), esta variante se vuelve más atractiva,

Mis 2 centavos.

0

Su primera solución duplica el código, y eso es algo que nadie quiere hacer.

En el tercero, es aceptable, pero la sentencia if se evaluará en tiempo de ejecución e implica una pérdida de tiempo.

La plantilla uno parece ser apropiada. La sentencia if será eather if (true) o if (false) en tiempo de compilación, y el compilador la optimizará, como si no fuera una instrucción if.

+0

"Su primera solución duplica el código", a menos que implemente una en términos del otro. –

+0

O ambos en términos de un tercero. –

0

Puedes hacer las tres cosas que sabes.

void fun_that_does_stuff(); 
void fun_that_does_something_else(); 

template < bool F > 
void fun_that_does_stuff_or_something(); 
template < > 
void fun_that_does_stuff_or_something<true>() { fun_that_does_stuff(); } 

... blah blah. 
0

La duplicación del código no trivial es la peor opción. Si las dos funciones hacen poco más que enviar a la función realmente interesante, puede ser la opción correcta. Por supuesto, las dos funciones podrían enviarse a la versión con plantilla pero podrían dar una mejor indicación de la diferencia que podría hacer true o false.

Otra preocupación es cómo se utilizan realmente las funciones: si se llaman desde funciones que tienen las mismas configuraciones o configuraciones de las que se puede derivar la elección de indicadores, usar nombres diferentes se vuelve bastante molesto o incluso no se puede mantener: ' d Extremo de tener que llamar a las funciones condicionalmente:

(flag? &myFunctionFlag1: &myFunctionFlag2)(...); 

la elección entre las otras dos versiones conos reduce a la pregunta: ¿importa el cheque? Si lo hace, la plantilla es la única opción. Si no es así, puedes elegir cualquiera de las versiones y apostaría a que una encuesta anónima votaría por la versión en tiempo de ejecución: los ojos de la mayoría de mis colegas se desvanecen cuando les muestro una plantilla trivial.