22 февраля 2008
Coding Standards
По ссылке с DOU: coding standards одного проекта. 141 страница! Очень грамотно что помимо самого правила описывают и причину его введения.
18 февраля 2008
Passing values to a function
"Стандартный" способ передать N значений типа T на C:
Можно, конечно, в цикле перегнать нужные значения в отдельный контейнер, и уже дальше передать его. Но мало того, что это не путь настоящихджедаев C++-программистов, так еще и кучу процессорного времени потеряем да и памяти заодно.
Тут стоит вспомнить, что умные дядьки уже подумали о нас, написали нужные адаптеры для итераторов, и запихнули все это в boost. Так, transform iterator достанет нам строку из
void foo(T* values, int N);Стандартное решение для C++:
template <typename it_t>Ну, если у нас есть
void foo(it_t first, it_t last);
vector<string>
и foo()
работает с string
- то все тривиально. Засада если у нас все лежит как-нибудь не так, например в map<int,string>
или в каком-нибудь vector<Object>
, у которого нужно вызвать Object::GetString()
для каждого элемента. А если еще и не все значения подряд нужны?Можно, конечно, в цикле перегнать нужные значения в отдельный контейнер, и уже дальше передать его. Но мало того, что это не путь настоящих
Тут стоит вспомнить, что умные дядьки уже подумали о нас, написали нужные адаптеры для итераторов, и запихнули все это в boost. Так, transform iterator достанет нам строку из
pair<int,string>
или Object::GetString()
, а filter iterator отфильтрует нам элементы на ходу.
14 февраля 2008
std::equal_to
С удивлением обнаружил, что std::equal_to и сотоварищи объявлены так:
Я-то наивно полагал, что должно быть как-то так:
template<class _Ty>
struct equal_to
: public binary_function<_Ty, _Ty, bool>
{ // functor for operator==
bool operator()(const _Ty& _Left, const _Ty& _Right) const
{ // apply operator== to operands
return (_Left == _Right);
}
};
Я-то наивно полагал, что должно быть как-то так:
template<class T1, class T2 = T1>
struct EqualTo : public std::binary_function<T1, T2, bool>
{
bool operator()(typename boost::call_traits<T1>::param_type Left,
typename boost::call_traits<T2>::param_type Right) const
{
return Left == Right;
}
};
08 февраля 2008
Multithreading: Messages
Хорошие языки учат хорошим манерам, но это не мешает использовать эти манеры не только в этих самых хороших языках. (Умные указатели тому пример.)
Многопоточность в C++ - довольно нетривиальная вещь, так как сам язык не дает (пока) абсолютно ничего для этого. Типичная подход - защита совместно используемых объектов критическими секциям. При неаккуратном обращении с ними недалеко до простоя потоков, а иногда и до deadlock-ов.
Как в свое время все вызовы new/delete упаковали в умные указатели, можно так же поступить и с критическими секциями, оставив для общения потоков такой вот простой интерфейс:
Послали сообщение - и бежим дальше, никаких лишних простоев.
PS. Почему auto_ptr, а не копия объекта: не всегда нужна копия, часто сообщение готовится только для того чтобы быть посланным - нет смысла делать оверхед в виде копии. А уж если нужна копия, то все просто:
Многопоточность в C++ - довольно нетривиальная вещь, так как сам язык не дает (пока) абсолютно ничего для этого. Типичная подход - защита совместно используемых объектов критическими секциям. При неаккуратном обращении с ними недалеко до простоя потоков, а иногда и до deadlock-ов.
Как в свое время все вызовы new/delete упаковали в умные указатели, можно так же поступить и с критическими секциями, оставив для общения потоков такой вот простой интерфейс:
template <typename T>(можно отказаться от шаблона в пользу IMessage вместо T - это на свой вкус и цвет).
void SendMessage(Thread, std::auto_ptr<T> Message);
Послали сообщение - и бежим дальше, никаких лишних простоев.
PS. Почему auto_ptr, а не копия объекта: не всегда нужна копия, часто сообщение готовится только для того чтобы быть посланным - нет смысла делать оверхед в виде копии. А уж если нужна копия, то все просто:
SendMessage(SomeThread, Сlone(Message));(или Message.Сlone(), у кого как)
05 февраля 2008
Quick way or right way
Бывало ли у вас так, что можно сделать:
а) как проще
б) как правильно?
У меня - постоянно ;) Примеров - хоть отбавляй. Одна из наиболее частых ситуаций: добавление нового атрибута (member-а) к объекту.
Как это выглядит:
И тут для конкретной задачи UserOfObjectUser1 требуется чтобы Object еще имел, например, и атрибут Name:
Но если сделать как проще, а не как правильно - последствия дадут знать о себе потом. Вот пример:
Истина где-то рядом:
а) как проще
б) как правильно?
У меня - постоянно ;) Примеров - хоть отбавляй. Одна из наиболее частых ситуаций: добавление нового атрибута (member-а) к объекту.
Как это выглядит:
class Object { ... };
class ObjectUser1
{
void UseObject(Object&);
Object ReturnObject();
std::container<Object> SomeMember_;
};
class ObjectUser2...
И тут для конкретной задачи UserOfObjectUser1 требуется чтобы Object еще имел, например, и атрибут Name:
class ObjectХочется думать, что Name - очень полезный атрибут для Object, и он ему совсем не помешает. Хотя предыдущим ObjectUser-ам (и user-ам этих user-ов) он вообще не нужен, но они при новой компиляции ничего и не заметят. А думать так хочется потому как альтернатива этому - такова:
{
... здесь то, что было...
// добавляем
void SetName(NameType);
NameType Name() const;
NameType Name_;
};
class ObjectWithName : public ObjectДа еще ObjectUser1 придется делать шаблонным - чтобы мог работать как с Object, так и с ObjectWithName.
{
ObjectWithName(A a) : Object(a) {}
ObjectWithName(B b, C c) : Object(b, c) {}
ObjectWithName(D d) : Object(d) {}
...
... сколько еще там у Object конструкторов?
... и не забыть добавить сюда новый конструктор если появится у Object
...
ObjectWithName(const Object& o) : Object(o) {}
void SetName(NameType);
NameType Name() const;
NameType Name_;
};
Но если сделать как проще, а не как правильно - последствия дадут знать о себе потом. Вот пример:
class FileХотя файл по определению - именованная область на диске, но Name явно здесь лишнее. Точнее - лишнее все, кроме Name. Не верите? Попробуйте создать hard link ;)
{
string Name;
int Size;
vectorDiskSectors;
void Read(...);
void Write(...);
}
Истина где-то рядом:
class DiskArea
{
int Size;
vector<int> DiskSectors;
void Read(...);
void Write(...);
};
class File
{
string Name;
shared_ptr<DiskArea> Area;
// адаптеры - расплата за правильный дизайн
void Read(...) { Area->Read(...); }
void Write(...) { Area->Write(...); }
};
Подписаться на:
Сообщения (Atom)