2009-05-31 11 views
9

¿Hay alguna forma de hacer algo como esto?Devolver diferente tipo de datos según los datos (C++)

(correct pointer datatype) returnPointer(void* ptr, int depth) 
{ 

    if(depth == 8) 
     return (uint8*)ptr; 
    else if (depth == 16) 
     return (uint16*)ptr; 
    else 
     return (uint32*)ptr; 
} 

Gracias

+0

¿Cómo espera a usar esto? Es probable que su diseño no siga las mejores prácticas de OOP. – outis

+0

Adobe Photoshop SDK es lo que estoy usando, y usan void * – Joel

+2

¿Podría dar un ejemplo de uso, incluyendo una llamada a una función de Photoshop SDK? – outis

Respuesta

9

No. El tipo de retorno de una función C++ sólo pueden variar en función de los parámetros de plantilla explícitas o los tipos de sus argumentos. No puede variar en función del valor de sus argumentos.

Sin embargo, puede usar varias técnicas para crear un tipo que sea la unión de varios otros tipos. Lamentablemente, esto no necesariamente lo ayudará aquí, ya que una de esas técnicas es nula * en sí misma, y ​​volver al tipo original será un dolor.

Sin embargo, al darle la vuelta al problema, puede obtener lo que desea. Me imagino que te gustaría utilizar el código que envió como algo parecido, por ejemplo:

void bitmap_operation(void *data, int depth, int width, int height) { 
    some_magical_type p_pixels = returnPointer(data, depth); 
    for (int x = 0; x < width; x++) 
    for (int y = 0; y < width; y++) 
     p_pixels[y*width+x] = some_operation(p_pixels[y*width+x]); 
} 

Debido a que C++ necesita saber el tipo de p_pixels en tiempo de compilación, esto no funcionará como está. Pero lo que podemos hacer es bitmap_operation ser en sí mismo una plantilla, a continuación, se envuelve con un interruptor basado en la profundidad:

template<typename PixelType> 
void bitmap_operation_impl(void *data, int width, int height) { 
    PixelType *p_pixels = (PixelType *)data; 
    for (int x = 0; x < width; x++) 
    for (int y = 0; y < width; y++) 
     p_pixels[y*width+x] = some_operation(p_pixels[y*width+x]); 
} 

void bitmap_operation(void *data, int depth, int width, int height) { 
    if (depth == 8) 
    bitmap_operation_impl<uint8_t>(data, width, height); 
    else if (depth == 16) 
    bitmap_operation_impl<uint16_t>(data, width, height); 
    else if (depth == 32) 
    bitmap_operation_impl<uint32_t>(data, width, height); 
    else assert(!"Impossible depth!"); 
} 

Ahora el compilador generará automáticamente tres implementaciones para bitmap_operation_impl para usted.

+1

En C#, puede devolver un tipo de objeto y convertirlo en el objeto deseado (siempre que conozca el tipo). O puede usar genéricos. ¿Puedes hacer algo similar en C++? –

+3

Lo más cercano que tiene C++ a un tipo de objeto es nulo *. También hay un tipo de variante de boost. – bdonlan

+0

Supongo que también usaría la variante boost. –

7

Si puede utilizar un argumento de plantilla en lugar de un parámetro normal, puede crear una función de plantilla que devuelva el tipo correcto para cada valor depth. Primero debe haber alguna definición del tipo correcto de acuerdo con depth. Se puede definir una plantilla con especializaciones para los diferentes tamaños de bits:

// template declaration 
template<int depth> 
struct uint_tmpl; 

// specializations for certain types 
template<> struct uint_tmpl<8> { typedef uint8_t type; }; 
template<> struct uint_tmpl<16> { typedef uint16_t type; }; 
template<> struct uint_tmpl<32> { typedef uint32_t type; }; 

El esta definición se puede utilizar para declarar una función de plantilla que devuelve el tipo correcto para cada valor de bit:

// generic declaration 
template<int depth> 
typename uint_tmpl<depth>::type* returnPointer(void* ptr); 

// specializations for different depths 
template<> uint8_t* returnPointer<8>(void* ptr) { return (uint8_t*)ptr; } 
template<> uint16_t* returnPointer<16>(void* ptr) { return (uint16_t*)ptr; } 
template<> uint32_t* returnPointer<32>(void* ptr) { return (uint32_t*)ptr; } 
+2

Esto no ofrece ninguna ventaja sobre una solución sin plantilla como returnPointer8(), returnPointer16() y returnPointer32(), porque el argumento de la plantilla tiene que ser constante (es decir, conocido por el programador) de todos modos. – Jem

+1

Que los argumentos de la plantilla deban conocerse en tiempo de compilación es una propiedad general de las plantillas. Aún así, esto es más flexible que las funciones con nombres diferentes, por ejemplo, podría definir algo así como la plantilla ptradd (void * ptr, int val) {* returnPointer (ptr) + = val; }. Esto funciona para todas las profundidades. Con funciones de puntero de retorno con nombres diferentes, también necesitaría declarar varias variantes separadas de ptradd. Para la versión con plantilla, una función ptradd es suficiente. – sth

1

Puede asigna algo de memoria en el montón, y devuelve un vacío * que lanzas al tipo que fue asignado. Es una forma peligrosa e insegura de trabajar y es un viejo truco de C.

Puede devolver una unión que contenga todos los tipos de datos válidos (y un indicador de selección).

Puede usar plantillas, que es la forma recomendada de C++ para este tipo de cosas.

Puede proporcionar un conjunto de funciones sobrecargadas que toman como referencia un parámetro (de cada tipo): el compilador decidirá a qué función llamar en función del tipo de datos. A menudo prefiero esta manera porque creo que es la más simple.

0

No; no puedes hacer eso en C++. La respuesta correcta es devolver void *.

Piense en ello desde el lado opuesto de la llamada - y desde el punto de vista del compilador en el que:

¿Cómo sería el compilador podrá verificar si el valor de retorno se utiliza correctamente (como asignado a una variable del tipo correcto, por ejemplo), si no puede saber cuál de los tres tipos de devolución será devuelto.

En ese punto, la noción de asignar "uno de varios tipos" al valor de retorno no tiene sentido. El tipo de devolución de una función no tiene otro propósito en la vida que posibilitar que el compilador haga su trabajo; el compilador necesita "un" tipo para poder hacer la verificación de tipo. Como no sabes cuál es hasta el tiempo de ejecución, el compilador no puede hacer la comprobación de tipos por ti. Tienes que decirle al compilador que "deje de intentar" hacer coincidir el valor de retorno con cualquier tipo de puntero específico; por lo tanto, devuelve un void *.

Si conoce sus argumentos de profundidad en tiempo de compilación, puede usar un conjunto de plantillas como @sth demostradas, o usar un conjunto de funciones independientes separadas, o usar un conjunto de funciones relacionadas que llaman a una función de implementación compartida y luego lanzar el regreso al tipo correcto. Cuál elegiste es una decisión principalmente estética.

Si el valor de depth no se conoce hasta el tiempo de ejecución, entonces probablemente debería devolver void *.

Ahora, supongo que su implementación real realmente hace algo para producir el puntero que no sea lo que muestra el código de muestra. Su código de muestra no es una función real; es más como tratar de duplicar lo que hace un cast. A cast no es una llamada a función; es una directiva de compilación para tratar de "hacer" su operando en un tipo específico (exactamente 'cómo', es una historia larga para otra publicación). No es una operación de lenguaje C++, sino una operación de compilación. No puede reescribir eso en C++.

0

se puede hacer esto:

si (profundidad == 8) (uint8 *) returnPointer (void * ptr, profundidad int) {// etc

Cuestiones relacionadas