Jump to content
  • Advertisement
Sign in to follow this  
ChaosWars

Passing an array of pointers to objects to a function

This topic is 3644 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I have a rather odd problem. I am trying to pass an array of pointers to objects to a function, which initializes and fills the array. It then returns the number of pointers to objects in the array. The code looks something like this:
class Material(){..}

class MaterialLoader(){
...
LoadMaterial(const char *path, Material *materials[] ){...}
}
The wierd thing is, when the LoadMaterial function is called from another class like this:
Material **materials = NULL;
MaterialLoader ml;
int num_materials = ml.LoadMaterial(path, materials);
printf("adress of materials is %x\n", materials);
the printf points to 0! The array is getting filled within the function, debugging the application as well as printf statements inside LoadMaterial confirm this. It is as though a copy is being made of materials, and that this is being passed to the function, but that's not possible, is it? And yet it must be, since that seems to be what is happening. Can someone shed some light on this? If you want to compile the code, it can be obtained from http://code.google.com/p/mll/ Thanks in advance.

Share this post


Link to post
Share on other sites
Advertisement
Your function

LoadMaterial(const char *path, Material *materials[] ){...}

is incorrect. With this implementation C++ is thinking the array materials[] already exists somewhere- which is incorrect.

It should be


int LoadMaterial(const char *path, Material ***materials)
{
 int number_of_materials = CountMaterials(path); // <-- helper function

 // Create the array (I think this syntax is correct)
 *materials = new Matirial *[number_of_materials];

 // Do normal loading
 ...

 // Return the count as specified in the prototype
 return number_of_materials;
}


-Linolium


**EDIT** Corrected pointer parameter. I knew something didn't seem right in that code segment. Thanks rip-off for the reminder ;)

Basically you would call the function like:
ml.LoadMaterial(path, &materials);

Share this post


Link to post
Share on other sites
The pointer argument is a copy of the pointer passed to it. Changing the value of the "materials" argument in LoadMaterial() has no effect on the value of the variable "materials" in the other class.

One way would be to return a standard library container (vector) of (smart) material pointers:

#include <vector>

class MaterialLoader
{
std::vector<Material*> LoadMaterial(const std::string &path);
};



Alternatively, you could pass the vector by reference.

#include <vector>

class MaterialLoader
{
void LoadMaterial(const std::string &path, std::vector<Material*> &materials);
};



You could have returned the pointer in your code (but then you still have to do more manual memory management, and you have no size information):

class MaterialLoader
{
Material **LoadMaterial(const std::string &path);
};



The last solution (and IMO the worst choice) is to pass the pointer by reference. References to pointers are a little ugly syntax wise, but a typedef can help:

class MaterialLoader
{
typedef Material **ArrayOfMaterialPointers;
LoadMaterial(const std::string &path, ArrayOfMaterialPointers &materials);
};



The last is really only shown for completeness and to demonstrate the references to pointers is a possible, if not always optimal, solution.

You code is very like this:

void foo( int *arg_ptr )
{
delete arg_ptr;
arg_ptr = 0;
}

int main()
{
int *main_ptr = new int(42);
foo(main_ptr);
// main_ptr was passed by value.
// the value of arg_ptr before it went out of scope is 0
// these changes are not made to main_ptr, it is now an invalid pointer
}


Share this post


Link to post
Share on other sites
Quote:
Original post by Linolium
Your function

LoadMaterial(const char *path, Material *materials[] ){...}

is incorrect. With this implementation C++ is thinking the array materials[] already exists somewhere- which is incorrect.

It should be


int LoadMaterial(const char *path, Material ***materials)
{
 int number_of_materials = CountMaterials(path); // <-- helper function

 // Create the array (I think this syntax is correct)
 *materials = new Matirial *[number_of_materials];

 // Do normal loading
 ...

 // Return the count as specified in the prototype
 return number_of_materials;
}


-Linolium


**EDIT** Corrected pointer parameter. I knew something didn't seem right in that code segment. Thanks rip-off for the reminder ;)

Basically you would call the function like:
ml.LoadMaterial(path, &materials);


I'm afraid this is incorrect. Actually, this was how I had it originally. It makes no difference to my problem. I was allocating the variable in the way you suggested by the way:

materials = new Material *[num_materials];

Share this post


Link to post
Share on other sites
yes

int LoadMaterial(const char *path, Material **materials)
{
 materials = new Material *[num_materials];
}

is incorrect. The tripple pointer is key here. Without it your allocated address will be lost.

-Linolium

Share this post


Link to post
Share on other sites
Quote:
Original post by rip-off
The pointer argument is a copy of the pointer passed to it. Changing the value of the "materials" argument in LoadMaterial() has no effect on the value of the variable "materials" in the other class.

One way would be to return a standard library container (vector) of (smart) material pointers:
*** Source Snippet Removed ***

Alternatively, you could pass the vector by reference.
*** Source Snippet Removed ***

You could have returned the pointer in your code (but then you still have to do more manual memory management, and you have no size information):
*** Source Snippet Removed ***

The last solution (and IMO the worst choice) is to pass the pointer by reference. References to pointers are a little ugly syntax wise, but a typedef can help:
*** Source Snippet Removed ***

The last is really only shown for completeness and to demonstrate the references to pointers is a possible, if not always optimal, solution.

You code is very like this:
*** Source Snippet Removed ***


I was hoping to avoid using vector, since I want the data to be aligned, ready to pass to VBO's and the like. I would like to get the code working as it is, although if it isn't possible then so be it. It seems like the typedef solution may work, although I jsut tried it in this fashion (trying to understand how it works)

LoadMaterial(const char *path, Material &(**materials) )


and nmake complained that is was illegal

materialloader.h(42) : error C2528: 'materials' : pointer to reference is illegal

Share this post


Link to post
Share on other sites
Quote:
The array is getting filled within the function,
Uh, what array? All I see is a Material**. So should I assume LoadMaterial allocates the array?

EDIT: Ahh, my post is so late, this is what I get for letting a post sit forever in a firefox tab.

Share this post


Link to post
Share on other sites
Quote:
Original post by Linolium
yes

int LoadMaterial(const char *path, Material **materials)
{
 materials = new Material *[num_materials];
}

is incorrect. The tripple pointer is key here. Without it your allocated address will be lost.

-Linolium


Yes, thank you very much. This is working - at least, it is comiling :p however, in order to check it, I need some more help :p

I had some other functions a bit later on, for example:

materials[cur_material]->SetAmbientColor( ambient_red, ambient_green, ambient_blue );

Now, the code:

*materials[cur_material] = new Material( name, const_cast< char* >( diffuse_map ) );
is compiling, but the code:

*materials[cur_material]->SetAmbientColor( ambient_red, ambient_green, ambient_blue );


fails with the error:

objmaterialloader.cpp(141) : error C2227: left of '->SetAmbientColor' must point to class/struct/union/generic type
1> type is 'mll::Material **'


how can I now access these functions?

Share this post


Link to post
Share on other sites
the thing I would do normally is create stepper and accessor variables so member data can be accessed more cleanly w/o the risk of loosing the initial pointer. So adding on to my inital code:

int LoadMaterial(const char *path, Material ***materials)
{
 // Get the total number of materials to add
 int number_of_materials = CountMaterials(path);

 // Create the array (I think this syntax is correct)
 *materials = new Matirial *[number_of_materials];

 // Do normal loading
 Material **MaterialStepper = *materials;
 for (int i=0; i<number_of_materials; ++i)
   {
    MaterialStepper = new new Material(name, const_cast< char* >(diffuse_map));

    // Temp accessor variable
    Material *CurrentMaterial = MaterialStepper;

    // Do you other stuff on the new object's data
    CurrentMaterial->SetAmbientColor(ambient_red, ambient_green, ambient_blue);
   }

 // Return the count as specified in the prototype
 return number_of_materials;
}


Or something similar.

-Linolium

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!