[C con Clase] Duda sobre crear y usar banderas y alternativas a system()

Davidson, Steven srd4121 en njit.edu
Mar Mayo 21 19:24:38 CEST 2013


Hola Antonio,

2013/5/20 Antonio Arjona <antonio.arjona.melches en gmail.com>

>  Hola a todos.
>
> Mi principal duda es la siguiente:
>     A falta de conocer alguna biblioteca dedicada a esto, estoy creando
> una clase que me permita navegar entre directorios, y el caso es que para
> las opciones de configuración me gustaria utilizar banderas pero no tengo
> muy claro como funcionan los operadores de bits...
>

Sugiero que leas el capítulo 18 de nuestro curso de C++:
http://c.conclase.net/curso/index.php?cap=018#inicio que trata sobre los
operadores a nivel de bits.

Estoy aprendiendo por mi cuenta, por tanto perdonadme si meto tanto código,
> si alguien desea ver todo el código tan solo pedírmelo y lo subo a
> github.com, muchas gracias de antemano...
>
> De momento lo que tengo es lo siguiente:
>
> // tengo declaradas unas pocas constantes
>
> #define MOSTRAR_OCULTOS     0
> #define INVERTIR_ORDEN         1
> #define ORDENA_TAMAÑO        2
> #define ORDENA_EXTENSION   3
> //__etc.__
>
>
En C++, no sugiero usar constantes simbólicas del preprocesador. En su
lugar, define constantes (variables); esto es,

const int MOSTRAR_OCULTOS = 0;
const int INVERTIR_ORDEN = 1;
...

O incluso, un tipo enumerado:

enum banderin_t { MOSTRAR_OCULTOS = 0, INVERTIR_ORDEN, ... };

// y dentro de mi clase he creado un struct tal que asi:
>
> struct conf
>         {                                 /**< ls = comando de consola
> Linux (muestra contenido directorio) */
>             bool archOcultos;   /**< ls -all muestra todos los archivos,
> incluidos los ocultos */
>             bool invertir;           /**< ls -r ordena el resultado en
> orden inverso */
>             bool tamano;          /**< ls -S ordena el resultado por
> tamaño */
>             bool extension;       /**< ls -X ordena por extension */
>             // etc.
>
>             void cambia(int opcion);   /
>         }conf;
>
>
No sugiero usar el mismo nombre para el tipo y variable.


> void FileManager::conf::cambia(int opcion)
> {
>         if (opcion = MOSTRAR_OCULTOS)
>

Esto no es lo que quieres. Te interesa el operador ==, para comparar.

        {
>                 if ( archOcultos == false) archOcultos = true;
>                 else archOcultos = false;
>
>

Esta forma de expresarse no es necesaria. En su lugar, escribe:

archOcultos = !archOcultos;

Sin embargo, por la lógica de tu diseño, esto no refleja tus intenciones.
Lo que te interesa hacer es dejar constancia de que quieres "mostrar
ficheros y directorios ocultos. Por lo tanto, te interesa que el booleano
sea verdadero; es decir,

if( MOSTRAR_OCULTOS == opcion )
  archOcultos = true;

O incluso podríamos reescribir lo anterior así:

archOcultos = MOSTRAR_OCULTOS == opcion;

Así no tenemos que hacer pruebas, simplemente guardamos la verdad en una
variable booleana.

        }
>         // etc.
> }
>
> Tras investigar por mi cuenta, mas o menos creo que seria tal que así:
>
> // los #defines de las banderas
>
> #define MOSTRAR_OCULTOS     0x00000001
> #define INVERTIR_ORDEN         0x00000010
> #define ORDENA_TAMAÑO        0x00000100
> #define ORDENA_EXTENSION   0x00001000
> // etc.
>
> // y pretendo usar las banderas miclase::conf::cambia(MOSTRAR_OCULTOS |
> INVERTIR_ORDEN |  etc...)
>
>
Vas por buen camino, pero date cuenta que esos unos están en hexadecimal.
Esto implica que 1 dígito hexadecimal involucra 4 bits (dígitos binarios).
Lo más seguro es que no requieras tantos lugares para indicar cada
concepto; con 1 bit es suficiente.

Sugiero hacer esto:

const int MOSTRAR_OCULTOS = 0x01;  // 0001
const int INVERTIR_ORDEN = 0x02;        // 0010
const int ORDENA_TAMANNO = 0x04;    // 0100
const int ORDENA_EXTENSION = 0x08;  // 1000
...

Esto implica que podemos combinar estos bits en una sola secuencia sin que
haya solapamiento de valores. Por ejemplo,

MOSTRAR_OCULTOS | INVERTIR_ORDEN | ORDENA_TAMANNO

significa que obtenemos un valor de: 0111.


Ahora bien, puede haber un problema con los demás banderines. Por ejemplo,
'ORDENA_TAMANNO' y 'ORDENA_EXTENSION' conceptualmente son métodos
mutuamente exclusivos. Es decir, no podemos ordenar por tamaño Y por
extensión simultáneamente. Esto implica que no podemos usar dos bits
diferentes, porque no tiene sentido interpretar la secuencia binaria: 1100.
En este caso, necesitamos dos bits para representar estos valores e
interpretar esa pareja de bits correctamente. Por ejemplo,

const int ORDENA_TAMANNO = 0x00;    // 0000
const int ORDENA_EXTENSION = 0x0C;  // 1100

Claro está, podríamos interpretar las secuencias de bits para aceptar o
rechazarlas; o sea, alguna forma de validación.

En general, deberías diseñar el formato de la secuencia de bits creando una
correspondencia entre lugares de bits y los conceptos de cada banderín. Por
ejemplo,

criterio de ordenación
 |  |  sentido de ordenación
 |  |  |  mostrar ocultos
 |  |  |  |
3 2 1 0

void FileManager::conf::cambia(Uint32 opcion)
> {
>         // mostrar archivos ocultos
>         if ((opcion >> 0) &0xf  == 0x1)
>         {
>                 if ( archOcultos == false) archOcultos = true;
>                 else archOcultos = false;
>
>         }
>
>         // invertir el orden de aparición
>         if ((opcion >> 4) &0xf == 0x1)
>         {
>                 if ( invertir == false) invertir = true;
>                 else invertir = false;
>
>         }
>         // etc.
> }
>
> Esto seria correcto?
>

Podrías hacer esto, pero creo que es más fácil crear la máscara AND exacta
para consultar cada bit que te interesa. Afortunadamente, ya las creaste:

if( opcion & MOSTRAR_OCULTOS )
{
  ...
}

if( opcion & INVERTIR_ORDEN )
{
  ...
}

y así sucesivamente. Además, esto es mucho más legible que andar con
valores hexadecimales.


Quiero agregar que no estoy convencido de que necesites tantas variables
booleanas. De hecho, dejaría el valor de los banderines en un solo campo de
tipo entero (sin signo). Así no tienes que estar convirtiendo los
banderines a booleanos. Lo que sí te puedo aconsejar - si quieres - es que
crees varias funciones miembro; por ejemplo,

bool mostrarOcultos() const  { return opcion & MOSTRAR_OCULTOS; }
bool invertirOrden() const  { return opcion & INVERTIR_ORDEN; }
...

...............................
>
> La otra cuestión es la manera que estoy usando para desplazarme entre
> directorios, aunque me funciona perfectamente con Linux, quisiera saber si
> es una manera correcta de hacer esto y si hay alternativas mejores, mi
> intención es que en un futuro sea multi plataforma.
> Lo que hago es:
>
>
Creo que deberías separar tu representación de ficheros, directorios, y
rutas de la representación de Linux, Microsoft, MacOS, etc.. De esta forma,
usas una interfaz común para todas las plataformas, que trae menos
quebraderos de cabeza a la hora de usar directorios y ficheros dentro de
otros programas. De hecho, esto es un claro ejemplo del uso de clases en
C++; específicamente, polimorfismo. Por ejemplo,

class SistemaFicheros {...};

class SistemaFicheros_Microsoft : public SistemaFicheros {...};
class SistemaFicheros_MacOS : public SistemaFicheros {...};
class SistemaFicheros_Linux : public SistemaFicheros {...};
...

 Y luego, haríamos algo así:

SistemaFicheros *psf = new SistemaFicheros_Microsoft;


Si de verdad quieres algo multiplataforma, entonces puedes usar la
biblioteca Boost, que ahora pertenece al estándar de C++. Así te quitas de
encima tener que crear toda esta parafernalia.


Espero que todo esto te oriente.

Steven
------------ próxima parte ------------
Se ha borrado un adjunto en formato HTML...
URL: <http://listas.conclase.net/pipermail/cconclase_listas.conclase.net/attachments/20130521/bb5c1c1c/attachment.html>


Más información sobre la lista de distribución Cconclase