Ссылки + forward declaration
От: eremeer  
Дата: 25.09.07 17:34
Оценка:
Сутки добрые всем!
Продолжаю делать трудные шаги в мире C++. Назрело вот:

//p3f.h
struct P3f
{
float x, y, z;
};



//sample.h
struct P3f; //fwd decl

struct Sample
{
Sample();
P3f& pos;
};



//sample.cpp
#include "Sample.h"
#include "p3f.h"

Sample::Sample(): pos(P3f())
{
}

В VS7.1 подобная инициализация неконстантной ссылки (pos) компилируется, временный и безымянный P3f() существует в течение жизни ссылки. Нестандартное решение, но очень хочется — используя ссылки вместо указателей, иметь также все преимущества forward declaration. Вопрос: как это сделать стандартными методами, изящно и эффективно?
Re: Ссылки + forward declaration
От: AstroMan  
Дата: 26.09.07 04:41
Оценка:
Здравствуйте, eremeer, Вы писали:

E>
E>//sample.cpp
E>#include "Sample.h"
E>#include "p3f.h"

E>Sample::Sample(): pos(*new P3f())
E>{
E>}

Sample::~Sample()
{
   delete &pos;
}

E>


В твоем случае объект P3f() живет только до закрывающей скобки pos, т.е. это вообще не решение (даже нестандартное). Вообще-то случай похож на pimpl и напрашивается использование какого-нибудь смарт-указателя.
Re[2]: Ссылки + forward declaration
От: Аноним  
Дата: 26.09.07 06:02
Оценка: 1 (1)
Здравствуйте, AstroMan, Вы писали:

E>>
AM>Sample::~Sample()
AM>{
AM>   delete &pos;// За такую конструктцию нужно руки отрывать. Сопровождать это как?!
AM>}
E>>
Re: Ссылки + forward declaration
От: Кодт Россия  
Дата: 26.09.07 07:58
Оценка:
Здравствуйте, eremeer, Вы писали:

E>//p3f.h
E>struct P3f
E>{
E>float x, y, z;
E>};

E>struct Sample
E>{
E>Sample();
E>P3f& pos;
E>};

E>Sample::Sample(): pos(P3f())
E>{
E>}

E>В VS7.1 подобная инициализация неконстантной ссылки (pos) компилируется,

Да, это нестандартное расширение компиляторов VC.

E> временный и безымянный P3f() существует в течение жизни ссылки.


Ха! И интересно, где же этот безымянный объект существует?
На самом деле, продление жизни объектов по ссылкам действует только вот здесь:
void foo()
{
    // локальное хранилище!
    Some const /*для VC - необязательно*/ &ref = make_Some_or_something_derived(); // конструктор или функция
    .....
    .....
    .....
}

А во всех остальных случаях временный объект (размещённый, естественно, на стеке) живёт до выхода из полного выражения.
В общем, Sample::Sample() : pos(P3f()) оказался инициализирован мусором.
Тебе просто повезло, что этот участок стека никто не трогал.

E> Нестандартное решение, но очень хочется — используя ссылки вместо указателей, иметь также все преимущества forward declaration.


Чем плохи указатели?
Используй какой-нибудь умный указатель, терпимый к недоопределённым классам (auto_ptr — нетерпим, shared_ptr — подходит).

E> Вопрос: как это сделать стандартными методами, изящно и эффективно?


Твои критерии эффективности? Да и изящества тоже...
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re[2]: Ссылки + forward declaration
От: eremeer  
Дата: 26.09.07 15:29
Оценка:
Спасибо за ответ!
К>А во всех остальных случаях временный объект (размещённый, естественно, на стеке) живёт до выхода из полного выражения.
К>В общем, Sample::Sample() : pos(P3f()) оказался инициализирован мусором.
К>Тебе просто повезло, что этот участок стека никто не трогал.
Повезло, потому что такой код в наш рабочий проект не помещу по вполне понятным причинам. А в тестовом режиме не удалось вызвать ошибку с инициализацией в конструкторе. Наверное, плохо старался.

К>Чем плохи указатели?

К>Используй какой-нибудь умный указатель, терпимый к недоопределённым классам (auto_ptr — нетерпим, shared_ptr — подходит).
ну просто указатели плохи по тем причинам, что ты вспомнил умные, а также каждый раз, делая "p->x = ..." меня пробирает суеверная дрожь (а вдруг NULL?!). Умные недостаточно хороши некоторым усложнением (для меня) кода, ну и насколько могу представить, перегруженная "->" наверное, несколько снизит производительность (конечно, это критично далеко не всегда). Ну и еще, пожалуй, субъективно выражение

l = sqrtf(pos->x*pos->x + pos->y*pos->y + pos->z*pos->z);

мною воспринимается хуже, чем

l = sqrtf(pos.x*pos.x + pos.y*pos.y + pos.z*pos.z);


К>Твои критерии эффективности? Да и изящества тоже...


class Sample()
{
public:
Sample(): pos(0,0,0) {}

private:
P3f pos;
};

менее изящно
class Sample()
{
public:
Sample(): pos(new P3f(0,0,0)) {}

private:
P3f* pos;
};
//и менее эффективно, если
Sample* samples = new Sample[1024];
Re[3]: Ссылки + forward declaration
От: Кодт Россия  
Дата: 26.09.07 15:46
Оценка:
Здравствуйте, eremeer, Вы писали:

К>>Чем плохи указатели?

К>>Используй какой-нибудь умный указатель, терпимый к недоопределённым классам (auto_ptr — нетерпим, shared_ptr — подходит).
E>ну просто указатели плохи по тем причинам, что ты вспомнил умные, а также каждый раз, делая "p->x = ..." меня пробирает суеверная дрожь (а вдруг NULL?!). Умные недостаточно хороши некоторым усложнением (для меня) кода, ну и насколько могу представить, перегруженная "->" наверное, несколько снизит производительность (конечно, это критично далеко не всегда). Ну и еще, пожалуй, субъективно выражение
E>

l = sqrtf(pos->x*pos->x + pos->y*pos->y + pos->z*pos->z);

мною воспринимается хуже, чем

l = sqrtf(pos.x*pos.x + pos.y*pos.y + pos.z*pos.z);


Увы, в С++ нет ни перегрузки оператора ., ни миксинов — поэтому либо ->, либо ().
class Sample
{
    P3f const& pt() const { return *pt_; }
    int d() const { return pt().x*pt().x + ..... };
};


К>>Твои критерии эффективности? Да и изящества тоже...


E>
E>class Sample()
E>{
E>public:
E>Sample(): pos(0,0,0) {}

E>private:
E>P3f pos;
E>};
E>

E>менее изящно
E>
E>class Sample()
E>{
E>public:
E>Sample(): pos(new P3f(0,0,0)) {}

E>private:
E>P3f* pos;
E>};
E>//и менее эффективно, если
E>Sample* samples = new Sample[1024];
E>


Тогда самым изящным и эффективным будет поместить туда объект полностью определённого типа (т.е. первый вариант).
Какие есть препятствия к этому?
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re[3]: Ссылки + forward declaration
От: Max M. Ниоткуда  
Дата: 26.09.07 15:47
Оценка:
Здравствуйте, eremeer, Вы писали:

К>>Твои критерии эффективности? Да и изящества тоже...


E>//и менее эффективно, если

E>
E>class Sample()
E>{
E>public:
E>Sample(): pos(0,0,0) {}

E>private:
E>P3f pos;
E>};
E>


P3f хранит три инта? тогда то что выше и есть самый эффективный и изящный вариант.
Зачем тут вообще new и вся вытекающая муть?
Уж такую банальность компилятор соптимизурует в лучшем виде.
И даже если не соптимизирует — копирование трех интов (да хоть сорока даблов) на порядки бестрее new...
// ...
подпись
Re[2]: Ссылки + forward declaration
От: eremeer  
Дата: 26.09.07 15:59
Оценка:
Спасибо за ответ!
E>>
Sample::Sample(): pos(*new P3f())
{
}
Sample::~Sample()
{
   delete &pos;
}

страшненько выглядит особенно деструктор. Наверное, так и придется на указателях телегу тянуть...

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

Ага, принял это за рабочее решение, когда смотрел, в какое время вызываются деструкторы, надо было еще на значения посмотреть.
Re[4]: Ссылки + forward declaration
От: eremeer  
Дата: 26.09.07 16:10
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Тогда самым изящным и эффективным будет поместить туда объект полностью определённого типа (т.е. первый вариант).

К>Какие есть препятствия к этому?

структура, наподобие указанной, хранится в здоровенном (около 200кб) заголовочном файле сторонней библиотеки. Мой заголовочный в десятке cpp подключается. В итоге проект на 20 секунд медленнее компилируется (при полной сборке). В проекте сотни три файлов: тут 20 сек, там 30 и получаем вместо 5 минут компиляции, например, 10.
P.S. Люди, собирающие проекты сутками, будут, наверное, смеяться над такими проблемами.
Re[4]: Ссылки + forward declaration
От: eremeer  
Дата: 26.09.07 16:20
Оценка:
Здравствуйте, Max M., Вы писали:
MM>P3f хранит три инта? тогда то что выше и есть самый эффективный и изящный вариант.
MM>Зачем тут вообще new и вся вытекающая муть?
MM>Уж такую банальность компилятор соптимизурует в лучшем виде.
MM>И даже если не соптимизирует — копирование трех интов (да хоть сорока даблов) на порядки бестрее new...

new это ответ по поводу эффективности и изящности (вопрос здесь)
Автор: Кодт
Дата: 26.09.07
. Т.е. если использовать предварительное объявление P3f, то, скорее всего, придется на указателях, через new делать, а это неэффективно. У меня мало опыта, поэтому подумал: а вдруг есть другой, волшебный способ?
Re[5]: Ссылки + forward declaration
От: Max M. Ниоткуда  
Дата: 26.09.07 17:05
Оценка:
Здравствуйте, eremeer, Вы писали:

E>Здравствуйте, Max M., Вы писали:

MM>>P3f хранит три инта? тогда то что выше и есть самый эффективный и изящный вариант.
E>new это ответ по поводу эффективности и изящности

А, извини — да (пока до конца дочитал содержание первого поста из головы выветрилось ).
Ну а с precompiled headers экспериментировать не пробовал? — все же есть тут что-то совсем мрачное
— из-за пяти минут времени компиляции жертовать простотой и читабельностью кода...
(эффективность уж ладно — можно прикрыть глаза)
// ...
подпись
Re[6]: Ссылки + forward declaration
От: eremeer  
Дата: 26.09.07 18:12
Оценка:
Здравствуйте, Max M., Вы писали:

MM>Ну а с precompiled headers экспериментировать не пробовал? — все же есть тут что-то совсем мрачное

precompiled? где-то выше специально упомянул про полный билд а его приходится иногда делать — слетает отладчик в debug-версии проекта, помогает только полный ребилд (говорят, еще может помочь хирург — выправить руки .

MM>- из-за пяти минут времени компиляции жертовать простотой и читабельностью кода...

MM>(эффективность уж ладно — можно прикрыть глаза)
когда сидишь в дебаге — 5 или 10 — разница все-таки есть. (хотя, после посещения хирурга, возможно и не придется корчиться в дебаге...)
Re[5]: Ссылки + forward declaration
От: AstroMan  
Дата: 26.09.07 18:46
Оценка:
Здравствуйте, eremeer, Вы писали:

E> Т.е. если использовать предварительное объявление P3f, то, скорее всего, придется на указателях, через new делать, а это неэффективно. У меня мало опыта, поэтому подумал: а вдруг есть другой, волшебный способ?


Волшебного способа нет. Или компилятор знает размер P3f при компиляции Sample, или нет — тогда только new.
Re[3]: Ссылки + forward declaration
От: AstroMan  
Дата: 26.09.07 18:49
Оценка:
Здравствуйте, Аноним, Вы писали:

E>>>
AM>>Sample::~Sample()
AM>>{
AM>>   delete &pos;// За такую конструктцию нужно руки отрывать. Сопровождать это как?!
AM>>}
E>>>


Я бы сам руки оторвал (и не только руки), если б увидел в проекте. Но вопрос был чисто теоретический. Ну хотелось человеку ссылок и forwarda
Re[3]: Ссылки + forward declaration
От: AstroMan  
Дата: 26.09.07 18:59
Оценка:
Здравствуйте, eremeer, Вы писали:

E>страшненько выглядит особенно деструктор. Наверное, так и придется на указателях телегу тянуть...


Я НЕ говорил, что так НАДО делать.

В данном случае поведение T& будет весьма близко к T* const, а выставлять член данных в public тоже как-то не комильфо. Хотя согласен, что писать p.x приятнее, p->x и p.x(). Для простых типов из трех int с простой как три копейки семантикой имхо разумнее использовать POD-структуру и не париться, на время компиляции ее полное определение не повлияет.
Re[4]: Ссылки + forward declaration
От: eremeer  
Дата: 26.09.07 19:01
Оценка: :)
Здравствуйте, AstroMan, Вы писали:

AM>Я бы сам руки оторвал (и не только руки), если б увидел в проекте. Но вопрос был чисто теоретический. Ну хотелось человеку ссылок и forwarda


пока есть руки, могу написать такое: (тоже теоретически)
//sample.h
struct P3f;
struct P3fForward
{
  //здесь копия данных P3f
};

//STATIC_ASSERT(sizeof(P3f) <= sizeof(P3Forward));

struct Sample
{
public:
  Sample();

private:
  P3fForward posForward;
  P3f& pos;
};

//sample.cpp
#include "sample.h"
#include "p3f.h"

Sample::Sample(): pos(reinterpret_cast<P3f&>(posForward))
{
   pos.SetZero();
}
//или даже скопировав в P3fForward соответствующий конструктор
//Sample::Sample(): posForward(0, 0, 0), pos(reinterpret_cast<P3f&>(posForward))
//{
//}
Re[5]: Ссылки + forward declaration
От: Erop Россия  
Дата: 26.09.07 19:31
Оценка:
Здравствуйте, eremeer, Вы писали:

E>структура, наподобие указанной, хранится в здоровенном (около 200кб) заголовочном файле сторонней библиотеки. Мой заголовочный в десятке cpp подключается. В итоге проект на 20 секунд медленнее компилируется (при полной сборке). В проекте сотни три файлов: тут 20 сек, там 30 и получаем вместо 5 минут компиляции, например, 10.


У тебя есть два выхода
Выход 1
struct StructFromLib;
struct UsefulDataFromStructFromLib {
    // Тут собственно описываешь нужные тебе поля
};

// эту функцию реализуешь в ОДНОМ cpp, который собственно и зависит от библиотечного хедера
extern UsefulDataFromStructFromLib extractUsefulData( const StructFromLib& );

ну и в своём классе хранишь константную копию UsefulDataFromStructFromLib без всяких ссылок или указателей


Выход 2
struct StructFromLib;
class StructFromLibAccessor {
public:
    StructFromLibAccessor( StructFromLib* data_ = 0 ) : data( data_ ) {}
    // Тут пишешь аксессоры к нужным тебе полям, дописывая их по мере нужды
    int GetField1() const;
    double GetField2() const;
    
    void SetField1( int ); // если надо, конечно

private:
    StructFromLib * const data;
};
// Методы StructFromLibAccessor реализуешь в ОДНОМ cpp, 
//   который собственно и зависит от библиотечного хедера
Ну и в своём классе хранишь просто поле StructFromLibAccessor.

E>P.S. Люди, собирающие проекты сутками, будут, наверное, смеяться над такими проблемами.

Ну у всех свои проблемы, а люди, собирающие проекты сутками ИМХО хорошо и уютно устроились на большой зарплате...
Так что я им завидую
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[7]: надуманные проблемы...
От: Erop Россия  
Дата: 26.09.07 19:33
Оценка:
Здравствуйте, eremeer, Вы писали:

MM>>Ну а с precompiled headers экспериментировать не пробовал? — все же есть тут что-то совсем мрачное

E>precompiled? где-то выше специально упомянул про полный билд а его приходится иногда делать — слетает отладчик в debug-версии проекта, помогает только полный ребилд (говорят, еще может помочь хирург — выправить руки .
Эта, редко же, на ночь можно запускать в конце концов

E>когда сидишь в дебаге — 5 или 10 — разница все-таки есть. (хотя, после посещения хирурга, возможно и не придется корчиться в дебаге...)

А в дебаге ты тоже полный ребилд всё время запускаешь?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[5]: Ссылки + forward declaration
От: AstroMan  
Дата: 26.09.07 20:02
Оценка:
Здравствуйте, eremeer, Вы писали:

E>пока есть руки, могу написать такое: (тоже теоретически)

E>
E>//sample.h
E>struct P3f;
E>struct P3fForward
E>{
E>  //здесь копия данных P3f
E>};

E>//STATIC_ASSERT(sizeof(P3f) <= sizeof(P3Forward));

E>struct Sample
E>{
E>public:
E>  Sample();

E>private:
E>  P3fForward posForward;
E>  P3f& pos;
E>};

E>//sample.cpp
E>#include "sample.h"
E>#include "p3f.h"

E>Sample::Sample(): pos(reinterpret_cast<P3f&>(posForward))
E>{
E>   pos.SetZero();
E>}
E>//или даже скопировав в P3fForward соответствующий конструктор
E>//Sample::Sample(): posForward(0, 0, 0), pos(reinterpret_cast<P3f&>(posForward))
E>//{
E>//}
E>


Ну здесь уже приведения несвязанных типов. В моем коде все было легально, по крайней мере.
Как я понял проблему, исходный тип P3f из сторонней библиотеки? Если так, то вдвойне плохо.

Нормального решения не знаю. Для сторонних библиотек помогают precompiled headers. Если не менять
заголовки подключаемые в precompiled и не использовать automatic precompiled, то VS у меня не глючит. Решения, базирующиеся на шаблонах, боюсь только замедлит компиляцию.

У меня проект 1 млн. строк компилируется 20 минут. Сокращал время уменьшением связности. Также в библиотеках мелкие классы оставлял как есть, а крупные, сложные и зависимые от сторонних библиотек прятал в pimpl.
Re[5]: Ссылки + forward declaration
От: Аноним  
Дата: 27.09.07 06:03
Оценка:
Здравствуйте, eremeer, Вы писали:

E>структура, наподобие указанной, хранится в здоровенном (около 200кб) заголовочном файле сторонней библиотеки. Мой заголовочный в десятке cpp подключается. В итоге проект на 20 секунд медленнее компилируется (при полной сборке). В проекте сотни три файлов: тут 20 сек, там 30 и получаем вместо 5 минут компиляции, например, 10.

Increadi build спасет отца русской демократии?
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.