C++ Question Need help with handles/pointers

Hlynkacg

Aspiring rocket scientist
Addon Developer
Tutorial Publisher
Donator
Joined
Dec 27, 2010
Messages
1,870
Reaction score
3
Points
0
Location
San Diego
I lack the vocabulary to look this up so I'm going to describe the issue and hope someone can help me, or at least point me in the right direction.

I have a base class, one of it's members is a std::vector full of objects.

I want classes that are friends of, or derived from, my base class to be able to access those objects via handles similar to the way PROPELLANT_HANDLE, THRUSTER_HANDLE, DOCKHANDLE, etc... function in the Vessel api.

How do I make this happen?


As always, any assistance is appreciated.
 

Urwumpe

Not funny anymore
Addon Developer
Donator
Joined
Feb 6, 2008
Messages
37,640
Reaction score
2,356
Points
203
Location
Wolfsburg
Preferred Pronouns
Sire
Just define some kind of unique code or "reference number" for every object in the vector, for example its index. And then let other objects use this "reference number" for communicating with your base class. Think of email communication with some government agency there... "In tax report ABCDE, your accountant FGHIK had calculated something wrong..."

It could be a pointer turned into a number, some kind of index, etc. Doesn't matter. A handle just makes the machine turn, but is not the machine.
 

kamaz

Unicorn hunter
Addon Developer
Joined
Mar 31, 2012
Messages
2,298
Reaction score
4
Points
0
I have a base class, one of it's members is a std::vector full of objects.

Full of objects or object pointers? I.e. std::vector<foo> or std::vector<foo*> ?

I want classes that are friends of, or derived from, my base class to be able to access those objects via handles similar to the way PROPELLANT_HANDLE, THRUSTER_HANDLE, DOCKHANDLE, etc... function in the Vessel api.

See this post:

http://www.orbiter-forum.com/showthread.php?p=454681&postcount=16

if you have any questions, feel free to ask.
 

Hlynkacg

Aspiring rocket scientist
Addon Developer
Tutorial Publisher
Donator
Joined
Dec 27, 2010
Messages
1,870
Reaction score
3
Points
0
Location
San Diego
Full of objects or object pointers? I.e. std::vector<foo> or std::vector<foo*> ?

At the moment it is in the form of "std::vector<foo>" but it is not to late to change.

I'm trying to build a generalized (drop-in) framework for power and other resource management. There's a bunch of "house-keeping" functions that need to be run on each element that have been declared. Ideally i'd like to keep those housekeeping functions confined to the base class and hidden from the end user.

---------- Post added at 14:09 ---------- Previous post was at 13:42 ----------

My current format for creating a element looks something like this and it seems to work...

Code:
FOO_HANDLE MyClass::CreateFoo (double x, double  y, double z)
{
	FOO foo;

	foo.x_value = x;
	foo.y_value = y;
	foo.z_value = z;
	
	foo_list.push_back (foo);

	return (FOO_HANDLE) &foo_list.back ();
}

I suppose my biggest question is how do I get other functions to accept FOO_HANDLE as an argument in place of FOO.

For instance...

Code:
void MyClass::SetFooXvalue (FOO_HANDLE fh, double x)
{
   //*Something mysterious happens*

   Foo.x_value = x;
}

What is the mysterious something?
 
Last edited:

jedidia

shoemaker without legs
Addon Developer
Joined
Mar 19, 2008
Messages
10,884
Reaction score
2,139
Points
203
Location
between the planets
At the moment it is in the form of "std::vector<foo>" but it is not to late to change.

I would highly recommend to change it. The memory addresses of vector elements is not consistent. I.e. if the vector actually contains the objects, you can run in trouble with invalid pointers to those objects. Also, a vector with the whole objects as elements can become really, really slow.

As for the handle thing:
a handle, similar to a pointer, contains the memory address to the object. But unlike a pointer, a handle is not of the same type as the object. Rather, It can be converted to a pointer of the object type by the function that receives it. The whole trick of handles is simply that you can pass the memory address of an object around without having to worry that anyone has access to the actual object (which they would have if you'd hand them a pointer).
As such, if you're not making an API and can trust yourself enough to not screw things up, you might as well just use pointers. At least that's my (probably misguided) oppinion on the matter.
 

Hlynkacg

Aspiring rocket scientist
Addon Developer
Tutorial Publisher
Donator
Joined
Dec 27, 2010
Messages
1,870
Reaction score
3
Points
0
Location
San Diego
I would highly recommend to change it. The memory addresses of vector elements is not consistent. I.e. if the vector actually contains the objects, you can run in trouble with invalid pointers to those objects. Also, a vector with the whole objects as elements can become really, really slow.

Ok, so aside from adding an astrix to the vector's definition what do I need to do?


As such, if you're not making an API and can trust yourself enough to not screw things up, you might as well just use pointers. At least that's my (probably misguided) oppinion on the matter.

Trouble is that I am making an API. Not sure if it will ever be released but at this point i'm trying to treat every thing as if it will.

---------- Post added at 16:39 ---------- Previous post was at 16:25 ----------

Note: Thanks to Kamaz's thread I've got the basic framework up and working.

---------- Post added at 21:09 ---------- Previous post was at 16:39 ----------

. The memory addresses of vector elements is not consistent. I.e. if the vector actually contains the objects, you can run in trouble with invalid pointers to those objects.

I think I hay have run into this problem, i'm gettig odd behavior on shut down and i suspect a memory leak or some other form of "undefined" behavior.
 

jedidia

shoemaker without legs
Addon Developer
Joined
Mar 19, 2008
Messages
10,884
Reaction score
2,139
Points
203
Location
between the planets
Ok, so aside from adding an astrix to the vector's definition what do I need to do?

Simple, define the elements with the new statement so they're on the heap instead of on the stack, like so:

Code:
vector<myType*> elements;
elements.push_back(new myType);

Now, whenever you get an element from the vector, you don't receive the element itself, but a pointer to it. Not only is this much faster for any kind of class with only a little complexity, but these pointers will remain valid until you free the memory again. The memory address the pointers are located at might still change, but that's not usually what you care about.

Of course, you'll have to clear up the elements manually at exit, like so:


Code:
for (UINT i = 0; i < elements.size(); ++i)
{
        delete elements[i];
}
elements.clear();
 
Last edited:

meson800

Addon Developer
Addon Developer
Donator
Joined
Aug 6, 2011
Messages
405
Reaction score
2
Points
18
Trouble is that I am making an API. Not sure if it will ever be released but at this point i'm trying to treat every thing as if it will.

If the API is eventually going to be released, you could just use handles to prevent addon-breaking API changes.

You could still use the framework already set up to use pointers, just cast them to void * before returning them to other addons, like so:
Code:
[COLOR="Red"]typedef void* FOO_HANDLE;[/COLOR]
FOO_HANDLE MyClass::CreateFoo (double x, double  y, double z)
{
	FOO foo;

	foo.x_value = x;
	foo.y_value = y;
	foo.z_value = z;
	
	foo_list.push_back (foo);

	return ((FOO_HANDLE) &foo_list.back ());
}

However, this could cause issues with:
I suppose my biggest question is how do I get other functions to accept FOO_HANDLE as an argument in place of FOO.

For instance...

Code:
void MyClass::SetFooXvalue (FOO_HANDLE fh, double x)
{
   //*Something mysterious happens*

   Foo.x_value = x;
}

In a handle-based API, the "users" of the handles (the classes that can access the API, but not the classes that are accessed by the handles), can never directly access the classes used. If you use handles, you have to define your own API functions that take the handle, convert it to the correct class and operate on the object.

For example:
Code:
static void API::SetFooXvalue (FOO_HANDLE fh, double x)
{
   ((foo*)fh)->x_value = x;
}

Then, in a consumer class:
Code:
void MyClass::doCoolStuff(FOO_HANDLE fh)
{
  //let's set the x value to pi, for fun
  API::SetFooXValue(fh,3.14159);
}

Users of the API never touch the objects, they always use API "helper" functions.

Although this means that you do have to write all the API object mutator functions, it does allow you to fully change the implementation of said functions without breaking any addons that depend on it.
 

Hlynkacg

Aspiring rocket scientist
Addon Developer
Tutorial Publisher
Donator
Joined
Dec 27, 2010
Messages
1,870
Reaction score
3
Points
0
Location
San Diego
Simple, define the elements with the new statement so they're on the heap instead of on the stack, like so:

Code:
vector<myType*> elements;
elements.push_back(new myType);

Now, whenever you get an element from the vector, you don't receive the element itself, but a pointer to it. Not only is this much faster for any kind of class with only a little complexity, but these pointers will remain valid until you free the memory again. The memory address the pointers are located at might still change, but that's not usually what you care about.

In that case my "create" function would look like something like this correct?..

Code:
FOO_HANDLE MyClass::CreateFoo (double x, double  y, double z)
{
	int idx = foo_list.size();
	foo_list.push_back (new FOO);

	foo_list[idx]->x_value = x;
	foo_list[idx]->y_value = y;
	foo_list[idx]->z_value = z;
	
	return (FOO_HANDLE) &foo_list[idx];
}

What should my manipulation functions look like?

at the moment they look like this, as per kamaz's example...

Code:
void MyClass::SetFooX (FOO_HANDLE fh, double x)
{
	FOO *target =  (FOO*) fh;

	target->x_value = x;
}

However, I get a "Unhandled exception" and a "Access violation reading location" error when I try to actually use one.
 
Last edited:

meson800

Addon Developer
Addon Developer
Donator
Joined
Aug 6, 2011
Messages
405
Reaction score
2
Points
18
What is the type of FOO_HANDLE?

Also, if you just want to use pointers, don't bother casting it to type FOO_HANDLE.
Code:
[COLOR="Red"]FOO*[/COLOR] MyClass::CreateFoo (double x, double  y, double z)
{
	int idx = foo_list.size();
	foo_list.push_back (new FOO);

	foo_list[idx]->x_value = x;
	foo_list[idx]->y_value = y;
	foo_list[idx]->z_value = z;
	
	return [COLOR="Red"]&(foo_list[idx])[/COLOR];
}
Code:
void MyClass::SetFooX ([COLOR="Red"]FOO*[/COLOR] fh, double x)
{
	FOO *target = [COLOR="Red"] fh;[/COLOR]

	target->x_value = x;
}
 

Hlynkacg

Aspiring rocket scientist
Addon Developer
Tutorial Publisher
Donator
Joined
Dec 27, 2010
Messages
1,870
Reaction score
3
Points
0
Location
San Diego
FOO_HANDLE is a typedef void as noted in previous example.
 

meson800

Addon Developer
Addon Developer
Donator
Joined
Aug 6, 2011
Messages
405
Reaction score
2
Points
18
I know why it's failing.
Sorry for giving bad example code :facepalm:

The issue is with
Code:
&(foo_list[idx])
The member at foo_list[idx] is a FOO*, so referencing that value gives us a pointer to a pointer- a FOO**

The FOO** can therefore not be casted into a FOO*.

Don't bother referencing again, just cast the pointer. And watch the parenthesis on the cast-order of operations can get weird if you don't include another set.
Code:
FOO_HANDLE MyClass::CreateFoo (double x, double  y, double z)
{
	int idx = foo_list.size();
	foo_list.push_back (new FOO);

	foo_list[idx]->x_value = x;
	foo_list[idx]->y_value = y;
	foo_list[idx]->z_value = z;
	
	return [COLOR="Red"](FOO_HANDLE)(foo_list[idx])[/COLOR];
}
 

Hlynkacg

Aspiring rocket scientist
Addon Developer
Tutorial Publisher
Donator
Joined
Dec 27, 2010
Messages
1,870
Reaction score
3
Points
0
Location
San Diego
Yup that fixed it. :thumbup:

I know I've asked this before but i'm drawing a blank.

Is there a quick/efficient way to check to see if a particular slot in a vector actually contains an object?

in pseudo code...
Code:
if foo_list[x] contains a FOO, return true;
else return false;

I want to guard against user attempts to manipulate an element that is not there.
 

Linguofreak

Well-known member
Joined
May 10, 2008
Messages
5,034
Reaction score
1,273
Points
188
Location
Dallas, TX
I suppose my biggest question is how do I get other functions to accept FOO_HANDLE as an argument in place of FOO.

For instance...

Code:
void MyClass::SetFooXvalue (FOO_HANDLE fh, double x)
{
   //*Something mysterious happens*

   Foo.x_value = x;
}

What is the mysterious something?

The mysterious something is whatever you define it to be. The entire point of a handle is that, from the perspective of code calling your API, the mapping from handle to object *is* a mysterious something, so that that code can't just manipulate your objects itself, but has to use your API functions. A handle is just an ID number. How that ID number maps to an object is up to you and the requirements of your design. It can change between API versions. As long as each handle continues to map to the same object, the method of mapping handles to objects could conceivably even change at runtime (doing this would make a fun programming exercise, but I can't imagine any sane reason to do it in code that has any purpose other than showing off).

I've heard of systems where handles were double pointers, incremented pointers, and just plain pointers. I've heard of systems where the mapping method changed from one to the other of the above between API versions. You don't need to limit yourself to any of these.
 

meson800

Addon Developer
Addon Developer
Donator
Joined
Aug 6, 2011
Messages
405
Reaction score
2
Points
18
Is there a quick/efficient way to check to see if a particular slot in a vector actually contains an object?
Use std::find
Code:
#include <algorithm>
if(std::find(foo_list.begin(), foo_list.end(), x) != v.end())
{
  //foo_list contains x
}
else
{
  //foo_list doesn't contain x
}

If you use that with the handles, it actually safeguards against crashes when any user of the API passes in any invalid pointer.
 
Top