Инструменты пользователя

Инструменты сайта



// MinGW и локали

Если коротко, то всё, что связано с std::locale в MinGW не работает. Точка.

Зато вполне себе работает функционал из Си:

std::locale::global(std::locale("")); // не установит текущую локаль
setlocale(LC_ALL, ""); // установит текущую локаль, у меня это Russian_Russia.Cp1251

// Онлайн компиляторы C/C++ и не только

Иногда нужно быстро проверить работоспособность какой-то идеи или алгоритма. Хорошо, когда у вас Linux и какая-то система из семейства Unix с установленным компилятором (имхо, в 90% случаев это будет правдой), вам просто нужно открыть консоль вызвать vim/emacs/joe/mcedit/etc набросать программку и вызвать компилятор. Но иногда вы в гостях/командировке/интернет-кафе, в общем тогда, когда компилятора нет под рукой, но есть доступ в интернет. Тут помогут онлайн-компиляторы.

// Binary Logic Bit Operations In C and C++

На заметку: http://www.somacon.com/p125.php там же удобный калькулятор для перевода констант между четырмя популярными системами счисления (BIN, OCT, HEX, DEC)

// MinGW32: как избавиться от зависимостей в виде libgcc_*.dll и libstdc++-*.dll?

Почти всегда программа (особенно маленькие и без инсталлятора) для win распространяется в виде законченного бандла со всеми DLL и прочим потребством. Проблема, что программа, скомпилированная примерно так:

i486-mingw32-g++ -o foo.exe foo.cpp

как минимум требует двух DLL: libgcc_*.dll и libstdc++-*.dll, что бы избавится от них можно использовать опции -static-libgcc и -static-libstdc++:

i486-mingw32-g++ -static-libgcc -static-libstdc++ -o foo.exe foo.cpp

// Using Internet Sockets

Работая над программой столкнулся с проблемой в части функционала сокетов, пока искал в интернетах возможный пути решения проблемы, натолкнулся на интересное руководство: Beej's Guide to Network Programming. Using Internet Sockets

Руководство доступно в в различных форматах (по ссылке выше можно найти подходящие), вот самые удобные, на мой взгляд:

Руководство переведено на несколько языков, но русского среди них нет, так что если кому не терпится - переводите, будет, по крайней мере, от меня, большое спасибо и пару-тройку бутылочек пива, если окажетесь в зоне досягаемости :)

Стоит отметить, что помимо всяких Linux/Unix есть информация и про Windows, в частности будет полезно при разработке кроссплатформенных сетевых приложений.

Ещё, среди кучи ссылок, в тексте обнаружилась такая полезная: UNIX Socket FAQ - тоже кладезь знаний.

В дополнение, книжка «Linux Socket Programming by Example», можно купить на Амазоне: http://www.amazon.com/Linux-Socket-Programming-Example-Warren/dp/0789722410 или:

// Как узнать каким компилятором мы компилируемся?

Не всё делается одинаково во всех компиляторах, не на всех платформах, приходится временами городить хитрые конструкции из #if/#elif/#endif. Случайно наткнулся на шпаргалку, в которой описано, какие директивы препроцессора предопределяют конкретные компиляторы: http://sourceforge.net/p/predef/wiki/Compilers/

С того же ресурса:

А так же определение порядка байтов: http://sourceforge.net/p/predef/wiki/Endianness/

Другие ссылки на эту тематику:

// The Function Pointer Tutorials

Хорошее руководство: http://www.newty.de/fpt/index.html

PDF версия: http://www.newty.de/fpt/zip/e_fpt.pdf

Там же, прицепом: «Callback Implementations in C++»

// hypot

hypot(x, y) создан, что бы не звать sqrt(x*x + y*y)

Подробности:

// Robust Design Techniques for C Programs - на заметку

http://freetype.sourceforge.net/david/reliable-c.html

UPD 2014-06-09: сменил ссылку, прошлая убилась

// winsock2: неблокирующийся сокет

    unsigned long arg = 1;
    ioctlsocket(sockfd, FIONBIO, &arg);

в Linux:

    int arg = fcntl(sockfd, F_GETFL, NULL);
    arg |= O_NONBLOCK;
    fcntl(sockfd, F_SETFL, arg);

или универсальная функция:

void setSockNonblock(int sockfd)
{
#ifdef WIN32
    unsigned long arg = 1;
    ioctlsocket(sockfd, FIONBIO, &arg);
#else
    int arg = fcntl(sockfd, F_GETFL, NULL);
    arg |= O_NONBLOCK;
    fcntl(sockfd, F_SETFL, arg);
#endif
}

// inet_aton для windows

Если коротко: функиця заполняет структуру типа in_addr, преобразуя сведения о хосте из строкового представления.

У winsock2 нет такой функции. Есть более продвинутый аналог inet_pton, в POSIX он тоже есть, да вот только mingw про неё в windows не знает. Пичалька.

Поэтому делаем примерно следующее:

#ifdef WIN32
static int inet_aton(const char *cp, struct in_addr *inp)
{
    if (cp == 0 || inp == 0)
    {
        return -1;
    }
 
    unsigned long addr = inet_addr(cp);
    if (addr == INADDR_NONE || addr == INADDR_ANY)
    {
        return -1;
    }
 
    inp->s_addr = addr;
}
#endif

не верх совершенства и корректности, но для моих целей работало.

// Таймеры в Windows

Внезапно, правда? :)

Но волей судеб нужно было портировать программку, в которой использовался nanosleep(2). И…

Для внезапно стало откровением: В WINDOWS НЕТ ТАЙМЕРОВ ВЫСОКОГО РАЗРЕШЕНИЯ. А есть только по сути хаки и вызов Sleep, с минимальным промежутком в 10мс.

Наиболее часто рекомендуемый вариант:

void sleep_nsec(int64_t interval)
{
    int64_t time1 = 0, time2 = 0, sysFreq = 0;
 
    QueryPerformanceCounter((LARGE_INTEGER *) &time1);
    QueryPerformanceFrequency((LARGE_INTEGER *) &sysFreq);
 
    do
    {
        QueryPerformanceCounter((LARGE_INTEGER *) &time2);
    } while(((time2-time1)*1000.0*1000.0/(double)(sysFreq)) < interval);
}

Но тут мы упираем процессор в полку. Есть вариант с использованием select, но, по отзывам, он так же может ждать не менее 10мс.

Т.е., по сути, это единственный вариант для Windows, да ещё и не лишённый глюков: почти все процессоры уже умеют автоматическое понижение частоты… Чуете? Пока работает цикл, значение sysFreq может измениться (поправьте меня, если я не прав) и время потекло.

Кроме того, наткнулся на статью:
http://www.codeproject.com/KB/system/timers_intro.aspx - Timers Tutorial

Описываются различные вариант таймеров: стандартные win32, мультимедийные, Waitable Timers (не знаю как лучше перевести на русский язык), Queue Timers. Но опять таки, все периоды - миллисекунды.

// Си, прекрасное

http://eli.thegreenplace.net/2011/02/15/array-initialization-with-enum-indices-in-c-but-not-c/ - это воистину прекрасно! Использовать enum для именованной инициализации массива. Можно делать в C99, не работает в C++

Ну и ссылки, на правах мемориза:

  • http://www.advancedlinuxprogramming.com/alp-folder - по названию сайта уже понятно, что это такое :) книжка «Advanced Linux Programming» в сободном доступе. На просторах интернета встречается русский перевод, можно поискать.
  • http://mij.oltrelinux.com/devel/unixprg/ - «UNIX programming examples» - примеры кода на Си для unix, в частности - сокеты, IPC, потоки.

// На правах заметки Using Visual C++ DLLs with C++Builder

Потребовалось тут собрать для C++Builder DLL, наткнулся походу на такой мануал:
http://bcbjournal.org/articles/vol4/0012/Using_Visual_C_DLLs_with_CBuilder.htm

В мемориз, хотя, надеюсь, оно мне никогда не пригодится, собирать под винду, это ужас…

// Гоняем структуры по сети, часть вторая

Тут уже писал про проблему пересылки/приёмки структурированных данных по сети.

На нашей целевой платформе нельзя применять сторонние библиотеки (типа libpack, как в предыдущей статье), и, так вышло (лучи ненависти), что #pragma pack() то работает, то не работает, да ещё стоит условие, что данные должны быть преобразованы к сетевому виду (ntohs(), ntohl(), htons(), htonl()), поэтому я сделал на макросах такой фит ушами:

/**************************************************************************************************/
/* Паковка и распаковка данных, для передачи по сети ---------------------------------------------*/
/**************************************************************************************************/
 
// Распаковка массива в структуры
/**
 Начало блока распаковки массива данных, на которые указывает \c ptr и размера \c size
 */
#define unpack_begin(ptr, size) { \
            size_t   __addr = 0; \
            size_t   __size = (size_t)(size); \
            uint8_t *__ptr  = (uint8_t*)(ptr); \
            union { \
                uint8_t        cc[8]; \
                uint16_t       ii[4]; \
                uint32_t       ll[2]; \
                float          ff[2]; \
                double         dd;    \
            } __swapd; (void)(__swapd)
 
/**
 Распаковка очередной порции данных в \c target
 */
#define unpack_next(target) \
            pack_printf("Unpack \"%s\"... Size: %d, current offset: %d, current size: %d\n", #target, __size, __addr, sizeof(target)); \
            if ((__addr + (int)sizeof(target)) <= (__size)) \
            { \
                memcpy(&(target), __ptr + __addr, sizeof(target)); \
                __addr += sizeof(target); \
            } (void)(0)
 
/**
 Распаковка очередной порции данных в \c target, с преобразованием из сетевого вида
 Сетевое преобразование работает для простых типов размерос 2 (short), 4 (int, float)
 и 8 (lint32_tlint32_t double) байтint16_t остальных данных
 */
#define unpack_net_next(target) \
            unpack_next(target); \
            switch(sizeof(target)) \
            { \
                case 2: \
                    memcpy(&__swapd.ii[0], &target, 2); \
                    __swapd.ii[0] = ntohs(__swapd.ii[0]); \
                    memcpy(&target, &__swapd.ii[0], 2); \
                    break; \
                case 4: \
                    memcpy(&__swapd.ll[0], &target, 4); \
                    __swapd.ll[0] = ntohl(__swapd.ll[0]); \
                    memcpy(&target, &__swapd.ll[0], 4); \
                    break; \
                case 8: \
                    memcpy(&__swapd.dd, &target, 8); \
                    __swapd.ll[0] = ntohl(__swapd.ll[0]); \
                    __swapd.ll[1] = ntohl(__swapd.ll[1]); \
                    memcpy(&target, &__swapd.dd, 8); \
                    break; \
            } (void)(0)
 
/**
 Конец блока распаковки
 */
#define unpack_end() } (void)(0)
 
 
// Паковка данных из элементов струтуры в массив данных
/**
 Начало блока паковки данных в \c ptr размером \c size
 */
#define pack_begin(ptr, size) unpack_begin(ptr, size)
 
// Внутренний вспомогательный макрос, как общая часть для pack_next()/pack_net_next()
#define _pack_next_intr(source, data) \
            printf("Pack \"%s\"... Size: %d, current offset: %d, current size: %d\n", #source, __size, __addr, sizeof(source)); \
            if ((__addr + (int)sizeof(source)) <= (__size)) \
            { \
                memcpy(__ptr + __addr, &(data), sizeof(source)); \
                __addr += sizeof(source); \
            } (void)(0)
 
/**
 Пакует очередной элемент
 */
#define pack_next(source) \
            _pack_next_intr(source, source)
 
/**
 Пакует очередной элемент, преобразовывая его в сетевой вид
 */
#define pack_net_next(source) \
            switch(sizeof(source)) \
            { \
                case 2: \
                    memcpy(&__swapd.ii[0], &source, 2); \
                    __swapd.ii[0] = htons(__swapd.ii[0]); \
                    _pack_next_intr(source, __swapd.ii[0]); \
                    break; \
                case 4: \
                    memcpy(&__swapd.ll[0], &source, 4); \
                    __swapd.ll[0] = htonl(__swapd.ll[0]); \
                    _pack_next_intr(source, __swapd.ll[0]); \
                    break; \
                case 8: \
                    memcpy(&__swapd.dd, &source, 8); \
                    __swapd.ll[0] = htonl(__swapd.ll[0]); \
                    __swapd.ll[1] = htonl(__swapd.ll[1]); \
                    _pack_next_intr(source, __swapd.dd); \
                    break; \
                default: \
                    pack_next(source); \
                    break; \
            } (void)(0)
 
/**
 Конец блока паковки данных
 */
#define pack_end()            unpack_end()

Можно ещё оптимизировать, тут оверхед с переменными вспомогательными, можно без них в большинстве случаев. Ну и, кажется, не совсем корректная работа с величинами больше 4 байт (double), но на серверной машине, по-моему, вообще залепа сделана, посмотрим как работать будет :)

Пример использования для отсылки данных:

struct Data {
  short     id;
  float      d;
  long      tm;
} data;
 
data.id = 0;
data.d  = 73.3;
data.tm = 0xFEFD;
 
int n;
 
size_t data_size = 2 + 4 + 4; // sizeof(short) + sizeof(float) + sizeof(long) - поля структуры
char *data_ptr;
 
data_ptr = calloc(data_size, sizeof(char));
 
pack_begin(data_ptr, data_size);
  pack_net_next(data.id);
  pack_net_next(data.d);
  pack_net_next(data.tm);
pack_end();
 
n = send(sock, data_ptr, data_size, 0);

Пример получения данных:

struct Data {
  short     id;
  float      d;
  long      tm;
} data;
 
int n;
 
size_t data_size = 2 + 4 + 4; // sizeof(short) + sizeof(float) + sizeof(long) - сколько данных получить нужно
char *data_ptr;
 
data_ptr = calloc(data_size, sizeof(char));
 
n = recv(sock, data_ptr, data_size, 0);
 
unpack_begin(data_ptr, data_size);
  unpack_net_next(data.id);
  unpack_net_next(data.d);
  unpack_net_next(data.tm);
unpack_end();
 
// Тут структура заполнена распакованными данными

Если преобразование в сетевой вид не нужно, используйте pack/unpack_next()

// Гоняем структуры по сети

Вспоминаем о выравнивании (не говорю о том, что числовые поля нужно привести в сетевой вид, дабы на удаленной стороне была уже произведена корректная распаковка данных: man 3 htons/ntohs, man 3 htonl/htohl, man 3 endian), чешем репу - а если приёмная сторона использует другое выравнивание, нежели у нас при сборке бинарника?2)

Вижу варианты:

  • пихать все в char-буффер - некрасиво
  • поэлементно отправлять - неудобно и некрасиво
  • использовать #pragma pack() - удобно, красиво, но не супер переносимо
  • использовать систему атрибутов GCC - не переносимо

Посмотрим на использование #pragma pack(), конкретно посмотреть подробности на официальной странице: http://gcc.gnu.org/onlinedocs/gcc/Structure_002dPacking-Pragmas.html

При объявлении структуры, которая будет гоняться по сети рекомендуется отключить выравнивание:

#pragma pack(push,1)
....
#pragma pack(pop)

Простой пример:

#include <stdio.h>
 
#pragma pack(push,1)
struct test_packed {
  char ch;
  long data;
};
#pragma pack(pop)
 
struct test_unpacked {
  char ch;
  long data;
};
 
int main()
{
  printf("Packed size: %d, Unpacked size: %d\n",
         sizeof(struct test_packed),
         sizeof(struct test_unpacked));
  return 0;
}

Компилируем и выполняем:

$ gcc -o main main.c && ./main
Packed size: 5, Unpacked size: 8

Всё наглядно :)

Не рекомендуется использовать для обычной работы: в размерах выигрыша почти не получится, а в скорости доступа проиграете.

UPD: тут #864110 подкинули ещё ссылку: http://www.leonerd.org.uk/code/libpack/

PS по слухам появилось в 2.95.2 или около того.

2)
по рабоче-крестьянски: на типичной 32бит машине все переменные выравнены по 32битной длинне

// Мой "C/C++ Coding Style" версия 1.1 + Astyle

Подправил малость стандарт кодирования для своих проектов, из изменений:

  • Для функций, в которых производятся действия по инициализации допускается использовать префикс init (сокращенный вариант initialize)
  • Убрана жесткая рекомендация по использованию 0 вместо NULL в C++
  • Добавлена рекомендация использовать calloc вместо malloc в C
  • Добавлена рекомендация добавлять пробел после ключевого слова языка и открывающей скобкой
  • Рекомендация не использовать одиночные блоки if, while, for и т.п.
  • Добавлена рекомендация по использованию astyle с учетом стандартов перечисленных здесь.

Более подробно про Astyle.

Сама программа является достаточно мощным форматировщиком кода, причем с приятной документацией (для почти всегда достаточно простого astyle –help).

Моя команда astyle выглядит так:

astyle --style=ansi --indent=spaces=4 \
          --indent-classes --indent-switches --indent-preprocessor --indent-col1-comments \
          --max-instatement-indent=24 --min-conditional-indent=4 \
          --pad-oper --pad-header --unpad-paren --add-brackets \
          --convert-tabs --align-pointer=name \
          <source_file_name> 

По опциям пробежимся (за подробностями в astyle –help):

  • –style=ansi - базовый стиль форматирования, что бы на каждый чих установки не делать
  • –indent=spaces=4 - выравнивание пробелами, использовать 4 оных
  • –indent-classes - делать отступ для protected:, private: и т.д.
  • –indent-switches - делать отступ у case внутри switch
  • –indent-preprocessor - отступ у многострочных макроопределений
  • –indent-col1-comments - делать отступ однострочным комментариям (???)
  • –max-instatement-indent=24 - при разбивке выражения на строки, следующая строка обычно выравнивается по предыдущей, в частности, по открывающей скобке. Это опция ограничивает максимальный отступ в 24 символа
  • –min-conditional-indent=4 - ??? задал по аналогии :)
  • –pad-oper - делает отступы вокруг операторов, после запятых, точек с запятой, если просто - делает более удобочитаемыми математические выражения
  • –pad-header - делает отступы после ключевых слов языка и перед открывающимися скобками (у if, while, for и т.д.)
  • –unpad-paren - а вот ненужые удаляет (типа после функции), можно использовать только когда нужно
  • –add-brackets - обрамляет однострочные блоки в фигурные скобки
  • –convert-tabs - преобразует табуляцию в пробелы (эквивалентное количество, задано выше)
  • –align-pointer=name сдвигает *, & в определении к имени переменной, т.е. преобразует char* ptr или char * ptrchar *ptr