Есть старый API к одной очень распространенной в банковской сфере системе посылки сообщений (все имена, естественно, изменены).
Ключевая особенность работы с ним состоит в том, что для каждого сообщения имеется свой класс (Msg1, Msg2, ...), и для обработки сообщений нужно просто отдать в API функцию с одним аргументом сооответствующего типа, и API ее позовет, когда это сообщение придет.
Сейчас приложение уже есть и работает, через все эти функции обратного вызова. Все сообщения обрабатываются в реальном времени.
Задача: добавить обработку старых сообщений, которые проигрываются сервером при старте приложения.
Проблемы
Прелестей у этого API много, вот две, которые нас особенно заинтересуют сейчас:
1. При (пере)запуске программы, во время проигрывания старых сообщений, сервер посылает их не в том порядке, в котором они приходили в реальной жизни. Но обрабатывать, естественно, надо в правильном порядке, а это значит, что сначала нужно все старые сообщения получить, куда-то сложить, отсортировать, и только после этого уже начать обрабатывать.
2. Классы для разных типов сообщений не имеют общего предка, а это означает, что сложить их в нормальный контейнер по указателю на базовый класс не удастся.
Анализ
Стало быть, стоит задача каким-то образом из все-таки в один контейнер сложить и как-то отсортировать, после чего начать обрабатывать, причем опять же каждое — в соответствии с его типом.
Начнем прояснять с конца:
как сортировать?
Во-первых, каждое сообщение имеет поле, в котором лежит время. Правда, в каждом классе оно лежит в своем поле
Ну да не проблема написать перегруженную функцию get_time_field, которая будет принимать ссылку на класс сообщения и возвращать нечто, что можно будет сравнить с тем, что вернет эта же функция для другого класса — это в любом случае придется сделать, какой бы подход мы ни выбрали.
А если время одинаковое?
Возникает следующий вопрос — а что делать, если у нас есть два сообщения, и у них время одинаковое? Это вполне вероятно, потому что разрешение системы невелико — десятки миллисекунд.
В произвольном порядке их обрабатывать нельзя, потому что между ними есть зависимость, и в реальной жизни сообщения одного типа приходят всегда раньше сообщений другого типа (т.е. сначала приходит сообщение типа Msg1 с чем-нть вроде "произошла ошибка" и в другом поле текст ошибки, а потом приходит сообщение типа Msg2, в котором записано, что текущий статус связанного с ним объекта — неудача, и ты знаешь, что причина неудачи — как раз в пришедшем ранее Msg1).
Ниже я вернусь к тому, как выразить этот упорядоченность типов сообщений в коде.
Далее, что если у нас оказались с одним и тем же временем сообщения одного типа? Тут тоже можно кое-что придумать: например, сообщения типа Msg1 имеют поле со счетчиком, а Msg2 — с номером объекта, на который они ссылаются (т.е. тоже своего рода счетчик в виде внешнего ключа), и т.д.
В коде это тоже выражается очень просто — пишется какая-нть перегруженная функция compare_type с двумя аргументами одного типа, для каждого типа сообщения, и внутри производится упорядочение. Опять же, этот код тоже придется писать при любом решении.
Итак, на текущий момент мы имеем:
1. перегруженную функцию get_time_field, с помощью которой мы можем упорядочить сообщения по времени, независимо от типа сообщения (в этом примере она просто вернет сишную строку, которые можно будет сравнить банальной strcmp);
2. перегруженную функцию compare_type, с помощью которой мы можем упорядочить сообщения одного типа с одной и той же временной меткой.
Это то, что нам будет нужно при любой организации контейнера.
Нам осталось решить две задачи:
1. Упорядочить сообщения разных типов с одной и той же временной меткой.
2. Сложить сообщения разных типов в какой-нибудь контейнер.
3. Призовая задача — у нас уже есть оттестированные функции-обработчики для каждого типа сообщений (см. начало истории), хорошо бы их и заюзать для обработки сохраненных сообщений, по возможности без изменения.
Решение
Я применил следующее решение.
1. В контейнере у нас будет лежать вариант, который может принимать объекты только этих трех типов. Особенно хорошо для этой цели подходит boost::variant, так как в нем есть очень полезная штука — static visitor. В двух словах это означает, что ты передаешь варианту перегруженную по хранимому типу функцию (или вообще одну шаблонную), и вариант сам вызовет правильную версию в зависимости от того, что в нем находится. За подробностями — сюда: http://www.boost.org/doc/libs/1_35_0/doc/html/variant.html.
2. В качестве контейнера выберем обычный std::multiset, предоставив ему соответствующий компаратор, в котором мы инкапсулируем все наши сравнения.
В компаратор, конечно, придут не объекты самих классов сообщений, а варианты, но мы предоставим самому варианту с этим разбираться, позвав функцию boost::apply_visitor — она сама вызовет то, что нам нужно (а именно, сам же компаратор, но уже с правильными аргументами). Эта же boost::apply_visitor позволит нам решить вопрос перебора сообщений в очереди.
Например, мы хотим в целях отладки распечатать очередь после того, как мы получили все старые сообщения, только вот беда — разработчики API, которые добавили в сообщения метод print(), не знали слова const. Пишем простенький функтор с кастом (шаблонный, чтоб сработал для всех типов сообщений):
typedef boost::mpl::vector< Msg1, Msg2, Msg3 > MsgTypes;
template <typename T, typename U>
bool compare_type(const T& left, const U& right) const
{
// compare type position in the MsgTypesreturn
boost::mpl::find< MsgTypes, T >::type::pos::value
<
boost::mpl::find< MsgTypes, U >::type::pos::value;
}
Пояснения:
boost::mpl::find< MsgTypes, T > — это алгоритм, который ищет тип Т в последовательности типов MsgTypes (http://www.boost.org/doc/libs/1_35_0/libs/mpl/doc/refmanual/find.html),
boost::mpl::find< MsgTypes, T >::type — это вызов алгоритма, получаем итератор, который возвращает этот алгоритм,
boost::mpl::find< MsgTypes, T >::type::pos — это позиция итератора по отношению к началу последовательности (это все еще тип, в котором лежит число),
boost::mpl::find< MsgTypes, T >::type::pos::value — это сам номер, который можно сравнить с любым другим числом, известным во время компиляции, что мы и делаем.
Кстати, вариант можно собрать из mpl::vector, чем мы немедленно и воспользуемся.
Окончательное решение
Ниже привожу окончательное решение (все поместилось на одной странице простейшего кода):
#include <set>
#include"boost/variant.hpp"#include"boost/mpl/vector.hpp"#include"boost/mpl/find.hpp"#include"MsgAPI.hpp"// defines Msg1, Msg2, Msg3typedef boost::mpl::vector< Msg1, Msg2, Msg3 > MsgTypes;
class MsgComparer : public boost::static_visitor<bool>
{
const char* get_time_field( const Msg1& x ) const { return x.sending_time; }
const char* get_time_field( const Msg2& x ) const { return x.update_time; }
const char* get_time_field( const Msg3& x ) const { return x.orig_time; }
template <typename T, typename U>
bool compare_type(const T& left, const U& right) const
{
// compare type position in the MsgTypesreturn
boost::mpl::find< MsgTypes, T >::type::pos::value
<
boost::mpl::find< MsgTypes, U >::type::pos::value;
}
// comparators for same typebool compare_type(const Msg1& left, const Msg1& right) const
{
return strcmp( left.seq_num, right.seq_num ) == -1;
}
bool compare_type(const Msg2& left, const Msg2& right) const
{
return strcmp( left.external_id, right.external_id ) == -1;
}
bool compare_type(const Msg3& left, const Msg3& right) const
{
return left.counter < right.counter;
}
public:
template <typename T, typename U>
bool operator()( const T& left, const U& right ) const
{
const int r = strcmp( get_time_field( left ), get_time_field( right ) );
switch (r)
{
case -1: return true;
case 1: return false;
default: return compare_type( left, right );
}
}
};
// The variant Msg (let's call it MegaMsg) :)typedef boost::make_variant_over< MsgTypes >::type MegaMsg;
// The queuetypedef std::multiset< MegaMsg, MsgComparer > Msgs;
// Specialization for MegaMsg - jump from variant to specific message typestemplate <>
inline
bool MsgComparer::operator()( const MegaMsg& left, const MegaMsg& right ) const
{
return boost::apply_visitor(*this, left, right);
}
Здравствуйте, jazzer, Вы писали:
J>Сейчас приложение уже есть и работает, через все эти функции обратного вызова. Все сообщения обрабатываются в реальном времени.
Уточняющий вопрос: судя по тому, что используется std::multiset, имеющееся в задаче реальное время не налагает серьезных ограничений на использование динамической памяти. Соответственно, альтернативные варианты, в которых будут использоваться, скажем, списки умных указателей на некие объекты-интерфейсы, как непригодные по требованиям реального времени отвергаться не будут?
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re: [boost][mpl][variant] гетерогенная очередь
От:
Аноним
Дата:
02.04.08 14:29
Оценка:
Здравствуйте, jazzer, Вы писали:
Может для нериалтайма и есть время упорядочить, сложить в контейнер но в реальной жизни под высокими нагрузками надо делать все на лету.
Вот первый вариант, который пришел в голову: полный файл.
Поскольку я не понял, как создаются объекты Msg1, Msg2, Msg3, то позволил себе сохранять их копию в обертке MessageHandler. А для самих MessageHandler использовать умный указатель (т.к. я пользуюсь ACE, то вместо boost::shared_ptr использовал ACE_Refcounted_Auto_Ptr).
По сравнению с твоим решением я пока вижу у своего два серьезных недостатка:
1. Идентификаторы тегов для типов нужно проставлять вручную. У тебя же они выводятся неявно исходя из позиции в векторе типов. С другой стороны, у тебя все типы собраны в одном месте + есть какие-то органичения на их количество связанные с емкостью mpl::vector (и глубиной инстанциирования шаблонов). У меня таких ограничений нет.
2. У меня нет возможности реализовать сравнение двух разных типов сообщений. Так, у тебя можно определить compare_type(Msg1, Msg2), а у меня это нельзя в принципе. И это самый фатальный недостаток.
Собственно прикладная часть занимает не так много места:
template< class T > struct TypeTag {};
template<> struct TypeTag<Msg1> { static const int value = 0; };
template<> struct TypeTag<Msg2> { static const int value = TypeTag<Msg1>::value + 1; };
template<> struct TypeTag<Msg3> { static const int value = TypeTag<Msg2>::value + 1; };
class AbstractMessageHolder {
public :
virtual ~AbstractMessageHolder() {}
virtual void callApi() = 0;
virtual void print() const = 0;
virtual int getTime() const = 0;
virtual int compareWith( const AbstractMessageHolder & other ) const = 0;
virtual int typeTag() const = 0;
virtual const void * rawPointer() const = 0;
};
typedef ACE_Refcounted_Auto_Ptr< AbstractMessageHolder, ACE_Null_Mutex >
AbstractMessageHolderAutoPtr;
template< class T >
class MessageHolder : public AbstractMessageHolder {
public :
MessageHolder( const T & value ) : msg( value ) {}
virtual void callApi() { apiCall( msg ); }
virtual void print() const { apiCall( const_cast< T& >( msg ) ); }
virtual int getTime() const { return ::getTime( msg ); }
virtual int compareWith( const AbstractMessageHolder & other ) const {
int r = getTime() - other.getTime();
if( !r ) {
r = typeTag() - other.typeTag();
if( !r ) {
r = compareType(
msg,
*(reinterpret_cast< const T*>( other.rawPointer() )) );
}
}
return r;
}
virtual int typeTag() const { return TypeTag<T>::value; }
virtual const void * rawPointer() const { return &msg; }
private :
T msg;
};
template< class T >
AbstractMessageHolderAutoPtr makeMessageHolder( const T & o ) {
return AbstractMessageHolderAutoPtr( new MessageHolder< T >( o ) );
}
struct MessageHolderComparator {
bool operator()(
const AbstractMessageHolderAutoPtr & a,
const AbstractMessageHolderAutoPtr & b ) {
return a->compareWith( *b ) < 0;
}
};
typedef std::multiset<
AbstractMessageHolderAutoPtr,
MessageHolderComparator >
MessageContainer;
При запуске выдает:
calling API...
api on Msg3: 1, 8
api on Msg3: 1, 9
api on Msg1: 2, 4
api on Msg2: 2, 0
api on Msg2: 3, 0
api on Msg2: 3, 1
api on Msg2: 3, 4
api on Msg3: 3, 0
api on Msg1: 4, 2
api on Msg1: 4, 3
printing messages...
api on Msg3: 1, 8
api on Msg3: 1, 9
api on Msg1: 2, 4
api on Msg2: 2, 0
api on Msg2: 3, 0
api on Msg2: 3, 1
api on Msg2: 3, 4
api on Msg3: 3, 0
api on Msg1: 4, 2
api on Msg1: 4, 3
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, eao197, Вы писали:
E>Здравствуйте, jazzer, Вы писали:
J>>Сейчас приложение уже есть и работает, через все эти функции обратного вызова. Все сообщения обрабатываются в реальном времени.
E>Уточняющий вопрос: судя по тому, что используется std::multiset, имеющееся в задаче реальное время не налагает серьезных ограничений на использование динамической памяти.
Налагает, но не в этом режиме (проигрывания старых сообщений), в этом режиме реального времени нет.
Проигрывать старые сообщения можно медленно — задержка будет просто эквивалентна задержке со стартом приложения.
А вот потом, после проигрывания и обработки всех старых сообщений, нужно реальное время, и оно данной функциональностью не затронуто, оно как было через функции обратного вызова, так и осталось.
E>Соответственно, альтернативные варианты, в которых будут использоваться, скажем, списки умных указателей на некие объекты-интерфейсы, как непригодные по требованиям реального времени отвергаться не будут?
Не будут
Если они не будут оказывать влияния на режим реального времени, естественно
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, jazzer, Вы писали:
А>Может для нериалтайма и есть время упорядочить, сложить в контейнер но в реальной жизни под высокими нагрузками надо делать все на лету.
На каком лету, если можно обрабатывать только в правильном порядке, а его можно получить только получив и отсортировав все сообщения?
А рилтайм уже начнется потом, когда все старые сообщения обработаны
Здравствуйте, jazzer, Вы писали:
J>На каком лету, если можно обрабатывать только в правильном порядке, а его можно получить только получив и отсортировав все сообщения?
есть задачи в которых в принципе отсортировать по критерию (по времени например) нет возможности (например параллельная обработка сообщений в кластере). в подобных задачах сообщения не сортируют, а учитывают нужный критерий (время создания сообщения) в момент обработки. общих механизмов при в этом обычно не разрабатывают...
хотя возможно для вашей задачи все вышеописанное не нужно и сортировка прокатит...
Здравствуйте, _cb_, Вы писали:
__>Здравствуйте, jazzer, Вы писали:
J>>На каком лету, если можно обрабатывать только в правильном порядке, а его можно получить только получив и отсортировав все сообщения?
__>есть задачи в которых в принципе отсортировать по критерию (по времени например) нет возможности (например параллельная обработка сообщений в кластере). в подобных задачах сообщения не сортируют, а учитывают нужный критерий (время создания сообщения) в момент обработки. общих механизмов при в этом обычно не разрабатывают... __>хотя возможно для вашей задачи все вышеописанное не нужно и сортировка прокатит...
в слове "учитывают" обычно собака и зарыта
На самом деле, целью моего поста не является обсуждение различных реализаций систем обработки сообщений — это слишком обширная тема и место ей не здесь, а в "Архитектуре".
Я ни в коем случае не утверждаю, что создал тут фреймворк на все времена.
Наоборот.
У меня была совершенно конкретная система, с которой я работал, и появилась совершенно конкретная задача, которую надо было решить, и ее удалось решить крайне просто и элегантно при помощи boost::mpl и boost::variant.
Просто о бусте бытует следующее представление:
1. Буст исключительно сложный
2. Некоторые библиотеки типа boost::mpl — с ними вообще простым смертным не разобраться
3. Чтобы использовать буст, надо вначале написать кучу трехэтажных шаблонов (особенно в случае boost::mpl), и как только ты их используешь, твой обычный код, который был бы без буста очень простым и коротким, теперь всегда будет горой шаблонов, текста будет в разы больше, и в основном нечитабельного.
4. Буст и особенно такие его части, как boost::mpl, простым прикладным программистам не нужны, они нужны только разработчикам библиотек.
Так вот целью моего поста было исключительно показать, что буста бояться не надо, даже таких страшных библиотек, как boost::mpl, и что при правильном применении они способны решать возникающие в жизни простого прикладного программиста задачи совсем без написания многоэтажных шаблонов и килобайтов кода, в котором полезной работой занимается всего одна строчка, а все остальные нужны только для того, чтоб всё вообще хоть как-то скомпилировалось и заработало (как я понимаю, этот "весь остальной код" называют модным словом boilerplate), и что код в результате получится прозрачный, понятный и простой, а старый код, который про буст ни сном ни духом, совершенно не пострадает.
В предлагаемом решении я вижу только одно место, которое можно отнести к boilerplate, т.е. которое ничего полезного не делает само по себе — это специализация оператора вызова функции для перехода от варианта к самим типам сообщений.
Все остальное служит для выражения определенных аспектов условия задачи (взятие определенного поля, упорядочение по определенному признаку в различных случаях).
А так, каждой задаче необходим свой подход, и не исключено, что если бы какое-то условие в задаче поменялось, я пришел бы в результате к совершенно другому решению.
Здравствуйте, jazzer, Вы писали:
J>На самом деле, целью моего поста не является обсуждение различных реализаций систем обработки сообщений — это слишком обширная тема и место ей не здесь, а в "Архитектуре".
согласен. в рамках описанного решения действительно не стОит обсуждать обработку сообщений в целом.
J>У меня была совершенно конкретная система, с которой я работал, и появилась совершенно конкретная задача, которую надо было решить, и ее удалось решить крайне просто и элегантно при помощи boost::mpl и boost::variant.
W>const int r = strcmp( get_time_field( left ), get_time_field( right ) );
W>
W>потенциальная бага?
API гарантирует, что поля, в которых лежит время (т.е. те, которые возвращает get_time_field), содержат строки одной длины в фиксированном формате, типа 2001-02-03-04:05:06.00007) (иначе их, очевидно, бессмысленно сравнивать через strcmp) и всегда оканчиваются нулем.
С учетом этой информации бага все еще есть?
P.S. Ну а если бы в этих полях лежало нечто несовместимое, то был бы какой-то тип, к которому все это можно привести (тот же time_t), и get_time_field возвращала бы его. Так что в этом смысле решение не изменилось бы.
Здравствуйте, jazzer, Вы писали:
J>Просто о бусте бытует следующее представление: J>1. Буст исключительно сложный J>2. Некоторые библиотеки типа boost::mpl — с ними вообще простым смертным не разобраться J>3. Чтобы использовать буст, надо вначале написать кучу трехэтажных шаблонов (особенно в случае boost::mpl), и как только ты их используешь, твой обычный код, который был бы без буста очень простым и коротким, теперь всегда будет горой шаблонов, текста будет в разы больше, и в основном нечитабельного. J>4. Буст и особенно такие его части, как boost::mpl, простым прикладным программистам не нужны, они нужны только разработчикам библиотек.
Поскольку в изначальной теме, откуда появилось упоминание данного примера, я был противником boost-а, то у меня сложилось впечатление, данное повествование относится и ко мне так же. Поэтому я решил высказать свое мнение, прошу простить за навязчивость
Так вот у меня совсем другой список претензий к boost-у:
1. Реализация boost-а исключительно сложна. Ну т.е., когда все идет гладко и в исходники библиотеки заглядывать не нужно, то нет проблем. Но, тем не менее, здесь есть два момента, которые мне не нравятся:
— ни в одном другом языке (а мне приходилось лазить в библиотеки Ruby, Eiffel, D, немного Java) мне не приходилось видеть такой разительной разницы в сложности реализации библиотек и прикладных приложений. По мне, это некий признак того, что что-то где-то не так;
— всегда приятно иметь увереность в том, что твоих знаний хватает для того, чтобы при необходимости разобраться в деталях с происходящим в твоем приложении. По крайней мере у меня не было проблем с тем, чтобы залезть во внутренности MFC, Qt, FOX, ACE. В случае с boost ситуация обратная -- тот же boost.variant сразу же отбивает желание понять, как же он устроен.
2. Некоторые вещи в boost-е направлены на предоставление разработчику инструментов, которые, по хорошему, должны были быть в языке. Пример: boost.lambda. Да и, насколько я понимаю, boost.mpl так же использует различные трюки для того, чтобы обойти существующие недостатки языка. По-моему мнению, это не нормально. Должен развиваться язык, а недостатки языка не должны скрываться за библиотеками, сложность реализации которых слишком высока (см.предыдущий пункт).
3. Некоторые библиотеки boost-а (тот же boost.lambda, а так же boost.spirit), на мой взгляд, являются примерами того, что не нужно делать. При этом у меня складывается впечатление, что наличие подобных вещей создает у C++программистов "головокружение от успехов" -- т.е. программирование на шаблонах в C++ становится модным направлением. Именно "модным", когда шаблоны применяются и к месту, и не к месту.
4. Boost велик. Слишком велик. 22Mb в tar.bz2 версии 1.35 -- это занадто. Имхо, для C++ поздно создавать одну большую стандартную библиотеку. Можно понять Sun с JDK и MS с .NET Framework, которые держат большие штаты программистов, поддерживающих JDK/.NET Framework. А кто будет поддерживать OpenSource-ный boost? Что произойдет, если кому-то из авторов включенных в boost библиотек надоест сопровождать и развивать свое творение? Или как разработчикам каких-то библиотек выпускать новые релизы вне графика выхода версий самого boost-а?
Далее, включение библиотеки в boost осуществляется по результатам review, в которых, обычно, едва набирается несколько десятков отзывов. Т.е. заявляется что-нибудь типа boost.egg
и что? Находятся несколько ценителей прекрасного, которые присылают положительные review и egg в boost-е. И что из того, ценность буста повысилась? В то же время вне буста есть, например, crypto++, botan или cryptlib, которыми пользуются сотни, если не тысячи программистов, но которые никаким боком к boost-у не относятся.
Еще один момент. Библиотеки, которые являются альтернативными boost-овским, они что, уже второго сорта? Бустовская сериализация -- она лучше, чем s11n? Бустовские регулярные выражения лучше, чем PCRE? Бустовские юнит-тесты намного лучше CppUnit? Не получится ли так, что когда разработчику понадобиться что-то, то он просто возмет реализацию из boost-а даже не глядя на альтернативы? Т.е. выбор будет строится не на возможностях, а на репутации. Как бы в результате boost не стал монополистом, который своей массой будет просто давить конкурентов.
Вот такое, собственно говоря, имхо.
Не то, чтобы я принципиально отказывался от использования boost-а по каким-то религиозным соображениям. У меня даже часть старых проектов использует boost.unit для юнит-тестов. Или если мне потребуется boost.multi_index, то я возьму boost. Но сделаю это несколько скрепя сердцем, поскольку boost просто не является библиотекой, которая мне нравится (в отличии, например, от PCRE, Qt, FOX, ACE, Crypto++ и иже с ними).
К читателям: не думаю, что нужно начитать флейм по этому поводу. Я описал весь этот текст для того, чтобы у jazzer-а не сложилось превратного впечатления о том, в чем же я лично обвиняю boost.
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, eao197, Вы писали:
E>Еще один момент. Библиотеки, которые являются альтернативными boost-овским, они что, уже второго сорта? Бустовская сериализация -- она лучше, чем s11n? Бустовские регулярные выражения лучше, чем PCRE? Бустовские юнит-тесты намного лучше CppUnit? Не получится ли так, что когда разработчику понадобиться что-то, то он просто возмет реализацию из boost-а даже не глядя на альтернативы? Т.е. выбор будет строится не на возможностях, а на репутации. Как бы в результате boost не стал монополистом, который своей массой будет просто давить конкурентов.
Не понять тебя.
Ты же первый говоришь, что хотел бы видеть (по крайней мере) некоторые из этих вещей в стандартной библиотеке, нет?
Стандартная библиотека при таких рассуждениях тоже становится монополистом.
Является ли это аргументом против стандартной библиотеки?
Здравствуйте, eao197, Вы писали:
E>2. Некоторые вещи в boost-е направлены на предоставление разработчику инструментов, которые, по хорошему, должны были быть в языке. Пример: boost.lambda. Да и, насколько я понимаю, boost.mpl так же использует различные трюки для того, чтобы обойти существующие недостатки языка. По-моему мнению, это не нормально. Должен развиваться язык, а недостатки языка не должны скрываться за библиотеками, сложность реализации которых слишком высока (см.предыдущий пункт).
Т.е. рано или поздно любая фича должна быть добавлена в язык?
Здравствуйте, Alxndr, Вы писали:
E>>2. Некоторые вещи в boost-е направлены на предоставление разработчику инструментов, которые, по хорошему, должны были быть в языке. Пример: boost.lambda. Да и, насколько я понимаю, boost.mpl так же использует различные трюки для того, чтобы обойти существующие недостатки языка. По-моему мнению, это не нормально. Должен развиваться язык, а недостатки языка не должны скрываться за библиотеками, сложность реализации которых слишком высока (см.предыдущий пункт).
A>Т.е. рано или поздно любая фича должна быть добавлена в язык?
Нет. Любая фича должна быть реализована в виде библиотеки и включена в boost. И лишь после 7-8 лет ее использования она должна войти в очередной драфт стандарта.
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, Alxndr, Вы писали:
A>Ты же первый говоришь, что хотел бы видеть (по крайней мере) некоторые из этих вещей в стандартной библиотеке, нет?
Не помню, чтобы я говорил про стандартную библиотеку. Некоторые вещи должны быть частью языка (т.е. поддерживаться компилятором), а не библиотеками.
A>Стандартная библиотека при таких рассуждениях тоже становится монополистом. A>Является ли это аргументом против стандартной библиотеки?
о том, что разработка интераторов для сложных структур данных была бы проще и эффективнее, если бы вместо пары итераторов [begin, end) стандартные алгоритмы использовали бы range. Что может служить примером того, как зафиксированные намертво в стандарте решения приводят к непредвиденным сложностям в последствии.
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, eao197, Вы писали:
E>Здравствуйте, Alxndr, Вы писали:
A>>Ты же первый говоришь, что хотел бы видеть (по крайней мере) некоторые из этих вещей в стандартной библиотеке, нет?
E>Не помню, чтобы я говорил про стандартную библиотеку.
Я говорю о библиотеках предметной области. Мне казалось, что я нередко видел твои рассуждения, ставящие в минус языку черезчур большой выбор библиотек для каждой предметной области
E>Некоторые вещи должны быть частью языка (т.е. поддерживаться компилятором), а не библиотеками.
Зачем ты поскипал приводимый отбой же пример библиотек? Регулярные выражения, сериализация — разве это должно быть встроено в язык? С другой стороны, разве это не хотелось бы видеть частью стандартной библиотеки?
Здравствуйте, Alxndr, Вы писали:
A>>>Ты же первый говоришь, что хотел бы видеть (по крайней мере) некоторые из этих вещей в стандартной библиотеке, нет?
E>>Не помню, чтобы я говорил про стандартную библиотеку.
A>Я говорю о библиотеках предметной области. Мне казалось, что я нередко видел твои рассуждения, ставящие в минус языку черезчур большой выбор библиотек для каждой предметной области
Ну, мое мнение с декабря 2006 не очень изменилось. Я по прежнему сторонник описанного здесь мной варианта
. Хотя сейчас мне больше нравится идея систему управления пакетами типа RubyGems или Maven2 (хотя с последним я знаком очень поверхностно).
E>>Некоторые вещи должны быть частью языка (т.е. поддерживаться компилятором), а не библиотеками.
A>Зачем ты поскипал приводимый отбой же пример библиотек? Регулярные выражения, сериализация — разве это должно быть встроено в язык? С другой стороны, разве это не хотелось бы видеть частью стандартной библиотеки?
Регулярные выражения -- это вопрос, нужны ли они в std или нет. Но ответ на него уже дан.
А вот сериализация -- это явно для для std, имхо.
SObjectizer: <микро>Агентно-ориентированное программирование на C++.