среда, 21 августа 2013 г.

minimum rule

minimum rule
/** Правило минимального изменения кода:

    Если нет предпочтений к оформлению какой-то многострочной
    конструкции в коде, то следует писать так, чтобы при дальнейшем
    возможном изменении кода, количество изменяемых строк было
    минимально. Это упрощает жизнь при работе с системой контроля
    версии и анализе изменений.

    Примеры: Конструктор класса нужно оформлять так: */
object()
  : m_active(false)
  , m_name("object")
  , m_value(0)
{
}
/** При добавлении нового члена класса в список инициализации просто
    появляется новая строка, другие не изменяются.

   У enum после последнего значения нужно ставить запятую: */
enum type_id
  {
    type_id_one,
    type_id_two,
  };
/** При добавлении нового значения или удалении изменится только одна
    строка.

    Не следует выравнивать аргументы функции по открывающей скобке.
    так как если длина имени функции изменится, то придётся
    выравнивать и аргументы, что приведёт к лишим изменениям кода. */

/// Плохо:
void myfunction(int a1,
                int a2,
                int a3);

/// Хорошо:
void myfunction(
    int a1,
    int a2,
    int a3);


/// И так далее.

shared_ptr factory

tmp-12112Ddy
/** Многие до сих пор пишут свои фабрики объектов, хотя есть boost::shared_ptr и boost::factory
    При чём есть возможность передавать аргументы при создании
    объектов. Например, есть система полиморфных объектов: */

#include <map>
#include <iostream>
#include <boost/shared_ptr.hpp>
#include <boost/bind.hpp>
#include <boost/assign.hpp>
#include <boost/function.hpp>
#include <boost/functional/factory.hpp>

#define LOG(WHAT) std::cout << WHAT << std::endl

struct A
{
  virtual void func() = 0;
protected:
  ~A() {}
};

struct B: A
{
  B(int a): a(a) {}
  void func() { LOG("it's B"); }
  int a;
};

struct C: A
{
  C(int a): a(a) {}
  void func() { LOG("it's C"); }
  int a;
};

/** Объекты при создании принимают один аргумент: int.  Давайте
    создадим фабрику, которая будет создавать объекты, помещённые в
    смарт поинтер boost::shared_ptr. Для создания фабрики достаточно
    описать следующее: */
typedef boost::function<boost::shared_ptr<A>(int)> factory_t;

/** Связываем фабрики конкретных типов "B" и "C" с идентификаторами:
    "class_B" и "class_C": */
std::map<std::string, factory_t> fact = boost::assign::map_list_of
    ("class_B", factory_t(boost::factory<boost::shared_ptr<B> >()))
    ("class_C", factory_t(boost::factory<boost::shared_ptr<C> >()))
    ;

/** Использование: Создаём объект по идентификатору "class_B" и
    передаём аргумент, например, 33. */
boost::shared_ptr<A> a = fact["class_B"](33);
a->func();

/** Можно добавлять "фабрики по умолчанию", более подробно в доках
    http://www.boost.org/doc/libs/1_51_0/libs/functional/factory/doc/html/index.html */
 

template-template parameters

template-template parameters
/** Шаблонные шаблонные параметры это прошлый век, они не удобны. Я
    постараюсь объяснить как их избежать. Есть класс A, в котором
    нужно объявлять контейнер из произвольного типа, а тип контейнера
    передавать параметром шаблона: */
template <typename T, typename Container>
struct A
{
  typedef Container<T> type;
};

/** Но так как Container это шаблон, принимающий два параметра: тип
    данных и тип аллокатора, то следует писать так: */

template <typename T, template<typename, typename> class Container>
struct A
{
  typedef Container<T, std::allocator<T> > type;
}

/** Это плохо, я не хочу указывать явно и самостоятельно тип
    аллокатора, это может првести к ошибке.  Ещё одна из причин,
    почему шаблонные шаблонные параметры неудобны.  Чтобы получить тип
    теперь мы должны вызвать: */
typedef A<int, std::vector>::type type;

/** В boost mpl есть плейсхолдеры, которые используются для создания
    лямбда метафункций.  Так вот, с помощью них можно избежать
    создания шаблонных шаблонных параметров.  То есть я передаю в
    шаблон A уже инстанцированный шаблон так: */
typedef A<int, std::vector<boost::mpl::_1> >::type type;

/** Тогда в шаблоне A инстанцирование конейнера с произвольным типом
    будет выглядеть так: */

#include <boost/mpl/apply.hpp>

template <typename T, typename Container>
struct A
{
  typedef typename boost::mpl::apply<Container, T>::type type;
};

macros-function

macros-function
/** В #gcc есть возможность писать макросы, возвращающие значения: */

#define STR(WHAT) ({ std::stringstream e; e << WHAT; e.str(); })

/** Этот макрос получает строку, например: STR(33 << 44) получит
    строку "3344". В Visual Studio это было сделать нельзя. Благодаря
    стандарту c++11 сейчас можно написать макрос, который будет делать
    это же: */
#define STR(WHAT) ([]() -> std::string { std::stringstream e; e << WHAT; return e.str(); }())

/** Но с появлением variadic templates в c++11 вряд ли это уже будет нужно. */

shared descriptor

shared object
/** Автоматическое закрытие файла с подсчётом ссылок. */

/** Полезно отдавать дескрипторы во владение объектам, которые при
    уничтожении закрывают этот дескриптор (RAII). Чаще это делается
    так: */
struct holder
{
  holder(int fd): m_fd(fd) {}
  ~holder() { ::close(fd); }
private: const int m_fd;
};

/** Минус данного подхода - нет счётчика ссылок. Для объектов типа
    FILE* можно было бы использовать так: */
boost::shared_ptr<FILE> file(fopen(), fclose_check);
/** Вторым аргументом указывается функтор, удаляющий объект, который
    проверяет, что входной аргумент не NULL, иначе SIGSEGV */

/** Но что делать для типов не являющихся указателями? Есть
    решение: */

template <typename T>
struct shared_descriptor: public boost::shared_ptr<T>
{
  template <typename D>
  shared_descriptor(T value, D deleter)
  : boost::shared_ptr<T>(new T(value), boost::bind(deleter, value))
  , m_value(get()) {}
private:
  boost::shared_ptr<T> m_value;
};

/** Пример: */
boost::shared_ptr<int> file1 = shared_descriptor<int>(open("file.txt"), close);
std::cout << "descriptor: " << *file << std::endl;

array size

array size
/** Как определить длину массива в C++? */

/** Определять длину массива как sizeof(arr)/sizeof(*arr) не совсем
    безопасно, так как arr может быть обычным указателем.  В Google
    Chrome используется для этого такой макрос: */ 

template <typename T, size_t N>
char (&ArraySizeHelper(T (&array)[N]))[N];
#define ARRAY_SIZE(array) (sizeof(ArraySizeHelper(array)))

/** Компилятор ругнётся, если попытаться подставить обычный
    указатель. Для простоты и лёгкости понимания определения функции
    ArraySizeHelper, возвращающей ссылку на массив char[N] можно
    переписать так: */

#include <boost/type_traits/add_reference.hpp>

template <typename T, size_t N>
typename boost::add_reference<char[N]>::type ArraySizeHelper( T (&)[N] ) ;
#define ARRAY_SIZE(array) (sizeof(ArraySizeHelper(array)))

/** Почитать подробнее:
    http://easy-coding.blogspot.com/2011/05/blog-post_24.html
    http://stackoverflow.com/questions/6376000/how-does-this-array-size-template-work */

logger extension

logger extension
/** Продолжение предыдущих постов про logger */

/* Давайте не много порассуждаем: зачем макросы? что значит легко
   используем? что значит расширяем? а эффективен ли?*/

/** Макросы. В данном случае они действительно необходимы. Во-первых,
    это компактно выглядит. Хотя вы можете сказать, что сложного
    сделать так? */
typedef logger<std::ostream> std_logger;
std_logger mylogger()
{
  return std_logger(std::cout);
}

/** и использовать так: */
mylogger().get() << "Hello, world!" << 123;

/** Вы правы. Но в макросах у нас идёт ещё и проверка уровня
    лога. Проверку уровня лога нельзя выносить в функцию, так как
    понадобится излишнее конструирование сообщения, а если это
    DEBUG-сообщение, а уровень выводимых сообщений ERROR, то будет
    происходить лишние вычисления сообщения. Если условие ложное, то
    тело условия не вычисляется. Ещё один аргумент в пользу макросов -
    возможность указать местоположение в коде с помощью _FILE_ и
    _LINE_ и т. д. */

/** Легко используем.  За счёт использования шаблона, можно подставить
    не только std::ostream, но и вообще любой другой класс,
    поддерживающий операцию "<<", даже
    boost::archive::text_oarchive. */

/** Легко расширяем.  Допустим понадобилось синхронизовать сообщения
    между различными тредами в файл. Делается просто с помощью
    boost::mutex. Создадим тредобезопачный логгер: */

template <typename Out, typename Mutex>
struct logger_safe: logger<Out>
{
  logger_safe(Out& out, Mutex& mutex)
    : logger<Out>(out)
    , m_locker(mutex) {}

private:
  boost::unique_lock<Mutex> m_locker;
};

/** При создании он будет лочить мьютекс, который ему
    подаётся. Использование: */

typedef logger_safe<std::ostream, boost::mutex> std_logger_safe;

boost::mutex mutex;
std_logger_safe(std::cout, mutex).get() << "Hello, world! Safely!";

/** То есть мютекс лочится на время существования объекта, когда мы
    вводим цельную строку и разлочивается автоматически после
    окончания ввода */

/** Эффективность.  Обычно логгеры формируют строку, чаще std::string,
    которую ещё надо сформировать. Этот логгер обращается
    непосредственно к объекту вывода и не требует промежуточной
    временной строки, это быстро и очень удобно.  */

/** Заключение.  Я не говорю, что этот логгер подходит для любого
    проекта, но на основе него и его принципов можно написать гораздо
    более мощные логгеры. Позже я покажу как выводить в несколько
    файлов/потоков одновременно, учитывая уровень сообщений. */