Саморобна амфібія. (c) wiki |
Подивимося, що це за звір. Скачати її можна на офіційному сайті. В архіві є файл INSTALL, в якому описана процедура інсталяції. На жаль, вона доволі нетривіальна і сильно зав'язана на Linux (хоча, перенесення під Windows видається можливим). Перший етап -- "make menuconfig", генерує конфігурацію бібліотеки -- можливості, які слід включити. Результатом його буде файл system_configuration.h, із купою макросів виду "#define __UCLIBCXX_STL_BUFFER_SIZE__ 32". Після чого слід виконати хитрі магічні процедури, в результаті яких буде отримано незалежний lib-файл.
Для економії часу, я схитрував --- критично важливий system_configuration.h згенерував на доступній мені Linux-машині, а решту операцій уник, додавши файли бібліотеки до проекту. Для ясності, це може виглядати так -- в директорії проекту створюється директорія uclibcxx, до неї копіюється вміст директорій src та include із архіву текстів бібліотеки, крім Makefile-ів та піддиректорії src/abi -- вона буде зайвою. В include кладемо і system_configuration.h. В середовищі створюємо відповідні групи, додаємо до них файли із цих директорій. (Готовий демонстраційний проект CoIDE, як завжди, можна скачати тут).
Додаємо libsupc++ до списку бібліотек (як це робити, не раз згадувалося в попередніх постах) -- uClibc++ покладається на неї. (libstdc++, навпаки, повністю самодостатня, фактично є "два-в-одному"). Також, вважаємо, що С-ний runtime, про який говорили раніше, присутній. "Свій" С++-ний runtime не додаємо --- цим займається libsupc++.
Пробуємо компілювати. Халяви немає -- купа помилок.
1. В файлі unwind-cxx.h є ось така конструкція:
Компілюватися вона відмовляється. В принципі, це не дуже дивно --- наш компілятор версії 4.8, автор користувався 3.3.3, (десь траплялася згадка про трохи свіжіший, 4.3, не знайшовши її повторно, можу помилятися) -- може колись воно і компілювалося. Замінив на тривіальне:
Зараз _Unwind_Exception_Class оголошено як:
Можливо, колись це був якийсь довгий, 64-бітний, int? В файлі include/support цей тип оголошено по іншому -- якраз цілим, однак компілятор підхоплює його з іншого, системного, файлу unwind-arm-common.h. Не став розбиратися глибше.
Взагалі, проблема із використанням не тих файлів заголовків доволі серйозна, тому краще включати їх як `#include "..."`. Можна замість цього, для гарантії, передати компілятору опцію "-nostdinc++". (В демонстраційному проекті так і зроблено.)
2. Що мене дуже здивувало, файл src/support.cpp не може компілюватися взагалі, так як пробує скористатися вкладеними /* */ коментарями. Так як він мав би бути порожнім, підозрюю, штатна система компіляції його просто не компілює. Виправив, написавши #if 0 .... #endif, хоча, можна було повністю викинути.
Тепер бібліотека успішно компілюється. Пробуємо використовувати (користуючись тією ж "жертвою", що розглядалася і попередніх постах.
Знову маємо ряд проблем:
А. Про С++11 мова не йде, бібліотека написана до появи цього стандарту, і радикально не переписувалася останні роки -- використання її елементів слід вимкнути.
Б. Оператор == для set не компілюється. Виправляється викиданням цього оголошення із файлу include/set:
Ретельно не перевіряв, але після цієї зміни, порівняння, так виглядає, працюють коректно. Інші асоціативні контейнери таких проблем не мають.
В. Оператор == для queue (не плутати із deque!) теж не компілюється, скаржачись, що поле c -- захищене. Воно справді захищене. Виправив, зробивши цю функцію другом класу (і, для простоти, помістивши визначення прямо в оголошення класу). Зрозуміло, що і з іншими операторами, які стосуються queue, operator< , operator!=, тощо, буде та ж сама проблема. (Але поки, для економії часу, не виправляв. Можливо, займуся окремо -- це корисно, з точки зору вивчення стандартної бібліотеки С++).
Г. Для multimap operator== оголошено, але не визначено, при тому, це оголошення конфліктує із аналогічним із __base_associative. (Закоментувавши, можна скомпілювати, працездатність не тестував). Скільки там ще таких косяків...
Ґ. Це не зовсім проблема, швидше --- дозволена стандартом ситуація, але наголосити варто. Ось такий код не компілюється:
через те, що ітератор є вбудованим типом (звичайним вказівником), а до такого результату функції не можна застосовувати "--", він не є lvalue. Проблема відома, описана в книзі "Эффективное использование STL" Скотта Мейерса, совет 28, "Научитесь использовать функцию base", стор. 119. Обходимо її описаним там способом:
Д. Сама бібліотека чесно (попередженням) зізнається, що файл limits -- неакуратний.
Е. Аналогічно, сама зізнається, що для map використовує deque, щоб економити пам'ять (детальніше мотивацію див. у офіційному FAQ, в такому рішенні може бути сенс).
Є. Там же зізнається, що підтримки локалей свідомо немає -- це надміру громіздка річ.
Ж. В купі файлів є такі коментарі, стосовно, зокрема, тих же операторів порівняння:
Програма, яка використовує ряд можливостей STL (текст див. в архіві з проектом), скомпільована із використанням libstdc++ та uClibc++. Після згаданих вище правок (повторюся, вони далеко не вичерпні!), поводяться обидві програми однаково, а їх розмір:
Виграш в розмірі - ну, скажу делікатно, "не туди". Прошивка, крім варіанту без оптимізації -- тільки зросла. Однак, є дуже значимий виграш -- в розмірі потрібної оперативної пам'яті -- на 1800 байт (майже четвертина від наявної). Іноді це дуже важливо. (Хоча, ймовірно, libstdc++ використовує якісь статичні буфери, які теж можна зменшити). Ще один важливий аспект -- бібліотека дозволяє вибірково викидати компоненти -- див. system_configuration.h нижче.
Цікаві побічні спостереження --- для даної програми -Ofast не додав нічого нового, LTO зменшує розмір прошивки всього на відсоток з хвостиком, і, явно, "вбиває" якийсь продубльований об'єкт (константу?), зменшуючи потрібну RAM на 20 байт для libstdc++, однак, збільшує на 16 для uClibc++ -- як?!
А з uClibc++ воно чудово працює. (Увага -- тут важливо, щоб випадково не підхопило системний iostream -- ймовірно, все компілюватиметься, але виводу не буде). При чому, якщо додати ці рядки до програми, що тестувалася вище, то:
Тобто, використання cout з парою маніпуляторів додало менше кілобайта до пам'яті програм 16 байт до використання RAM, а stringstream -- ще трішки більше кілобайта, без зайвих витрат RAM. Непоганий результат.
Демонстраційний проект для CoIDE, тут.
Скоро ми до контролерів ще повернемося, а на разі:
Додаємо libsupc++ до списку бібліотек (як це робити, не раз згадувалося в попередніх постах) -- uClibc++ покладається на неї. (libstdc++, навпаки, повністю самодостатня, фактично є "два-в-одному"). Також, вважаємо, що С-ний runtime, про який говорили раніше, присутній. "Свій" С++-ний runtime не додаємо --- цим займається libsupc++.
Пробуємо компілювати. Халяви немає -- купа помилок.
1. В файлі unwind-cxx.h є ось така конструкція:
// This is the exception class we report -- "GNUCC++\0". const _Unwind_Exception_Class __gxx_exception_class = ((((((((_Unwind_Exception_Class) 'G' << 8 | (_Unwind_Exception_Class) 'N') << 8 | (_Unwind_Exception_Class) 'U') << 8 | (_Unwind_Exception_Class) 'C') << 8 | (_Unwind_Exception_Class) 'C') << 8 | (_Unwind_Exception_Class) '+') << 8 | (_Unwind_Exception_Class) '+') << 8 | (_Unwind_Exception_Class) '\0');
Компілюватися вона відмовляється. В принципі, це не дуже дивно --- наш компілятор версії 4.8, автор користувався 3.3.3, (десь траплялася згадка про трохи свіжіший, 4.3, не знайшовши її повторно, можу помилятися) -- може колись воно і компілювалося. Замінив на тривіальне:
const _Unwind_Exception_Class __gxx_exception_class="GNUCC++";
Зараз _Unwind_Exception_Class оголошено як:
typedef char _Unwind_Exception_Class[8];
Можливо, колись це був якийсь довгий, 64-бітний, int? В файлі include/support цей тип оголошено по іншому -- якраз цілим, однак компілятор підхоплює його з іншого, системного, файлу unwind-arm-common.h. Не став розбиратися глибше.
Взагалі, проблема із використанням не тих файлів заголовків доволі серйозна, тому краще включати їх як `#include "..."`. Можна замість цього, для гарантії, передати компілятору опцію "-nostdinc++". (В демонстраційному проекті так і зроблено.)
2. Що мене дуже здивувало, файл src/support.cpp не може компілюватися взагалі, так як пробує скористатися вкладеними /* */ коментарями. Так як він мав би бути порожнім, підозрюю, штатна система компіляції його просто не компілює. Виправив, написавши #if 0 .... #endif, хоча, можна було повністю викинути.
Тепер бібліотека успішно компілюється. Пробуємо використовувати (користуючись тією ж "жертвою", що розглядалася і попередніх постах.
Знову маємо ряд проблем:
А. Про С++11 мова не йде, бібліотека написана до появи цього стандарту, і радикально не переписувалася останні роки -- використання її елементів слід вимкнути.
Б. Оператор == для set не компілюється. Виправляється викиданням цього оголошення із файлу include/set:
/* template <class Key, class Compare, class Allocator> _UCXXEXPORT bool operator== (const multiset<Key,Compare,Allocator>& x, const multiset<Key,Compare,Allocator>& y) { if(x.data == y.data){ return true; } return false; } */
Ретельно не перевіряв, але після цієї зміни, порівняння, так виглядає, працюють коректно. Інші асоціативні контейнери таких проблем не мають.
В. Оператор == для queue (не плутати із deque!) теж не компілюється, скаржачись, що поле c -- захищене. Воно справді захищене. Виправив, зробивши цю функцію другом класу (і, для простоти, помістивши визначення прямо в оголошення класу). Зрозуміло, що і з іншими операторами, які стосуються queue, operator< , operator!=, тощо, буде та ж сама проблема. (Але поки, для економії часу, не виправляв. Можливо, займуся окремо -- це корисно, з точки зору вивчення стандартної бібліотеки С++).
Г. Для multimap operator== оголошено, але не визначено, при тому, це оголошення конфліктує із аналогічним із __base_associative. (Закоментувавши, можна скомпілювати, працездатність не тестував). Скільки там ще таких косяків...
Ґ. Це не зовсім проблема, швидше --- дозволена стандартом ситуація, але наголосити варто. Ось такий код не компілюється:
printf("Reverse iterator base--: %i\n", *(--ritr.base()));
через те, що ітератор є вбудованим типом (звичайним вказівником), а до такого результату функції не можна застосовувати "--", він не є lvalue. Проблема відома, описана в книзі "Эффективное использование STL" Скотта Мейерса, совет 28, "Научитесь использовать функцию base", стор. 119. Обходимо її описаним там способом:
printf("Reverse iterator base--: %i\n", *(++ritr).base());
Д. Сама бібліотека чесно (попередженням) зізнається, що файл limits -- неакуратний.
Е. Аналогічно, сама зізнається, що для map використовує deque, щоб економити пам'ять (детальніше мотивацію див. у офіційному FAQ, в такому рішенні може бути сенс).
Є. Там же зізнається, що підтримки локалей свідомо немає -- це надміру громіздка річ.
Ж. В купі файлів є такі коментарі, стосовно, зокрема, тих же операторів порівняння:
/* Non-member functions. These are at the end because they are not associated with any
particular class. These will be implemented as I figure out exactly what all of
them are supposed to do, and I have time.
*/
Питання розміру
Програма, яка використовує ряд можливостей STL (текст див. в архіві з проектом), скомпільована із використанням libstdc++ та uClibc++. Після згаданих вище правок (повторюся, вони далеко не вичерпні!), поводяться обидві програми однаково, а їх розмір:
libstdc++ | uClibc++ | |||||||
---|---|---|---|---|---|---|---|---|
text | data | bss | Разом | text | data | bss | Разом | |
O0 | 122924 | 2256 | 2208 | 127388 | 108920 | 2272 | 400 | 111592 |
Os | 81088 | 2256 | 2208 | 85552 | 85580 | 2272 | 400 | 88252 |
Os, LTO | 80192 | 2236 | 2208 | 84636 | 83636 | 2288 | 400 | 86324 |
-Ofast | 82200 | 2256 | 2208 | 86664 | 86792 | 2272 | 400 | 89464 |
-Ofast,LTO | 81244 | 2236 | 2208 | 85688 | 87724 | 2288 | 400 | 90412 |
-O3 | 82200 | 2256 | 2208 | 86664 | 86792 | 2272 | 400 | 89464 |
-O3, LTO | 81244 | 2236 | 2208 | 85688 | 87724 | 2288 | 400 | 90412 |
Цікаві побічні спостереження --- для даної програми -Ofast не додав нічого нового, LTO зменшує розмір прошивки всього на відсоток з хвостиком, і, явно, "вбиває" якийсь продубльований об'єкт (константу?), зменшуючи потрібну RAM на 20 байт для libstdc++, однак, збільшує на 16 для uClibc++ -- як?!
Потоки та стрічки
Не зважаючи на вищесказану критику, з використанням libstdc++ ви на цьому контролері аж ніяк не зможете зробити таке:cout << "Test:" << right << setw(20) << 555 << endl; stringstream ss; ss << "Test:" << right << setw(20) << 555 << endl; cout << ss.str() << flush;
А з uClibc++ воно чудово працює. (Увага -- тут важливо, щоб випадково не підхопило системний iostream -- ймовірно, все компілюватиметься, але виводу не буде). При чому, якщо додати ці рядки до програми, що тестувалася вище, то:
cout
| cout + stringstream
|
|||||||
---|---|---|---|---|---|---|---|---|
text | data | bss | Разом | text | data | bss | Разом | |
Os, LTO | 84544 | 2288 | 400 | 87232 | 86656 | 2288 | 400 | 89344 |
Демонстраційний проект для CoIDE, тут.
system_configuration.h
Наведу вміст цього файлу -- з назва макросів видно, які можливості присутні, та що із того можна викинути для економії./* * Automatically generated C config: don't edit */ /* * Version Number */ #define __UCLIBCXX_MAJOR__ 0 #define __UCLIBCXX_MINOR__ 2 #define __UCLIBCXX_SUBLEVEL__ 4 /* * Target Features and Options */ #define __UCLIBCXX_HAS_FLOATS__ 1 #define __UCLIBCXX_HAS_LONG_DOUBLE__ 1 #define __UCLIBCXX_HAS_TLS__ 1 #define __WARNINGS__ "-Wall" #define __BUILD_EXTRA_LIBRARIES__ "" #define __HAVE_DOT_CONFIG__ 1 /* * String and I/O Stream Support */ #undef __UCLIBCXX_HAS_WCHAR__ #define __UCLIBCXX_IOSTREAM_BUFSIZE__ 32 #define __UCLIBCXX_HAS_LFS__ 1 #define __UCLIBCXX_SUPPORT_CDIR__ 1 #define __UCLIBCXX_SUPPORT_CIN__ 1 #define __UCLIBCXX_SUPPORT_COUT__ 1 #define __UCLIBCXX_SUPPORT_CERR__ 1 #undef __UCLIBCXX_SUPPORT_CLOG__ /* * STL and Code Expansion */ #define __UCLIBCXX_STL_BUFFER_SIZE__ 32 #define __UCLIBCXX_CODE_EXPANSION__ 1 #define __UCLIBCXX_EXPAND_CONSTRUCTORS_DESTRUCTORS__ 1 #define __UCLIBCXX_EXPAND_STRING_CHAR__ 1 #define __UCLIBCXX_EXPAND_VECTOR_BASIC__ 1 #define __UCLIBCXX_EXPAND_IOS_CHAR__ 1 #define __UCLIBCXX_EXPAND_STREAMBUF_CHAR__ 1 #define __UCLIBCXX_EXPAND_ISTREAM_CHAR__ 1 #define __UCLIBCXX_EXPAND_OSTREAM_CHAR__ 1 #define __UCLIBCXX_EXPAND_FSTREAM_CHAR__ 1 #define __UCLIBCXX_EXPAND_SSTREAM_CHAR__ 1 /* * Library Installation Options */ #define __UCLIBCXX_RUNTIME_PREFIX__ "/usr/uClibc++" #define __UCLIBCXX_RUNTIME_INCLUDE_SUBDIR__ "/include" #define __UCLIBCXX_RUNTIME_LIB_SUBDIR__ "/lib" #define __UCLIBCXX_RUNTIME_BIN_SUBDIR__ "/bin" #define __UCLIBCXX_EXCEPTION_SUPPORT__ 1 #define __IMPORT_LIBSUP__ 1 #define __IMPORT_LIBGCC_EH__ 1 #define __BUILD_STATIC_LIB__ 1 #undef __BUILD_ONLY_STATIC_LIB__ #undef __DODEBUG__
Висновки
Бібліотека доволі неоднозначна, хоч мені вона подобається. Плюси:- Компактність джерельних текстів -- вона значно менша, ніж "реалізація з коробки".
- Відносна простота і зрозумілість (якщо не вірите -- загляньте у файли заголовків GCC та порівняйте.
- Внаслідок компактності, можливість користуватися на контролерах потоки та стрічки.
- Складна система компіляції. В принципі, вирішується. :-)
- Стійке відчуття певної неохайності -- див. описані вище проблеми. Через що невідомо, наскільки їй можна довіряти без тестування кожного шматочка.
Скоро ми до контролерів ще повернемося, а на разі:
Дякую за увагу!
Немає коментарів:
Дописати коментар