2010-05-08 18 views
6

Tengo algunas funciones que se pueden agrupar pero que no pertenecen a ningún objeto/entidad y, por lo tanto, no se pueden tratar como métodos.C++ Espacios de nombres y plantillas

Así que, básicamente en esta situación, crearía un nuevo espacio de nombres y pondría las definiciones en un archivo header, la implementación en el archivo cpp. También (si es necesario) crearía un espacio de nombre anónimo en ese archivo cpp y pondría todas las funciones adicionales que no tienen que estar expuestas/incluidas en la interfaz de mi espacio de nombres allí.

ver el código de abajo (probablemente no es el mejor ejemplo y se podría hacer mejor con otro programa de arquitectura, pero simplemente no puedo pensar en una muestra mejor ...)

Código de la muestra (header)

namespace algorithm { 
    void HandleCollision(Object* object1, Object* object2); 
} 

código de la muestra (cpp)

#include "header" 

// Anonymous namespace that wraps 
// routines that are used inside 'algorithm' methods 
// but don't have to be exposed 
namespace { 
    void RefractObject(Object* object1) { 
     // Do something with that object 
     // (...) 
    } 
} 

namespace algorithm { 
    void HandleCollision(Object* object1, Object* object2) { 
     if (...) RefractObject(object1); 
    } 
} 

Hasta ahora todo bien. Supongo que esta es una buena manera de administrar mi código, pero no sé qué debería hacer si tengo algunas funciones basadas en plantillas y quiero hacer básicamente lo mismo.

Si uso plantillas, tengo que poner todo mi código en el archivo header. Bien, pero ¿cómo debería ocultar algunos detalles de implementación?

Quiero ocultar RefractObject función de mi interfaz, pero no puedo simplemente retire su declaración (sólo porque tengo todo mi código en un archivo header) ...

El único enfoque que se me ocurrió fue algo así como:

código de la muestra (header)

namespace algorithm { 
    // Is still exposed as a part of interface! 
    namespace impl { 
     template <typename T> 
     void RefractObject(T* object1) { 
     // Do something with that object 
     // (...) 
     } 
    } 

    template <typename T, typename Y> 
    void HandleCollision(T* object1, Y* object2) { 
     impl::RefractObject(object1); 
     // Another stuff 
    } 
} 

Alguna idea de cómo hacer esto mejor en términos de diseño de código?

Respuesta

7

Esa es una solución bastante común. Boost lo hace, y yo también lo hago, pero con el espacio de nombre detail. Simplemente haga una regla: "¡no mire adentro detail!"

File-wise, recomiendo dar detalles de su propio archivo, y guardarlo en una carpeta de detalles. Es decir, mi código sería similar a:

//   v 
#include "detail/RefractObject.hpp" 

namespace algorithm { 

    template <typename T, typename Y> 
    void HandleCollision(T* object1, Y* object2) { 
     detail::RefractObject(object1); 
     // Another stuff 
    } 
} 

Esto es sólo una buena práctica de código en general (mantener las cosas separaron y reutilizables) y mantiene limpio el archivo de cabecera de los detalles de implementación.

1

No puede ocultar su fuente al usuario a menos que primero la compile, lo cual es imposible con las plantillas. Así que sugiero, en cierto sentido, que no te molestes.

También tengo que preguntar por qué Refract no puede ser un método miembro.

+0

Miembro de lo que también, las funciones libres son preferidas de todos modos :) – GManNickG

+0

Bueno, he dicho que esto es solo por muestra. En el caso de mi proyecto real, 'algorithm' incluye algoritmos de aproximación y algoritmos de búsqueda de rutas, por lo que es simplemente imposible hacerlos' members' en el significado de esa palabra ... –

+0

funciones miembro> funciones gratuitas desde la invención de Intellisense. – Puppy

2

Cree una "clase estática": en lugar del espacio de nombres, declare una clase con el mismo nombre.Haga que los operadores de constructor, destructor, copia y asignación sean privados, luego convierta cada una de sus funciones independientes en una función de miembro estático.

Un Ejemplo de plantilla:

template<class T> 
class StringOperator 
{ 
friend SomeOtherLibraryClass; // Let it use "Hidden()" 
private: 
    StringOperator() {} 
    ~StringOperator() {} 
    StringOperator(const StringOperator&); 
    StringOperator& operator=(const StringOperator&); 

    static bool Hidden(const T& input) { 
     // Hidden routine end-users shouldn't see... 
    } 

public: 
    static void YourFunction(T& parameter) { 
     // Some public code.... 
    } 

    static T  AnotherRoutine(const T* ptr) { 
     // Some public code... 
    } 
}; 

Aquí están algunas ventajas frente a un espacio de nombres:
1) Es posible la plantilla a toda la clase, asegurando que usted tiene una versión de todas las funciones para cada IgA- usted podría incluso se especializan para agregar/eliminar funciones basadas en el tipo.
2) Tiene un área privada donde puede ocultar las declaraciones para los objetos no públicos y los métodos que necesita
3) A través del mecanismo "amigo", puede permitir que otros objetos como "SomeOtherLibraryClass" utilicen sus funciones sin exponiéndolos a los usuarios finales.

Los usuarios finales pueden acceder a las funciones con "StringOperator :: FunctionName()", o bien pueden crear plantillas de las funciones para proporcionar "StringOperator :: FunctionName()". Este último es el mismo patrón que usarían para acceder a las funciones en un espacio de nombres.

0

pensé en lo mismo por un tiempo. Al final, decidí usar clases para que el código sea más portátil, reutilizable y general.

  • clases le permite hacer todo tipo de meta-programación como rasgos, despacho de etiquetas y políticas.
  • clases ya tienen público, privado y protegido.

las dos únicas razones por las que se use namespace:

  • para resolver los conflictos de nombres std::for_each vs boost::for_each
  • para reducir el ruido visual. por ejemplo using namespace boost::filesystem, le permite llamar a su función sin ::.

incluso para estos dos casos, puede heredar la clase que tiene los métodos o renombrar iniciando (my_own_filesystem_methods fs; y nosotros como fs.mkdir(). Además, . es más corta que ::.

hay otros beneficios de espacios de nombres como std::cout << "hi" << endl; pero no es muy importante.

Cuestiones relacionadas