Здравствуйте, AndreyM16, Вы писали:
AM>2) boost::any. Вообщем намного лучше чем первый вариант, есть возможность проверить тип, хотя все равно тоже самое. Но эта возможность(через type_info) не очень стандартизирована и при компиляции разных модулей разными компиляторами может не прокатить, а также тратиться дополнительное время на эту самую проверку.
Тут надо понимать, что при компиляции разных модулей разными компиляторами может вообще ничего не проканать, кроме plain С. И даже обычный _HAS_ITERATOR_DEBUGGING=0 в MSVC 2010 способен поломать бинарную совместимость std::string между вашим модулем и CRT. Так что бы порекомендовал осетра маленько урезать и сперва четко определиться с набором условий, для которого вам нужно решение.
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Здравствуйте, AndreyM16, Вы писали:
AM>Возникли вопросы про сабжу. Как это можно сделать и вообще правильно ли это? По мне так такая штука очень полезная, например есть набор модулей с общим интерфейсом, которые общаются через сообщения, но данные в сообщениях могут быть разного типа и их количество(типов данных) может со временем расти
Здравствуйте, Mr.Delphist, Вы писали:
AM>>А вообще, потребность в этом не ошибка проектирования?
MD> — полностью согласен. Сколько раз сталкивался с таким, всё равно этот "произвольный объект" и финальный свитч "по типам" в конце концов заменялся на иное решение, более "прямого" характера. Поэтому — не тратьте время, если нет горящего дедлайна. Обдумайте спокойно: как можно генерализовать необходимые операции? зачем понадобился этот нашлёпок с "любым типом"?
для расширяемости. например поддержки в сервере новой операции без переделки старых клиентов, её не использующих. странно, что ты проспал использование xml, например, для этих целей
Возникли вопросы про сабжу. Как это можно сделать и вообще правильно ли это? По мне так такая штука очень полезная, например есть набор модулей с общим интерфейсом, которые общаются через сообщения, но данные в сообщениях могут быть разного типа и их количество(типов данных) может со временем расти или в том же обзёрвере в его push варианте. Может быть такое возникает при ошибках проектирования или это нормальная ситуация?
Сейчас вижу несколько способов как такое реализовать:
1) void* По мне это грубый хак без какой либо проверки типов.
2) boost::any. Вообщем намного лучше чем первый вариант, есть возможность проверить тип, хотя все равно тоже самое. Но эта возможность(через type_info) не очень стандартизирована и при компиляции разных модулей разными компиляторами может не прокатить, а также тратиться дополнительное время на эту самую проверку.
3) boost::variant. Реализация паттерна визитор. Нет проблем с проверкой типов, но количество типов изначально ограничено и при добавлении нового нужно переписывать код этих самых визиторов.
4) сериализация. Универсальный способ, но зачем нужно сериализовывать объект если нужна только ссылка на него.
В языках с динамической(утиной) типизацией таких вопрос не возникает. В C# насколько я знаю(хотя плохо знаю) передают объект CObject.
Вообщем теряюсь в догадках как это можно нормально реализовать на C++ и вообще правильно ли это или нет.
Здравствуйте, AndreyM16, Вы писали:
AM>4) сериализация. Универсальный способ, но зачем нужно сериализовывать объект если нужна только ссылка на него.
5) Завести модуль, который рулит передачей параметров и сообщений. Все остальные регят в нём типы, которые используют, сообщения, и конвертилки типов, если нужны/доступны.
Дальше аналог any, но свой и не привязанный к реализации RTTI...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, AndreyM16, Вы писали:
AM>>4) сериализация. Универсальный способ, но зачем нужно сериализовывать объект если нужна только ссылка на него.
E>5) Завести модуль, который рулит передачей параметров и сообщений. Все остальные регят в нём типы, которые используют, сообщения, и конвертилки типов, если нужны/доступны.
E>Дальше аналог any, но свой и не привязанный к реализации RTTI...
Как выделенное можно реализовать? Или нужно изначально задать определенные типы которые могут быть использованы(хотя они могут быть определены, только в этом спомогательном модуле) и добавлять новые если появляются, или использовать type_info, я других способов не вижу. А вообще, потребность в этом не ошибка проектирования?
Здравствуйте, AndreyM16, Вы писали:
AM>4) сериализация. Универсальный способ, но зачем нужно сериализовывать объект если нужна только ссылка на него.
если система маленькая, то void* + GetType() достаточно
сериализация подходит для серьезной системы (event bus)
лучше отказаться от "ссылок" и передавать данные по значению. так вы решите проблемы с разными модулями (std::string через границы dll, runtime, etc) и временем жизни данных
в сериализации одна из больших подзадач — это поддержка версионности (через xml можно все обрулить)
общее название: message queue, event bus. можете в гугле поискать
Здравствуйте, AndreyM16, Вы писали:
AM>2) boost::any. Вообщем намного лучше чем первый вариант, есть возможность проверить тип, хотя все равно тоже самое. Но эта возможность(через type_info) не очень стандартизирована и при компиляции разных модулей разными компиляторами может не прокатить, а также тратиться дополнительное время на эту самую проверку.
предлагаю обсудить такой набросок (кое где можно подправить, но как иллюстрация пойдет):
enum any_message { any_error, any_clone, any_alloc, any_free };
template< typename T >
struct holder {
T value;
holder() : value() {}
static void * apply ( any_message msg, void const * src ) {
switch ( msg ) {
case any_alloc: {
return 0;
} case any_clone: {
void * ptr = new T( * static_cast<T const *>( src ) );
std::cout << "clone:" << src << " -> " << ptr << std::endl;
return ptr;
} case any_free: {
std::cout << "free:" << src << std::endl;
delete static_cast<T const *>( src );
return 0;
} default: {
assert( 0 );
}
}
}
};
struct any {
void * content;
void * (* id)( any_message, void const * );
template< typename T >
any ( T const & src ) : content( new T( src ) ), id( &holder<T>::apply ) { std::cout << "alloc:" << (void *)content << std::endl; }
any ( any const & rhs ) : content( rhs.id( any_clone, rhs.content ) ), id( rhs.id ) {}
~any () { this->id( any_free, this->content ); }
any & swap ( any & rhs )
{
std::swap( this->content, rhs.content );
std::swap( this->id, rhs.id );
return *this;
}
any & operator = ( any rhs ) { rhs.swap( *this ); return *this; }
template< typename T > T * get() {
return ( this->id == &holder<T>::apply ) ? static_cast<T *>( this->content ) : 0;
}
};
int main()
{
any x = int( 1 );
any y = int( 2 );
any z = float( 2 );
x = y;
assert( *x.get<int>() == 2 );
x = z;
assert( x.get<int>() == 0 );
return 0;
}
Здравствуйте, night beast, Вы писали:
NB>Здравствуйте, AndreyM16, Вы писали:
AM>>2) boost::any. Вообщем намного лучше чем первый вариант, есть возможность проверить тип, хотя все равно тоже самое. Но эта возможность(через type_info) не очень стандартизирована и при компиляции разных модулей разными компиляторами может не прокатить, а также тратиться дополнительное время на эту самую проверку.
NB>предлагаю обсудить такой набросок (кое где можно подправить, но как иллюстрация пойдет): NB>
...
NB>
Это не будет работать если есть по крайней мере два отдельно компилирумых модуля.
Здравствуйте, AndreyM16, Вы писали:
AM>>>2) boost::any. Вообщем намного лучше чем первый вариант, есть возможность проверить тип, хотя все равно тоже самое. Но эта возможность(через type_info) не очень стандартизирована и при компиляции разных модулей разными компиляторами может не прокатить, а также тратиться дополнительное время на эту самую проверку.
NB>>предлагаю обсудить такой набросок (кое где можно подправить, но как иллюстрация пойдет):
немного подумав решил что механизм виртуальности предпочтительней.
AM>Это не будет работать если есть по крайней мере два отдельно компилирумых модуля.
объектника или длл? если объектника, то в чем именно косяк?
Здравствуйте, night beast, Вы писали:
NB>Здравствуйте, AndreyM16, Вы писали:
AM>>>>2) boost::any. Вообщем намного лучше чем первый вариант, есть возможность проверить тип, хотя все равно тоже самое. Но эта возможность(через type_info) не очень стандартизирована и при компиляции разных модулей разными компиляторами может не прокатить, а также тратиться дополнительное время на эту самую проверку.
NB>>>предлагаю обсудить такой набросок (кое где можно подправить, но как иллюстрация пойдет):
NB>немного подумав решил что механизм виртуальности предпочтительней.
AM>>Это не будет работать если есть по крайней мере два отдельно компилирумых модуля.
NB>объектника или длл? если объектника, то в чем именно косяк?
Я имел ввиду dll так как в вашем any типы индексируются ссылкой на инстанцированный класс holder, и в различных dll для одного типа они будут разными.
Здравствуйте, AndreyM16, Вы писали:
NB>>>>предлагаю обсудить такой набросок (кое где можно подправить, но как иллюстрация пойдет):
NB>>немного подумав решил что механизм виртуальности предпочтительней.
AM>>>Это не будет работать если есть по крайней мере два отдельно компилирумых модуля.
NB>>объектника или длл? если объектника, то в чем именно косяк?
AM>Я имел ввиду dll так как в вашем any типы индексируются ссылкой на инстанцированный класс holder, и в различных dll для одного типа они будут разными.
Здравствуйте, AndreyM16, Вы писали:
AM>А вообще, потребность в этом не ошибка проектирования?
— полностью согласен. Сколько раз сталкивался с таким, всё равно этот "произвольный объект" и финальный свитч "по типам" в конце концов заменялся на иное решение, более "прямого" характера. Поэтому — не тратьте время, если нет горящего дедлайна. Обдумайте спокойно: как можно генерализовать необходимые операции? зачем понадобился этот нашлёпок с "любым типом"?
Здравствуйте, AndreyM16, Вы писали:
AM>Как выделенное можно реализовать? Или нужно изначально задать определенные типы которые могут быть использованы(хотя они могут быть определены, только в этом спомогательном модуле) и добавлять новые если появляются, или использовать type_info, я других способов не вижу.
В смысле? Какие проблемы с реализацией? Регишь имя типа в виде строки и интерфейс для доступа. Полагаешься на то, что твои плагины не будут называть одним именем два разных типа. Это ничего не ухудшает, так как просто при программировании на С++ ты на это тоже полагаешься...
Если всё и всегда inprocess, то можно обойтись и без доп. модуля. И всё регать локально. В каждом из модулей.
Смотри. Пишешь что-то типа
class MyAny {
public:
MyAny( const MyAny& ); // тривиально, через data->Clone()
~MyAny(); // тоже тривиально.template<typename T>
static MyAny Create( T& toCreate )
{
return MyAny( TypeRegistrar<T>::GetAnyName(), new IT<T>( toCreate ) );
}
template<typename T> T Get()
{
assert( TypeRegistrar<T>::HasName( typeName ) );
return return *static_cast<T const*>( data->Get() )
}
template<typename T>
class TypeRegistrar {
static TypeRegistrar* first; // = 0
TypeRegistrar* next;
std::string name;
TypeRegistrar( const TypeRegistrar& ); void operator=( const TypeRegistrar& ); // тел нетpublic:
TypeRegistrar( const char* TypeName ) : name( TypeName )
{
assert( name != 0 );
next = first;
first = this;
}
static bool HasName( const char* name )
{
assert( name != 0 );
for( TypeRegistrar* cur = fisrt; cur != 0; cur = cur->next ) {
if( cur->name == name ) {
return true;
}
}
return false;
}
static const char* GetAnyName()
{
assert( first != 0 );
return first->name.c_str();
}
};
private:
struct I {
virtual ~I() {}
virtual const void* Get() = 0;
virtual I* Clone() = 0;
};
template<typename T> struct IT : I {
virtual ~I() {}
virtual const void* Get() { return &data; }
virtual I* Clone() { return new IT( *this ); }
private:
T data;
};
const char* typeName;
I* data
};
Ну и пишешь дальше вне всех функций, например там, где объявляешь MyType, ещё и так:
MyAny::TypeRegistrar<MyType> registrarMyType( "MyType" ); // для этого можно сделать макрос
А потом в какой-то функции пишешь:
void foo( MyType& x )
{
SendMyMessage( MessageId, MyAny::Create( x ) );
}
а в обработчике пишешь:
void processMyMessage( MyAny any )
{
MyType t = any.Get<MyType>();
// текст обработчика
}
AM>А вообще, потребность в этом не ошибка проектирования?
Ну это от задачи зависит. иногда плагинам нужен сколь угодно расширяемый интерфейс, например.
Зачем тебе вообще сообщения понадобились?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
AM>Возникли вопросы про сабжу. Как это можно сделать и вообще правильно ли это? По мне так такая штука очень полезная, например есть набор модулей с общим интерфейсом, которые общаются через сообщения, но данные в сообщениях могут быть разного типа и их количество(типов данных) может со временем расти или в том же обзёрвере в его push варианте.
Я выскажу свое имхо. Как правило, с переданным объектом надо что-то делать, правильно? Вот это что-то напрашивается реализовать в виде виртуальных функций и передавать ссылку (указатель) на общего предка.
Здравствуйте, Vamp, Вы писали:
V>Я выскажу свое имхо. Как правило, с переданным объектом надо что-то делать, правильно? Вот это что-то напрашивается реализовать в виде виртуальных функций и передавать ссылку (указатель) на общего предка.
это работает если клиенты зафиксированы и вызывают функции только из определённого набора. когда новые функции могут появляться и на серверной, и на клиентской стороне, это не работает
BZ>это работает если клиенты зафиксированы и вызывают функции только из определённого набора. когда новые функции могут появляться и на серверной, и на клиентской стороне, это не работает
Не понял, ясное дело, если появляются новые функции, надо добавлять функции к коду класса, и добавлять код их вызова к коду клиента. А как иначе?
Здравствуйте, Vamp, Вы писали:
BZ>>это работает если клиенты зафиксированы и вызывают функции только из определённого набора. когда новые функции могут появляться и на серверной, и на клиентской стороне, это не работает V>Не понял, ясное дело, если появляются новые функции, надо добавлять функции к коду класса, и добавлять код их вызова к коду клиента. А как иначе?
а так, что при этом придётся выкинуть существующих клиентов и ждать пока появятся новые. что приемлемо, если это всё — часть одной программы и клиентов немного, но когда тебе нужно поменять десяток классов только потому что в одном из них появился новый параметр у метода — начинаешь понимать, что тут что-то не так
Здравствуйте, Vamp, Вы писали:
BZ>>а так, что при этом придётся выкинуть существующих клиентов и ждать пока появятся новые. V>Не понял, зачем выкидывать клиентов-то?
затем, что они несовместимы со старым интерфейсом, в который добавлен новый параметр
BZ>затем, что они несовместимы со старым интерфейсом, в который добавлен новый параметр
Добавлять новые параметры (или иным образом изменять оные), конечно же, нельзя. Но в хорошо спроектированном интерфейсе это не требуется. Но можно добавлять новые методы в интерфейс, в крайнем случае, заводить новые версии существующих методов.
Здравствуйте, AndreyM16, Вы писали:
AM>Возникли вопросы про сабжу. Как это можно сделать и вообще правильно ли это? По мне так такая штука очень полезная, например есть набор модулей с общим интерфейсом, которые общаются через сообщения, но данные в сообщениях могут быть разного типа и их количество(типов данных) может со временем расти или в том же обзёрвере в его push варианте. Может быть такое возникает при ошибках проектирования или это нормальная ситуация?
Идея хорошая и при качественной реализации дает стабильный редкоизменяемый каркас с развитием в виде модулей расширения.
Беда тут в том что сделать такой каркас очень непросто и проблем там гораздо больше чем просто правильно выбрать тип сообщения и интерфейс.
Я принимал участие в нескольких таких попытках. И как минимум одну из них можно назвать успешной.
Это интересно для разработчика но очень рисковано для заказчика и наверно поэтому встречается нечасто.
Ведь в большинстве случаев можно взять готовое решение.
Я бы порекомендовал поглядеть на Apache ActiveMQ.
Даже если решите строить свою систему там есть на что посмотреть
Короче, Склифосовский (с), ты лучше пальцем покажи (с)
Можешь на простом простом примере продемонстрировать свое решение, а то XML , tabi. Посмотрел я tabi, так и не понял как его можно приложить, дa и XML тоже
Здравствуйте, Vamp, Вы писали:
V>Добавлять новые параметры (или иным образом изменять оные), конечно же, нельзя. Но в хорошо спроектированном интерфейсе это не требуется.
это хорошо, что у вас в архитекторы берут только ясновидящих 3-го класса. ну а мы в провинции по старинке пишем расширяемые интерфейсы
Здравствуйте, qqqqq, Вы писали:
Q>Короче, Склифосовский (с), ты лучше пальцем покажи (с) Q>Можешь на простом простом примере продемонстрировать свое решение, а то XML , tabi. Посмотрел я tabi, так и не понял как его можно приложить, дa и XML тоже
Здравствуйте, BulatZiganshin, Вы писали:
BZ>Здравствуйте, Vamp, Вы писали:
V>>Добавлять новые параметры (или иным образом изменять оные), конечно же, нельзя. Но в хорошо спроектированном интерфейсе это не требуется.
BZ>это хорошо, что у вас в архитекторы берут только ясновидящих 3-го класса. ну а мы в провинции по старинке пишем расширяемые интерфейсы
Здравствуйте, hramovnik, Вы писали:
H>Здравствуйте, BulatZiganshin, Вы писали:
BZ>>Здравствуйте, Vamp, Вы писали:
V>>>Добавлять новые параметры (или иным образом изменять оные), конечно же, нельзя. Но в хорошо спроектированном интерфейсе это не требуется.
BZ>>это хорошо, что у вас в архитекторы берут только ясновидящих 3-го класса. ну а мы в провинции по старинке пишем расширяемые интерфейсы
А версионирование интерфейсов, как в СОМ, не устраивает?
Здравствуйте, hramovnik, Вы писали:
H>А версионирование интерфейсов, как в СОМ, не устраивает?
это хорошо, если новые версии появляются редко и одна из сторон (клиенты или серверы) имеется в единственном экземпляре. представь десяток серверов, десяток клиентов и каждый месяц появляется новый параметр, который один из этих клиентов хочет передавать одному из серверов
Здравствуйте, BulatZiganshin, Вы писали:
BZ>Здравствуйте, hramovnik, Вы писали:
H>>А версионирование интерфейсов, как в СОМ, не устраивает?
BZ>это хорошо, если новые версии появляются редко и одна из сторон (клиенты или серверы) имеется в единственном экземпляре. представь десяток серверов, десяток клиентов и каждый месяц появляется новый параметр, который один из этих клиентов хочет передавать одному из серверов
Столь активное обновление меня настораживает.
В любом случае будут необходимы изменения и на клиенте и на сервере, так что не вижу проблем.
Версионирование обеспечит поддержу старых клиентов. А без обновлений сервера (в той или иной форме), использование нового клиента, с новыми параметрами, мне сложно представить.
Или предполагается независимое развитие каждого из серверов?
Здравствуйте, hramovnik, Вы писали:
H>Столь активное обновление меня настораживает.
обычное развитие. сейчас и новую. версию выпускать ежемесячно несложно
H>В любом случае будут необходимы изменения и на клиенте и на сервере, так что не вижу проблем.
я имел в иду необязательные параметры, хинты. нарпимер сколько потоков/памяти использовать
H>Или предполагается независимое развитие каждого из серверов?
да, а что тут такого. в один добавлены одни новые фичи, в другой другие. а клиенты общие
Здравствуйте, BulatZiganshin, Вы писали:
BZ>Здравствуйте, Mr.Delphist, Вы писали:
AM>>>А вообще, потребность в этом не ошибка проектирования?
MD>> — полностью согласен. Сколько раз сталкивался с таким, всё равно этот "произвольный объект" и финальный свитч "по типам" в конце концов заменялся на иное решение, более "прямого" характера. Поэтому — не тратьте время, если нет горящего дедлайна. Обдумайте спокойно: как можно генерализовать необходимые операции? зачем понадобился этот нашлёпок с "любым типом"?
BZ>для расширяемости. например поддержки в сервере новой операции без переделки старых клиентов, её не использующих. странно, что ты проспал использование xml, например, для этих целей
Честно говоря, не понял, кому адресовано выделенное. Ну да ладно.
Вот именно XML-based решения — один из ярких примеров генерализации, безо всяких нашлепков. Есть единый API между клиентом и сервером, внутри дергается парсер-фабрика, порождающая далее нужное инстанциирование и т.п. Парсинг свалился — возвращем ошибку. Постепенно начинаем понимать, что "наивный" XML с захардкоживанием туда жестких структур и путей — тот же свитч по типам, "только сбоку" (с). Думаем. Далее генерализуем структуру XML (например, делаем тот же property bag). Парсер упрощается, интерфейс единый, профит (с).
Здравствуйте, Mr.Delphist, Вы писали:
MD>>> — полностью согласен. Сколько раз сталкивался с таким, всё равно этот "произвольный объект" и финальный свитч "по типам" в конце концов заменялся на иное решение, более "прямого" характера. Поэтому — не тратьте время, если нет горящего дедлайна. Обдумайте спокойно: как можно генерализовать необходимые операции? зачем понадобился этот нашлёпок с "любым типом"?
BZ>>для расширяемости. например поддержки в сервере новой операции без переделки старых клиентов, её не использующих. странно, что ты проспал использование xml, например, для этих целей
MD>Честно говоря, не понял, кому адресовано выделенное. Ну да ладно.
MD>Вот именно XML-based решения — один из ярких примеров генерализации, безо всяких нашлепков.
xml — это и есть один из способов кодирования произвольного типа. неэффективный, зато "из коробки". если тебя это не устраивает, ты модешь взять бдолее эффективную библиотеку сериализации/rpc или написать свою