Указатель на вложенный элемент данных - не возможно? - PullRequest
9 голосов
/ 09 августа 2010

Следующий пример сокращенного кода не делает ничего полезного, а выполняет два последовательных присваивания указателю на элемент данных. Первое назначение работает, второе дает ошибку компилятора. Предположительно, потому что это вложенный член.

Вопрос будет таким: действительно ли просто невозможно позволить указателю члена указывать на вложенный элемент, или я там пропускаю какой-то причудливый синтаксис?

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

Банкомат. Я работаю, используя смещения байтов и много приведения. Но это ужасно, я бы лучше использовал эти указатели на членов.

Да, я знаю, что этот вопрос наверняка возникал раньше (как почти любой вопрос). Да, я искал заранее, но не нашел удовлетворительного ответа.

Спасибо за ваше время.

Ответы [ 8 ]

5 голосов
/ 09 августа 2010

Я полагаю, что вы пытаетесь получить указатель на датабер Red.Поскольку это определено в структуре Color, тип указателя - Color::*.Следовательно, ваш код должен быть:

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

Чтобы использовать его, вам необходимо привязать его к экземпляру Color, например:

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

РЕДАКТИРОВАТЬ : Нельзя ли сделать функцию анимации шаблоном?Например:

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;
}
4 голосов
/ 09 августа 2010

AFAIK, это невозможно.Указатель на член может быть сформирован только выражением типа &qualified_id, что не соответствует вашему случаю.

Решение Vite Falcon, вероятно, является наиболее подходящим.

3 голосов
/ 09 августа 2010

Вместо указателя на член можно использовать функтор, который возвращает float*, когда ему присваивается экземпляр Material;измените тип ParamToAnimate на что-то вроде:

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

С положительной стороны он переносной, но с другой стороны, он требует значительного количества стандартного кода и имеетнакладные расходы времени выполнения.

Если это критично для производительности, у меня возникнет соблазн придерживаться метода смещения.

2 голосов
/ 09 августа 2010

Обычно вы пытаетесь получить указатель на переменную типа float, которую вы можете анимировать. Почему бы не использовать float*. Проблема в том, что Brightness является элементом Material, однако Red является элементом Color, а не Material для компилятора. Использование float* должно решить вашу проблему.

1 голос
/ 12 мая 2019

Это невозможно. Но есть обходной путь, очень близкий к тому, чего вы хотите достичь. Это включает помещение вложенного члена в объединение вместе с «совместимой с макетом» анонимной структурой. Недостатком является немного раздутый интерфейс и необходимость синхронизации определений родственных структур.

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

ИСО МЭК 14882-2003 (c ++ 03):

§3.9

11

Если два типа T1 и T2 имеют одинаковый тип, то T1 и T2 совместимые с макетом типы. [Примечание: совместимые с компоновкой перечисления описано в 7.2. Совместимые с макетом POD-структуры и POD-объединения описано в 9.2. ]

§9.2

16

Если POD-объединение содержит две или более POD-структур, которые имеют общий начальная последовательность, и если объект объединения POD в настоящее время содержит один из этих POD-структур разрешается проверять общий начальный часть любого из них. Две POD-структуры имеют общую начальную последовательность если соответствующие члены имеют совместимые с макетом типы (и, для битовые поля одинаковой ширины) для последовательности из одного или нескольких начальных члены.

Возможно также многократное вложение:

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

Два типа POD-struct (раздел 9) совместимы с макетом, если они имеют такое же количество нестатических элементов данных и соответствующих нестатических элементы данных (по порядку) имеют совместимые с макетом типы (3.9).

0 голосов
/ 01 мая 2011

Как насчет наследования вместо композиции?

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; }
0 голосов
/ 09 августа 2010

Поскольку в какой-то момент вам нужен указатель на фактические данные, это может или не может работать для вас:

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;
0 голосов
/ 09 августа 2010

Вы можете просто сделать рефакторинг таким образом, чтобы у вас вообще не было вложенной структуры. Добавьте сеттер, а не распаковывает цвет в его составные части, чтобы существующий код не сильно изменился, и переходите оттуда.

Вы также можете взять необязательный второй указатель, который копается во вложенный тип. Один тест, чтобы увидеть, нужен ли вам второй параметр, может оказаться достаточно хорошим по сравнению с вашим текущим методом, и его будет легче расширить, если позже появятся дополнительные поля.

Сделайте еще один шаг, и у вас есть базовый класс MaterialPointer с виртуальным методом Dereference. Класс case может обрабатывать простые члены, а производные классы обрабатывают вложенные члены с любой дополнительной информацией, необходимой для их поиска. Затем фабрика может производить MaterialMember* объектов соответствующего типа. Конечно, теперь вы застряли с распределением кучи, так что это, вероятно, слишком далеко, чтобы быть практичным.

...