Здравствуйте, enji, Вы писали:
E>Здравствуйте, niralex, Вы писали:
E>>>Кстати, а чего вы тот-же Qt не заюзаете? Свойства там есть и как раз такие, как вам нужны. Плюс поддержка от ИДЕ и очень хороший хелп
N>>1. По независящим от меня причинам, студентов(основных пользователей этой системы) учат работать в Embarcadero RAD Studio C++Builder. Перейти на QT самостоятельно в сжатые сроки смогут единицы. N>>2. По "политическим" мотивам предпочитаем разрабатывать свои велосипеды. Проект не коммерческий, в шею никто не гонит, делаем для себя, почему бы и не попробовать...
E>в блдере есть собственные свойства и средства для их рантайм-перебора.
да, но поддержка этих свойств(если имеется ввиду __property) за счет нестандартных средств компилятора и во-вторых слишком громоздко получается.
Е>Кстати, если проект не коммерческий — то каким боком тут рад-студия? она весьма недешевая...
а никто и не покупает... проект для внутреннего использования.
E>Насчет перехода на qt — не надо на него переходить. Достаточно просто заюзать свойства оттуда, а гуй пишите на чем писали
я, конечно, глубоко не вникал в qt, но думаю не так просто вычленить оттуда систему поддержки свойств, во всяком случае для нас.
После долгих и мучительных размышлений решил остановиться на таком варианте решения своей задачи, которую лучше всего сформулировал Erop:
E>Мы, типа, хотим писать какие-то классы, просто так брать и писать, а потом некоторые из полей этих классов делать доступными через интерфейс, который эти классы поддерживают. На поля мы хотим ссылаться по именам. E>И мы хотим, чтобы публикация проходила декларативно, а работало всё это безопасно.
using namespace std;
class TComponent;
//---------------------------------------------------------------------------
// базовый класс свойств - обеспечивает доступ к полям классов
class TProperty
{
public:
virtual string Type()const=0; // тип свойства
virtual void *Value(TComponent * AThis)=0; // значение свойства
};
//---------------------------------------------------------------------------
// пример класса свойств для стандартных типов
typedef TComponent::*TField;
template<class TypeValue>
class TPropSimple : public TProperty
{
private:
// указатель на переменную-член класса
// для которой создается свойство
TField _field;
public:
TPropSimple(TField AField) : _field(AField) {}
string Type()const { return typeid(TypeValue).name(); }
void *Value(TComponent * AThis) { return &(AThis->*_field); }
};
//---------------------------------------------------------------------------
// базовый класс компонентов
class TComponent
{
private:
typedef std::map<string, boost::shared_ptr<TProperty>, less<string> > TMapProperty;
// синглетон контейнера свойств класса
static TMapProperty &Props()
{
static TMapProperty _props;
return _props;
}
protected:
static void PushProp(const string& AName, TProperty *AProp)
{
Props()[AName] = boost::shared_ptr<TProperty>(AProp);
}
public:
// интерфейс доступа к свойствам объекта
static int CountProperties() { return Props().size(); }
static vector<string> AllPropNames() // список имен всех свойств
{
vector<string> _pnames;
for(TMapProperty::const_iterator i = Props().begin(); i != Props().end(); i++)
_pnames.push_back( i->first );
return _pnames;
}
static string PropertyType(string AName) { return Props()[AName]->Type(); }
template<class Type>
Type &PropertyAs(string AName){ return *((Type*)Props()[AName]->Value(this)); }
};
//---------------------------------------------------------------------------
// макросы для декларации свойств в пользовательсках классах
#define BEGIN static void Constructor() {
#define PROP_INT(FIELD, NAME) TComponent::PushProp(NAME, new TPropSimple<int>((TField)&FIELD) );
#define PROP_FLOAT(FIELD, NAME) TComponent::PushProp(NAME, new TPropSimple<float>((TField)&FIELD) );
#define END }
//---------------------------------------------------------------------------
// вспомагательный класс, обеспечивающий автоматический вызов статической функии
// Type::Conctructor() у класса-наследника, которая в свою очередь создает все
// свойства класса
template<class Type>
class TComIniter : public TComponent
{
static struct TClasIniter
{
TClasIniter() { Type::Constructor(); }
} _initer;
// вспомагательная функция, необходимая для того, чтобы компилятор
// инстанцировал статическую переменную _initer
virtual void f() { (void)_initer; }
};
// инстанцирование статической переменной класса _initer
template<class Type>
typename TComIniter<Type>::TClasIniter TComIniter<Type>::_initer;
//---------------------------------------------------------------------------
// пример пользовательского класса
class TDemo : public TComIniter<TDemo>
{
private:
int x;
float y;
public:
// декларация свойств класса
BEGIN
PROP_INT(x, "x");
PROP_FLOAT(y, "y");
END
};
//----------------------------------------------------------------------------------
// пример использования
int main()
{
vector<string> props(TDemo::AllPropNames());
cout << "All properties: " << TDemo::CountProperties() << endl;
for(int i = 0; i < props.size(); i++)
cout << props[i].c_str() << ": "
<< TDemo::PropertyType(props[i]).c_str() << endl;
TComponent *Ob1(new TDemo);
Ob1->PropertyAs<int>("x") = 1;
float &y = Ob1->PropertyAs<float>("y");
y = 1.1;
cout << Ob1->PropertyAs<int>("x") << endl; // 1
cout << y << endl; // 1.1
delete Ob1;
getchar();
return 0;
}
В решении использованы идеи и кодинг Erop-а(спасибо, оказались очень полезными)
и старый топик о конструкторах класса
В связи с недостаточностью моих знаний и практики в области С++, многие из предложенных другими участниками решений не подошли по банальной причине — не смог их понять
Приведенное решение не окончательное. Если кто-то видит потенциальные проблемы, ошибки и т.д., буду рад услышать об этом.
On 03/11/2012 02:15 PM, niralex wrote:
> // базовый класс свойств — обеспечивает доступ к полям классов
> class TProperty > { > public: > virtual string Type()const=0; // тип свойства > virtual void *Value(TComponent * AThis)=0; // значение свойства > };
Ну, после всего что тут было понаписано, возвращать void* как-то уж совсем ...
Не С++ way.
Здравствуйте, Анатолий Широков, Вы писали:
N>>Приведенное решение не окончательное. Если кто-то видит потенциальные проблемы, ошибки и т.д., буду рад услышать об этом.
АШ>А зачем же так многословно-то? тынц
В твоем решении я вижу две проблемы:
1) имена полей не статические, т.е. каждый объект класса будет хранить свою копию std::map<std::string, boost::any> property; Это, как минимум нерационально.
2) Например, есть уже написанный класс и нужно сделать из него компонент, т.е. сделать свойства из уже существующих полей. В моем решении я только добавлю наследование от TComIniter<Type> и макросы BEGIN...END. Все. При этом размер объектов не увеличится. В твоем случае будет сложнее... Хочется, чтобы класс можно было писать не думая о том, что там будут какие-то свойства, а потом просто дополнить чем-то по-минимуму, чтобы появились свойства.
Здравствуйте, MasterZiv, Вы писали:
>> // базовый класс свойств — обеспечивает доступ к полям классов
>> class TProperty >> { >> public: >> virtual string Type()const=0; // тип свойства >> virtual void *Value(TComponent * AThis)=0; // значение свойства >> };
MZ>Ну, после всего что тут было понаписано, возвращать void* как-то уж совсем ... MZ>Не С++ way.
Да, в целом согласен. В оправдание могу только сказать, что это все это спрятано "внутри", т.е. клиентский код не работает с viod* напрямую, а использует медод PropertyAs<Type>(string), куда можно добавить всякие проверки, assert-ы и т.д и пполучить свой велосипед. Во вторых, в моем проекте есть своя специфика — все пользовательские классы помещаются в dll-ки, для чего классы "заворачиваются" в С-оболочку(понимаю, что звучит дико). Там естественно используется void*, что красиво стыкуется с приведенным кодом. Ну и главное — я не работал никогда с boost::any или boost::variant и есть предубеждение, что использование подобных штук(если они имелись в виду) скажется на размере/производительности/удобстве использования. Или я не прав?
N>Да, в целом согласен. В оправдание могу только сказать, что это все это спрятано "внутри", т.е. клиентский код не работает с viod* напрямую, а использует медод PropertyAs<Type>(string), куда можно добавить всякие проверки, assert-ы и т.д и пполучить свой велосипед. Во вторых, в моем проекте есть своя специфика — все пользовательские классы помещаются в dll-ки, для чего классы "заворачиваются" в С-оболочку(понимаю, что звучит дико). Там естественно используется void*, что красиво стыкуется с приведенным кодом. Ну и главное — я не работал никогда с boost::any или boost::variant и есть предубеждение, что использование подобных штук(если они имелись в виду) скажется на размере/производительности/удобстве использования. Или я не прав?
В общем случае, при выборе средств "предубеждение" должно быть последним на чаша весов "за" или "против". А так, как и в любой исследовательской работе, сначала изучаешь, что уже есть, выделяешь достоинства и недостатки, проводишь эксперименты с целью выявить осязаемые метрики, дабы при сравнении оперировать конкретными "цифирями". Это все общие слова, конечно. В своих проектах отдавал и отдаю предпочтение средствам стандартным и хорошо специфицированным.
> красиво стыкуется с приведенным кодом. Ну и главное — я не работал никогда с > boost::any или boost::variant и есть предубеждение, что использование подобных > штук(если они имелись в виду)
Я имел в виду не их, что-то типа свойств на шаблонах.
скажется на размере/производительности/удобстве
На удобстве конечно скажется. А на размер и производительность, думаю, тебе
обращать внимание не нужно.