2010-08-09 4 views
9

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

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

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 Я работаю с использованием байтовых смещений и множества отливок. Но это уродливо, мне бы хотелось использовать эти указатели.

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

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

+0

jpalecek правильный; ответ на ваш вопрос - нет. Но то, что вы действительно ищете, - это решение проблемы. При незначительной реструктуризации ваших данных вы можете найти тип, который указывает на все четыре поплавка. (См. Ниже). –

ответ

4

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

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

+0

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

+0

Хотя мне не нравится ссылка на Falcon, ваш ответ, вероятно, правильный. Его невозможно - грустно. –

5

Я предполагаю, что вы пытаетесь получить указатель на datamember 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; 
} 

EDIT: Это не возможно сделать функцию анимации шаблон? Например:

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

У вас большая проблема, что вы не можете оживить яркость с помощью этой архитектуры. – jpalecek

+0

@jpalecek: Да, вы правы. Я больше концентрировался на синтаксисе. – Naveen

+0

Ум, да, но с использованием разных указателей все это бессмысленно. Я хочу, чтобы один указатель, который хранит, который плавает в материале (или его вложенных членах), должен быть анимирован. И на самом деле у меня, конечно, еще больше вложенных членов в материал. Теоретически это должно быть возможно. Мое решение с байтовыми смещениями и множеством отливок. Это просто синтаксис. –

2

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

+0

Простым указателем на float будет абсолютный указатель на единую ячейку памяти. Он не может использоваться на нескольких материальных объектах и ​​станет недействительным, если материал изменит местоположение памяти. –

+1

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

+2

Указатели указателей размещаются в памяти! Это только относительные смещения в объект. Вы должны указать дополнительный экземпляр для доступа к ним. –

2

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

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

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

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

+0

Получил вашу идею, но да, это критическая производительность. Я работаю над 3D-движком в реальном времени. –

+1

Тогда смещение/метод, вероятно, лучше. –

0

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

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

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

+0

Все это возможные альтернативы. Но они также более сложны и/или менее эффективны, чем мое существующее решение с байтовыми смещениями и отбрасываниями. –

0

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

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

Да, это другой указатель с другим типом. Не помогло бы сделать все проще и элегантнее. –

0

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

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