Returning A Different Data Type Depending On A Variable

Okay, I've been working on this Texture class that's going to be compatible with both SDL and OpenGL with the capability to create an array of textures within one class as opposed to multiple classes and such.

Now I've run into a slight issue if I want to return the value of a texture. There's two different types of textures: SDL_Texture, and a standard GLuint Texture. The problem I have is that one or the other is used, not both which is depending on whether or not the person is using OpenGL.

So when the user wants to get the texture, I need the ability to return either an SDL_Texture, or GLuint depending on whether or not OpenGL is being used.

I tried this with a template class, but it didn't work, but I'll post the code so you can see what I'm trying to do.

1
2
3
4
5
6
7
8
9
10
11
template <class Tex> Tex Texture::GetTexture(int TextureNumber)
{
    if (TextureNumber > NumTextures || TextureNumber < 0)
        return;
    if (!Loaded[TextureNumber])
        return;
    if (UsingGL)
        return GLTextures[TextureNumber];
    else
        return SDLTextures[TextureNumber];
}


It basically just comes down to the last four lines of code. If the person is Using OpenGL return a GLuint, if the person is using SDL, return an SDL_Texture.

I would prefer to have the GetTexture function to be one function instead of two separate ones so I don't have to call a different function every time to check if I'm using SDL or OpenGL.

Any help on how this can be done is greatly appreciated.
The problem I have is that one or the other is used, not both which is depending on whether or not the person is using OpenGL.


The obvious solution is not to have 2 arrays of the different types in one class, and let the user specify which he or she is using by supplying the type via template parameter.
You can return a void pointer to the texture and convert it to pointer to the type you want and get and just put * operator in front to get the actual texture. (like a template only you put it in front of function name)

Like this:
1
2
3
void* GetTexture(int TextureNumber)
    { return &GLTextures[TextureNumber]; } //Notice the & operator
GLTex = *(GLTextures*)GetTexture(5);
I think macros fit well here:
1
2
3
4
5
6
7
#ifdef SDL
// ... function declaration
#endif SDL

#ifdef GL
// ... Same name as you'd use with SDL, just different return type..., slightly different code of course.
#endif GL 


I see this used quite often in multi-platform libs.

There are two other ways I know of:
1. Return a union containing both pointers. Make the user choose which ptr to use.
2. throwing either ptr, then catching it outside the function:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>

void integer_or_float(bool integer)
{
    if (integer)
        throw std::rand();
    throw 32E9f;
}

int main()
{
    int b;
    std::cin >> b;
    try
    {
        integer_or_float(b);
    }
    catch (float a)
    {
        std::cout << "Float!\n";
    }
    catch (int a)
    {
        std::cout << "Integer!\n";
    }
    return 0;
}


Reckon that last one to be too slow.
@ OP: Have you looked at the template tutorial here? It's how I learned to use them and it's pretty good. Also when writing something like this it's worth the little extra work to have some feed back from the tool itself.

Your code might work like this.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
struct Array_Limit_Exception: public std::exception
{
    virtual const char* what() const throw()
    {
        return "Too Many Elements For This Array, Returning End Of Array\n";
    }
}ALE;



struct Not_Loaded_Exception: public std::exception
{
    virtual const char* what() const throw()
    {
        return "Array Element Not Loaded. Attempting To Load Texture.\n";
    }
    const char* FinalFail() const throw()
    {
        return "Failed To Load Texture. Returning Default Texture.\n";
    }
}NLE;



template <class Tex> Tex Texture::GetTexture(int TextureNumber)
{
    Tex Texture = 0;

    try
    {
        if(TextureNumber > NumTextures || TextureNumber < 0)
        {
            throw ALE;
            return Loaded[NumTextures]; //I'm Using Loaded As A Stand In Here
        }
        if(!Loaded[TextureNumber])
        {
            throw NLE;
        }
    }
    catch(Array_Limit_Exception E)
    {
        std::cerr << E.what();
        return Texture /*The End Of Your Array*/;
    }
    catch(Not_Loaded_Exception N)
    {
        std::cerr << N.what();
        try
        {
            if(!/*Try To Load Your Default Texture*/)
            {
                throw NLE;
            }
            else
            {
                return /*YOUR DEFAULT TEXTURE*/;
            }
        }
        catch(Not_Loaded_Exception fN)
        {
            std::cerr << fN.FinalFail();
            return 0;
        }
    }

    return Texture;
}
Last edited on
Topic archived. No new replies allowed.