Nested data member pointer - not possible?

2019-01-19 14:20发布

问题:

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.

回答1:

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.



回答2:

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;
}


回答3:

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.



回答4:

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.



回答5:

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.



回答6:

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;


回答7:

How about inheritance instead of composition?

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

struct DiffuseColor : public Color {
    };

struct Material : public DiffuseColor {
    float Brightness; };


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