Stratos: Punto de Encuentro de Desarrolladores

¡Bienvenido a Stratos!

Acceder

Foros





Namespaces, Enums Y Tipos Propios

Iniciado por tamat, 13 de Abril de 2006, 04:08:38 PM

« anterior - próximo »

tamat

 Resulta que para el sistema GUI de mi engine necesito tener muchas constantes propias (no se si este es el nombre adecuado), me refiero a palabras que para el engine significan algo, cosas como Top, Bottom, Left, Right, BorderColor, etc. La cuestión es que tengo muchisimos así que me interesa tenerlos catalogados (no quiero tener un archivo con un enum gigante con todo dentro).

Lo que quiero es que si una función solo puede recibir Positions (p.e. StickTo(Position::Top) ) pues el programador esté obligado a pasarle un Position (es decir, Top, Left, ... ) y no otra cosa.

He probado con enums pero el problema de los enums es que se convierten en globales colisionando entre ellos (ya que puede que Position tenga un valor None y BackgroundType tenga otro None. Además con enums no puedo prevenir que el programador le pase un tipo que no tiene nada que ver.

¿Cómo lo puedo hacer exactamente? Es posible?

Me interesa que sea mediante enums porque no quiero tener que definir manualmente el valor numerico de cada palabra.

Gracias de antemano.
Por un stratos menos tenso

zupervaca

 Veamos...

este codigo...

enum Position
{
Top, Left, Right, Bottom
};

Position pos = Position::Top;

Esta forma de acceder a los enums (Position pos = Position::Top;) no es valida, muchos compiladores te diran que Position no tiene el elemento o componente llamado Top, el visual c++ traga, pero el gcc por ejemplo da error.
La solucion que puedes hacer es:

enum Position
{
PositionTop, PositionLeft, PositionRight, PositionBottom
};

Position pos = PositionTop;

Yo es lo que normalmente hago, asi los enum nunca se pisan y accedo a ellos correctamente, no obstante si no te gusta tienes esta otra opcion:

class Position
{
public:
   enum Value
   {
       Top, Left, Right, Bottom,
   };
};

Position::Value pos = Position::Bottom;

Este ultimo es mas elegante ;)  

Pogacha

 
En realidad lo que yo hago es:

class MiClase {
  enum Posicion  {   ...    };
  mifuncion( Posicion laposicion, ...);
};

Luego el programador hace MiClase::mifuncion(MiClase::Arriba, ... );

Pero si son constantes globales tienes el namespace

namespace Posicion {
enum { None, Top, ... };
};
namespace Color {
enum { None, Blanco, ... };
};
mi_funcion_global_o_lo_que_sea( Posicion::Top, Color::Blanco };


Ambos son cosas distintas y mas aprovechables en una u otra situación.
Dependiendo de que estos enums solo seán usados en una clase, los enumeras dentro de la clase y punto, ahora si son globales con estos namespaces terminas el problema.

Saludos.

tamat

 Gracias a ambos. Creo que me hago una idea, supongo que usaré el metodo de las clases que dice zupervaca pero en lugar de clase pondré struct, así el enum es publico y me ahorro la palabra public:, la cosa quedará así:
struct Position {
enum {
  Top,
  Left,
  Right,
  Bottom
};
};

Y así ya puedo hacer lo de Position::Top o forzar un Position como parametro.

EDIT:

Arg, no, no puedo hacer lo de forzar un tipo de parametro ya que si fuerzo un Position el compilador espera que le pase un new Position, no un enum que hay dentro. :/
Nada me veo haciendolo sin poder usar el scope, pero es una putada porque son nombres muy largos... cosas como PropertyBackgroundColor...

EDIT 2:

Vale, ya lo tengo, al final hago esto:
namespace Position {
enum Position {
  Top,
  Left,
  Right,
  Bottom
};
};

De esta manera puedo hacer lo siguiente:
void MiFuncion( Position::Position pos )
{
...
}

Si, se que es un tanto rebuscado pero consigo las dos cosas que quería, evitar colisiones y tener los valores dentro de un scope.

Por un stratos menos tenso

zupervaca

 Con el ejemplo que te puse antes:

class Position
{
public:
  enum Value
  {
      Top, Left, Right, Bottom,
  };
};

Puedes hacer todo:

Position::Value pos = Position::Bottom;

void MiFuncion( Position::Value valor )
{
   ....
}

Despues de varias horas programando veras que poner Position::Position no queda elegante por el codigo ;)

El mayor problema de usar namespace es que no podras tener enumeraciones dentro de una clase, ademas con un programa que genere documentacion te pondra las enumeraciones con los namespaces y puede llegar a ocasionar un lio brutal en la documentacion.

Ejemplo de enumeracion dentro de otra clase

class Rectangulo
{
public:
   ....

   class Position
   {
   public:
      enum Value
      {
          Top, Left, Right, Bottom,
      };
   };
};

_Grey

 Igual soy el único que no le entiende, pero creo que tamat pretende usar enum's para forzar tipos y al mismo tiempo poder usar varios valores del enum para activar banderas, ( (Top|Left); por ejemplo ).

Si esto es lo que pretendes, no es posible, además de que los valores de un enum son consecutivos, con lo cual, podrían salir incompatibilidades, y de ser posible, tendrías que darle un valor a cada uno, que creo que es precisamente lo que quieres evitar... :P

Usa enum cuando los valores sean excluyentes, pues solo podrás poner uno de ellos, y un const tipo_variable, cuando quieras poder activar banderas de forma independiente.

Si quieres evitar un valor mal pasado por el programador siempre puedes pasar el valor que le pasan a la función por una mascara para ver si esta activa alguna bandera que no usas, y devuelve un código de error.

... de todas formas esta comprobación de error no es muy recomendable, es típico que algunas de esas constantes coincidan, es decir las constantes de, por ejemplo, la case ventana tienen unos valores, pero las constantes que usa la clase imagen tienen otros, también independientes, pero las constantes de una clase pueden colisionar con las de otra.

Puedes forzar un tipo con múltiples valores que no colisionen, creando una clase para ello, pero detesto del todo ese método de tener que crear toda una clase para poder crear otras clases con las que poder llamar a una función para una tontería (asco)

Así que te recomiendo el sistema del 3er párrafo, const tipo_variable, y enum cuando sea necesario, que es lo "normal".

Saludos.

tamat

 No Grey, gracias pero no quería usar mascaras ni nada parecido. De hecho para mascaras ya tengo una clase especial que sí usa enums pero haciendo bit shifting y queda así -> 1<
Por un stratos menos tenso

Pogacha

 namespace Position {
enum Value {
 Top,
 Left,
 Right,
 Bottom
};
};


Position::Value   es el tipo ( position value literalmente valor de posicion)

Position::Top    es un valor ( position top literalmente posicion arriva)

Esta es la forma correcta si lo que quieres es forzar un tipo y evitar colision de nombres.

Pero el problema que encuentro es que el tipo puede ser tratado como entero en algunos compiladores ...

void SetPosition(Position::Value pos); // declaracion del mutator

SetPosition( 123 ); // esto sería valido en algunos compiladores-

SetPosition(Color::Black); // lo mismo esto ya que Color::Black es un tan solo un int para algunos compiladores.

Haz la prueba, pero no utilizes una clase como te dice zupervaca, los namespaces fueron diseñados para evitar colisiones de nombres, solo pon el enum dentro de una clase si este enum solo será usado por la clase.

Saludos

zupervaca

 Lo voy a poner mas sencillo

namespace Position
{
   enum Value
   {
       Top, Left, Right, Bottom
   };
};
namespace Flags
{
   enum Value
   {
       Top, Bottom
   };
};
Position::Value valor1 = Position::Top;
Flags::Value valor2 = Flags::Bottom;
void MiFuncion( Position::Value v1, Flags::Value v2 )
{
   ...
}

El problema de aqui es que son globales y no puedes ponerlo en la clases.

Si pones como dice pogacha en las clases una enumeracion tendras los problemas que tuviste hasta ahora, es decir, colision entre nombres:

class Caja
{
public:
   enum Position
   {
       Top, Left, Right, Bottom
   };
   enum Flags
   {
   Top, Bottom// <-- Colision no se puede
   };
};

Ademas si solo tuvieras una enumeracion para que no tuviera colision, tendrias algunas enumeraciones poniendo Position::Value y otros Caja::Position, con lo que el que coja la libreria estara todo el dia perdido mirando la documentacion.

La solucion que te pongo es unificar toda la forma de programar las enumeraciones, te pongo el ejemplo mas complejo que puedes encontrate:

class Caja
{
public:
   class Position
   {
   public:
       enum Value
       {
           Top, Left, Right, Bottom
       };
   };
   class Flags
   {
   public:
       enum Value
       {
           Top, Bottom
       };
   };
};

Caja::Position::Value valor1 = Caja::Position::Top;
Caja::Flags::Value valor2 = Caja::Flags::Bottom:
void MiFuncion( Caja::Position::Value v1, Caja::Flags::Value v2 )
{
   ...
}

De esta forma este donde este la enumeracion siempre sabras que para crear o pasar variables a funciones debes de ponerle "::Value" y que sus datos los coges sin ponerle el value, si haces esto en toda la libreria, los programadores que la cojan no estaran todo el dia perdidos por que habras definido un estandar en tu libreria. Si no te gusta poner clases pon estructuras que es lo mismo.
Si las enumeraciones fueran globales pues seria quitar la clase Caja accediendo a ellas de la misma manera, es decir:

class Position
{
public:
   enum Value
   {
       Top, Left, Right, Bottom
   };
};
class Flags
{
public:
  enum Value
  {
      Top, Bottom
  };
};
Position::Value valor1 = Position::Top;
Flags::Value valor2 = Flags::Bottom:
void MiFuncion( Position::Value v1, Flags::Value v2 )
{
   ...
}


Ya como ultimo la ultima ventaja de programar de esta manera:

class Position
{
public:
  enum Value
  {
     Top, Left, Right, Bottom
  };
};

class Caja : public Position
{
public:
   void Crear( Caja::Position::Value valor )
   {
       ...
   }
};

O tambien:

class Caja
{
public:
   typedef ::Position Position;
   void Crear( Caja::Position::Value valor )
   {
       ...
   }
};


Editado: Tambien podrias acostumbrarte a poner un valor dentro de la enumeracion que fuera Count para indicar el numero de enumeraciones que tiene, de esta manera pueden crear arrays del tamaño de una enumeracion o para otras cosas.

Editado2: Te dejo un ejemplo de como lo tengo yo montado en una clase de dibujar imagenes

   /// <summary>Formas de renderizar una imagen</summary>
   struct RopMode
   {
    /// <summary>Formas de renderizar una imagen</summary>
    enum Value
    {
     /// <summary>Copia la imagen si tener en cuenta el alpha</summary>
     Copy  = 0,
     /// <summary>Copia la imagen teniendo en cuenta el alpha</summary>
     Blend  = 1,
     /// <summary>Solo se dibuja aquel alfa que se indique</summary>
     AlphaKey = 2,
    };

    /// <summary>Numero de elementos en la enumeracion</summary>
    static const int Count = 3;
   };

Despues podrias ponerle mas atributos a la estructura indicando si la enumeracion es de numeros consecutivos, si es en base 2, etc, mientras mas informacion pongas en la estructura mas funcionalidad das en la libreria, no obstante implica mucho mas curro ;)  

Pogacha

 
Cita de: "zupervaca"Si pones como dice pogacha en las clases una enumeracion
Cita de: "Pogacha"pero no utilizes una clase como te dice zupervaca, los namespaces fueron diseñados para evitar colisiones de nombres, solo pon el enum dentro de una clase si este enum solo será usado por la clase.
Creo que fui claro en lo que quise decir, en tu primer post no se entendia claro que querías decir, ahora en este último si.

En el caso que demuestra zupervaca si ponlo en clases.

tamat

 A todo esto, qué método usais vosotros para cuando tienes muchas palabras clave? tirais de defines? consts?
Por un stratos menos tenso

zupervaca

 ¿a que te refieres con palabras claves?

Es que yo entiendo por palabra clave a las "instrucciones nativas" del lenguaje, es decir el for, if, return, sizeof, etc.

tamat

 leñe, a lo que llevo hablando en todo el thread, palabras propias de tu engine
Por un stratos menos tenso

_Grey

  :ph34r: sorry por el anterior gazapo, si no era lo que querías....

Respecto a lo ultimo que preguntas, si tienes que decidir entre #define o un const, si estas en C++, por lo que se , se usa const, así el valor tendrá "protección de tipo".

zupervaca, diría que se refiere a las constantes, que era precisamente lo que le traía por aquí.

Saludos.

zupervaca

 Yo pondria const como dice grey pero hay algunos casos en los que te puede interesar usar una funcion en linea, un ejemplo es este:

  /// <summary>Obtener el valor de PI</summary>
  /// <returns>El valor de PI</returns>
  template <class TYPE> __forceinline TYPE pi()
  {
   return (TYPE)3.141592653589793238462643383279502884197169399375105820974944592;
  }

La ventaja es que si en un futuro tienes que hacer algo para calcular pi no tendras que cambiar nada de codigo ya que seguira siendo una funcion, ademas de esta manera resuelves los casting en tiempo de ejecucion ya que con el template el compilador lo hara durante la compilacion (aunque este ultimo no estoy seguro ya que el compilador podria dejar un casting igual, me imagino que dependera del compilador).
Para llamar a esta funcion se haria asi: pi(); como ves es una manera mas en plan pijillo :lol:

PD: Si ese es el valor aproximado de pi jajajaja, fue sacado del wikipedia






Stratos es un servicio gratuito, cuyos costes se cubren en parte con la publicidad.
Por favor, desactiva el bloqueador de anuncios en esta web para ayudar a que siga adelante.
Muchísimas gracias.