Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nested data member pointer - not possible?

The following reduced code sample does not do anything useful but two subsequent assignments to a data member pointer. The first assignment 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.

like image 450
Ole Dittmann Avatar asked Aug 09 '10 10:08

Ole Dittmann


4 Answers

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.

like image 92
jpalecek Avatar answered Oct 29 '22 09:10

jpalecek


It's not possible. But there is a workaround very close to what you want to achieve. It involves putting the nested member into an union alongside with a "layout-compatible" anonymous struct. The downside is a bit bloated interface and the need of keeping definitions of sibling structs in sync.

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

struct Material {
    float Brightness;
    union {
        struct { // "Layout-compatible" with 'Color' (see citation below)
            float DiffuseColorRed;
            float DiffuseColorGreen;
            float DiffuseColorBlue; };
        Color DiffuseColor; }; };

int main() {
    Material M;

    float Material::* ParamToAnimate;
    ParamToAnimate = &Material::DiffuseColorRed;
    std::cin >> M.*ParamToAnimate;
    std::cout << M.DiffuseColor.Red << std::endl;
    return 0; }

ISO IEC 14882-2003 (c++03):

§3.9

11

If two types T1 and T2 are the same type, then T1 and T2 are layout-compatible types. [Note: Layout-compatible enumerations are described in 7.2. Layout-compatible POD-structs and POD-unions are described in 9.2. ]

§9.2

16

If a POD-union contains two or more POD-structs that share a common initial sequence, and if the POD-union object currently contains one of these POD-structs, it is permitted to inspect the common initial part of any of them. Two POD-structs share a common initial sequence if corresponding members have layout-compatible types (and, for bit-fields, the same widths) for a sequence of one or more initial members.

Multiple nesting is possible too:

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

struct Material {
    float Brightness;
    Color DiffuseColor; };

struct Wall {
    union {
        struct {
            float SurfaceBrightness;
            struct {
                float SurfaceDiffuseColorRed;
                float SurfaceDiffuseColorGreen;
                float SurfaceDiffuseColorBlue; }; };
        Material Surface; }; };

int main() {
    Wall W;

    float Wall::* ParamToAnimate;
    ParamToAnimate = &Wall::SurfaceDiffuseColorRed;
    std::cin >> W.*ParamToAnimate;
    std::cout << W.Surface.DiffuseColor.Red << std::endl;
    return 0; }

§9.2

14

Two POD-struct (clause 9) types are layout-compatible if they have the same number of nonstatic data members, and corresponding nonstatic data members (in order) have layout-compatible types (3.9).

like image 34
anonymous Avatar answered Sep 25 '22 13:09

anonymous


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;
}
like image 5
Naveen Avatar answered Oct 29 '22 09:10

Naveen


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.

like image 3
JoeG Avatar answered Oct 29 '22 10:10

JoeG