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.
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.
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).
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;
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With