views:

121

answers:

6

The foolowing reduced code sample does not do anything usefull but two subsequent assignments to a data member pointer. The first assignement works, the second one gives a compiler error. Presumably because its to a nested member.

Question would be: Is it really just not possible to let a member pointer point to a nested member or am I missing any fancy syntax there?

struct Color {
    float Red;
    float Green;
    float Blue; };


struct Material {
    float Brightness;
    Color DiffuseColor; };


int main() {
    float Material::* ParamToAnimate;
    ParamToAnimate = &Material::Brightness;       // Ok
    ParamToAnimate = &Material::DiffuseColor.Red; // Error! *whimper*
    return 0; }

ATM I am working around by using byte offsets and a lot of casts. But that is ugly, I would better like to use those member pointers.

Yes, I know that question surely arised before (like nearly any question). Yes, I searched beforehand but found no satisfying answer.

Thanks for your time.

+4  A: 

I assume you are trying to get the pointer to the datamember Red. Since this is defined in the struct Color the type of the pointer is Color::*. Hence your code should be:

int main() {
    float Color::* ParamToAnimate;
    ParamToAnimate = &Color::Red; 
    return 0; }

To use it, you need to bind it to an instance of Color for example:

void f(Color* p, float Color::* pParam)
{
    p->*pParam = 10.0;
}
int main() {
    float Color::* ParamToAnimate;
    ParamToAnimate = &Color::Red; 

    Material m;
    f(&m.DiffuseColor, ParamToAnimate);
    return 0;
}

EDIT: Is it not possible to make the animation function a template? For example:

template<class T>
void f(T* p, float T::* pParam)
{
    p->*pParam = 10.0;
}
int main() {

    Material m;

    f(&m.DiffuseColor, &Color::Red);
    f(&m, &Material::Brightness);
    return 0;
}
Naveen
This has a big problem, that you can't animate brightness with this architecture.
jpalecek
@jpalecek: Yes you are right. I was concentrating more on syntax.
Naveen
Uhm, yes, but using different pointers would render the whole thing pointless. I want a single pointer that stores which float in material (or its nested members) has to be animated. And in reality I have of course even more nested members there in material. Theoretically it should be possible. My solution with the byte offsets and a lot of casts works. Its just a syntax thing.
kaptnole
@kaptnole: Updated answer. Not sure though..
Naveen
And what would the stored member pointer now look like? I would still need several of them, right?
kaptnole
The templated method doesn't work - he needs to be able to get the float from an instance of `Material`, regardless of whether the `float` is a member of `Material` or of `Color`.
Joe Gauterin
+2  A: 

Basically you're trying to get a pointer to a float variable that you can animate. Why not use float*. The issue you're having there is that Brightness is a member of Material, however, Red is a member of Color and not Material, to the compiler. Using float* should solve your problem.

Vite Falcon
A simple float pointer would be an absolute pointer to a single memory location. It could not be used on several material objects and would become invalid if material changes its memory location.
kaptnole
As far as I understand, a pointer will always be invalid if material changes it's memory location. No pointer follows the change in memory location.
Vite Falcon
Member pointers do follow memory locations! They are only relative offsets into an object. You must specify an additional instance to access them.
kaptnole
@Vite: You don't understand member pointers.
Joe Gauterin
+1  A: 

AFAIK, this is not possible. A pointer-to-member can only be formed by an expression of type &qualified_id, which is not your case.

Vite Falcon's solution is probably the most appropriate.

jpalecek
I am also afraid it is just not possible. Maybe I have to stick with my byte offset solution. Using absolute float pointers would not be the same.
kaptnole
Although I dislike the reference to Falcon, your Answer is probably the right one. Its not possible - sadly.
kaptnole
+2  A: 

Instead of a member pointer, you can use a functor that returns a float* when given an instance of Material; change the type of ParamToAnimate to something like:

std::function<float*(Material&)>

On the plus side, it's portable - but on the downside, it requires a significant amount of boilerplate code and has significant runtime overhead.

If this is performance critical, I'd be tempted to stick with the offset method.

Joe Gauterin
Got your idea, but yes, it is performance critical. I am working on a realtime 3D Engine.
kaptnole
Then the offset hack/method is probably better.
Joe Gauterin
A: 

You could simply refactor such that you don't have the nested structure at all. Add a setter than unpacks the color into its component parts so that existing code need not change much, and go from there.

You could also take an optional second pointer that digs into the nested type. A single test to see if you need the second parameter may prove good enough compared to your current method, and would be more easily extended should additional fields turn up later.

Take that a step further, and you have a base MaterialPointer class with a virtual Dereference method. The case class can handle simple members, with derived classes handling nested members with whatever additional information they need to find them. A factory can then produce MaterialMember* objects of the appropriate type. Of course, now you're stuck with heap allocations, so this is likely a little too far to be practical.

Dennis Zickefoose
All those are possible alternatives. But they also are more complicated and/or less performant than my existing solution with byte offsets and casts.
kaptnole
A: 

Since at some point you need a pointer to the actual data, this may or may not work for you:

float Material::* ParamToAnimate;
ParamToAnimate = &Material::Brightness;       // Ok
float Color::* Param2;
Param2 = &Color::Red; 

Material mat;
mat.Brightness = 1.23f;
mat.DiffuseColor.Blue = 1.0f;
mat.DiffuseColor.Green = 2.0f;
mat.DiffuseColor.Red = 3.0f;

float f = mat.DiffuseColor.*Param2;
John Dibling
Yes, thats another pointer with a different type. Would not help to make the whole thing more easy and elegant.
kaptnole