Sharing objects between functions

Sharing objects between functions

Hello.
I made myself a class for storing textures and loading them from files.
It's quite a simple class.
Internally it uses OpenGL for managing the texture and libpng for loading images.
It stores a private handle to the texture used by OpenGL, and the texture's dimensions (width & height).
There's a constructor that takes the dimensions and creates a new texture object of these dimensions.
Then there's a method for filling the texture with data which come from a user-provided buffer of pixel data (the data are then copied to the texture managed by OpenGL internally).
I also have a free-standing "factory function" textureFromFile that takes a string with a path to an image file. It loads the file, creates a new texture for it and fills it with the image data, then returns that new object by value.

Everything worked fine as long as I had everything inside main().
But then I wanted to refactor the code in order to make it more robust and readable, by moving the texture-loading code to a separate function init(), and the rendering code to another separate function render(). And this caused a problem :q
Now I need some way to share the texture objects between my init() function and my render() function.
The obvious (and naïve) solution was to make these texture objects global, so that both functions could see them. But of course this requires constructing them :q And I can't construct them yet, from two reasons:

1. I don't know the sizes of the textures yet, until I load their images from files. Without knowing the sizes, I can't construct them.

2. Even if I somehow knew the sizes, or used some temporary values, it won't work anyway, because before entering main() the OpenGL context doesn't exist yet :P I create it inside init(), before calling textureFromFile() a couple of times.

"OK" – I thought to myself – "maybe I could use global pointers then?"
So I created a bunch of pointers, hoping to set them to point at the newly-created textures inside init() and then use them inside render() to access those objects. But then I realized it won't work either :P

If I create a texture object inside init() and set a pointer to it, this object will last until the end of the init() function and then it will go away (it's automatic after all), so the pointer will point to garbage :P

Similarly, I can't set the pointer to whatever textureFromFile() returns, because it's a temporary object that will be gone as soon as the expression ends :P and again, the pointer will point to garbage ;q

It seems that the only option left is new and delete, but this would undermine the whole idea of juggling with value objects. Creating simple objects with just a bunch of numbers inside of them on the heap doesn't sound like a good idea to me, especially if I'd have to remember to delete them later :q

Some libraries "solve" this by having a defaul constructor that does nothing, and a separate create() method that does the actual initialization and resource acquisition. But I don't think it's a good idea either, because it requires the user to remember to call create() before calling any other methods of the object, which otherwise may not work properly or crash :P

Here's my question then:

What is the best way to share objects between functions if one uses simple objects like the Texture class I made? Or maybe I should have designed it in a different way entirely?
Am I missing something?

I guess I have programmed in dynamic languages for too long and it made me forget how to manage objects "the hard way" in languages like C++ :q
So a good refresher of the current design guidelines and trends regarding object creation/destruction/handling would be welcome.


P.S.: The formatting buttons doesn't seem to work in my browser :q Nothing happens when I click them. The preview function doesn't seem to work either (it shows an empty page).
one of these ideas maybe?

type * foo()
{
static type* rv;
rv = new type;
return rv; //OK, you can copy this into another variable when you call it, eg x = foo();
//this is NOT thread safe!
}

and for threaded programs you can pass it in:
void foo(type*& rv)
{
rv = new type;
}

does this help you? I used new and delete as examples only. if the pointer comes from elsewhere or its not a pointer at all, you can still use these same ideas. you can even return the end of a push-back to a vector to avoid news and deletes if you want something kind of neat...

type * foo2()
{
static vector<type> r;
type tmp;
r.push_back(tmp); //effectively 'new'
return & r[r.size()-1];
} //or you can return the vector itself and pick off the last element, and that would let you iterate it if you needed to do something with all the textures or whatever...


Last edited on
OK maybe I clarify a bit, because now when I think of it, the problem is not necessarily specific to my Texture class. It's more general.

Suppose I had the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int main()
{
    // Here I initialize some stuff.
    SomeType1 var1(1,2,3);
    SomeType2 var2(4,5,"abcd");
    
    // Here I use it.
    while (running) {
        var1.use();
        doSomethingWith(var2);
    }
    
    // And here they are automatically released.
}


so quite common programming pattern, nothing fancy here...
As long as you don't want to refactor it into separate functions for more readability and modularity, to get something 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
// Here I initialize some stuff.
void init()
{
    SomeType1 var1(1,2,3);
    SomeType2 var2(4,5,"abcd");
}


// Here I use it.
void use()
{
    var1.use();  // Well, not really: those names are unknown here.
    doSomethingWith(var2);
}

int main()
{
    init();
    
    while (running) {
        use();
    }
    
    // And here they are automatically released.
}


because now, of course, var1 and var2 are not known inside use() :P
In order to make them known, I would have to make them global, something 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
// Global definitions for sharing.
SomeType1 var1 = ...?;    // WUT?
SomeType2 var2 = ...?;

// Here I initialize some stuff.
void init()
{
    var1 = SomeType1(1,2,3);          // Not really: too late for initialization :P
    var2 = SomeType2(4,5,"abcd");
}


// Here I use it.
void use()
{
    var1.use();
    doSomethingWith(var2);
}

int main()
{
    init();
    
    while (running) {
        use();
    }
    
    // And here they are automatically released.
}


but then, what initial values should I give them? :P I can't think of anything meaningful, because my Texture objects can be initialized only when I already know their sizes and data, and only after I have the OpenGL context up and running, which is somewhere inside init() and no earlier!

I can't default-construct them at the global level, because they would have invalid state and any other methods called upon them would fail miserably :P (Not to mention that I can't call any functions to initialize their values on the global level either.)

On the other hand, inside init(), it's already too late for any initialization if I created them globally :P Seems like a conundrum with no exit :/

I found this here piece of advice in one of Stroustrup's books:

Don't introduce a name until you have a suitable value for it.


and I cannot agree more, because this makes a lot of sense, of course.
Unfortunately, Daddy Stroustrup doesn't say what to do when you need that name to be known somewhere else in the program, e.g. in another function, and how to make that name known there too :q

I feel stupid...
Last edited on
-In order to make them known, I would have to make them global -
Not True! Just pass them in: (ok, so you can't initialize this way … keep reading)

1
2
3
4
5
void init(sometype &var1, sometype &var2)
{
    var1 = SomeType1(1,2,3);          // Not really: too late for initialization :P
    var2 = SomeType2(4,5,"abcd");
}


and, if you want to call a constructor, then do so..
1
2
3
4
5
6
7
8
9
10
void init(sometype*& s;)
{
   s = new sometype(ctor params);
}
int main()
{
sometype* st; 
init (st);
*st.use();
}


ok, so st won't be automatically destroyed for you now. you have to do that yourself somewhere. There are ways to handle that cleanly.

finally, its OK to not initialize some things. This is a convention, not a requirement of the language. Lets say you program something that has a fat object that holds 1mb of data. You need to read about a billion of them from a file into your data structure, say its a binary tree. Are you going to construct a billion guys each with a default value and then read your file and over-write that for each one? something like type *tmp = new type(defaultcrap); read from the file into all the fields (overwriting defaultcrap); tree.insert(tmp); ? Or are you just going to load it from the file into tmp and then insert it? Are you going to make your tree node's members uninitialized, or have them also copy useless junk into each field upon construction? Do you see how much slowness that would introduce into this (admittedly exaggerated) example?
Last edited on
Not True! Just pass them in

Passing hundreds of objects as parameters is neither smart nor feasible (especially if you can't know their number at compile-time). This would be a readability nightmare – something contrary to what I wanted to achieve by factoring this code out into separate functions :P (In case you didn't get it: The code above was just an example. In my code I don't have just two objects. There's much more.)

its OK to not initialize some things. This is a convention, not a requirement of the language

Sure, everything is just a convention after all. We may as well not wear any clothes and code in raw assembly. Who cares about establishing class invariants and keeping them anyway? :q

(BTW ever heard of constant objects? They have to be initialized. There's no way around it.)

Are you going to construct a billion guys each with a default value and then read your file and over-write that for each one?

Of course not! But that's also something I wanted to avoid myself – I don't want to reserve memory and default-construct it at the global scope just to have it overriden by the actual construction inside init() :P

Do you see how much slowness that would introduce into this (admittedly exaggerated) example?

Of course I see! But that's also not what I tried to do. You're not trying to make a strawman argument, are you? :q

I expected some more wise advices from this forum.
Anyone else?
Last edited on
I don't think this is the forum for you.
Oh, sorry, I thought this is a forum about modern C++ and software engineering.
My bad then, I'll go somewhere else...
Last edited on
Good luck. Hope you find what you're looking for.
Topic archived. No new replies allowed.