הבלוג של ארתיום
בלוג על לינוקס, תוכנה חופשית, מוזיקה, סלסה, ומה לא!
מאמרים בנושא תכנה ומחשבים.
לשרוד פיתוח תואם "Windows" בעידן Unicode...
אם פיתחתם מעט עבור Windows אתם בוודאי מכירים את המושג שנקרא Wide-API. קרי לכל פונקציית המערכת יש שתי גרסאות: "ANSI" ו־"Wide", למשל: DeleteFileA ו־DeleteFileW, כאשר אחת מהן מקבלת char const *
והשניה wchar_t const *
.
נחמד לא? יש לך שני סוגי API שנוח לך, או לעבוד עם המחרוזות הפשוטות או לעבוד עם מחרוזות מבוססות "תווים־רחבים". אבל, לא בדיוק.
למעשה, לפי מדיניות של Microsoft, כדי לגשת לכל הכוח של מערכת ההפעלה אתה חייב להשתמש ב־Wide API אחרת... אתה אפילו לא תוכל ליצור קובץ "שלום.txt". נחמד, לא?
הבעיה שלא C99 ולא, C++, וגם לא C++0x לא מכירים במושג העמעום של wide-path ולמעשה, לפי התקן אין, דרך לפתוח קובץ שהשם לו הוא "שלום.txt" מקודד כמחרוזת של wchar_t
(רק char). כמובן חבר'ה ב־MS הם "ידידותיים" למפתחים והם הציע API חילופי: _wfopen(wchar_t const *,wchar_t const *);
וגם הוסיפו הרחבה לסטנדרט ב־Visual Studio: std::fstream::open(wchar_t const *,...);
הכל לטובת המפתח (הם כנראה לא שמעו בכלל על UTF-8)...
כאן, מתחיל הסיוט, למעשה למפתח יש שתי אופציות:
- להתעלם מהטמטום של Wide-API ולעבוד רק עם "ANSI-API" ו... התוכנה תפסיק לעבוד באופן אקראי על קבצים אקראיים, והמשתמשים המסכנים ישברו את הראש מה לא עובד.
- להתחיל לשכפל את כל הקוד שלך למקרה wide ולמקרה נורמלי.
סיוט. לא פלא, שרבים בורחים באופציה הראשונה, ולכן בשנת 2010, אנחנו נתקלים יישומים כמו Thunderbird, שלא עובד כשתיקיית המשתמש שלך בחלונות מכילה תוים עבריים.
כך גם, אני כשהתחלתי תמיכה בחלונות ב־CppCMS החלטתי להתעלם מהעובדה ש־fopen
או std::fstream::open
לא יעבדו לי. אבל בסוף ייסורי מצפון הזיזו את כף־המאזניים: מספיק לכתוב קוד גרוע... והכנתי ספריית עזר קטנה, קראתי לה booster::nowide. כל מה שהיא עושה זה: להעביר ל־namespace שלך את הכלים סטנדרטיים שקיימים ב־stdio וב־STL בכל מערכות ההפעלה נורמליות. ובחלונות, היא פשוט עוטפת אתה API _w*
עם פונקציות משלה שממירות UTF-8 ל־UTF-16 ואז קוראות לפונקציות המתאימות.
בנוסף יצרתי מחלקות תואמות ל־std::fstream שעובדות מעל stdio (ועל הדרך סוף־סוף הבנתי כיצד לממש streambuf משלך).
התוצאה? אם אתה מתכוון לתמוך בחלונו, במקום לכתוב
std::ofstream f("שלום.txt")
שמייצרת ג'יבריש במקרה הטוב אתה כותב:
booster::nowide::ofstream f("שלום.txt")
וזה עובד בצורה שקופה, כנ"ל, std::fopen, std::freopen, std::remove, std::rename - רק החלף std::
ב־booster::nowide::
.
במילים אחרות: no-more-wide-crap!
הספרייה היא חלק מ־booster של CppCMS ומופצת תחת רישיון Boost (משהו סגנון MIT). אם מישהו ירצה ספריה בלתי תלויה ב־booster, אז תגידו ואני אגזור אחת (לא מסובך בכלל).
קריאה נוספת: "Should UTF-16 be considered harmful?"
גרסת הספרית nowide התלויה בקומפיילר C++ לבלבד ניתנת להורדה כאן:
http://art-blog.no-ip.info/files/nowide.zip
אם מישהו יגיד לי כמה CMake נפלא וכמה autotools גרוע אני...
לאחרונה אני סבלתי מבעיות בניה מוזרות, פתאום אחרי עריכת כמה קבצים היו לי כשלונות בבניה. בהתחלה לא הבנתי מדוע זה קורה לי. חשבתי אולי משהו השתנה ב־svn והוא לא מעדכן נכון את ה־timestamp או אולי אני עשיתי איזושהי שטות.
היום סוף סוף הבנתי מה הבעיה ועוד פעם, כמה אני שונא CMake.
"למזלי", התקנתי שלשום CMake 2.8.1 מקוד מקור, כדי לדבג בעיה קומפיליצה ששברה את הבניה שלי (שעבדה יפה עם 2.6, אגב גם באג או ליתר דיוק שינוי מדיניות).
אז נתחיל בקצרה:
זה פלט של הפקודה בעבודה עם CMake 2.6
$ grep application.h CMakeFiles/cppcms.dir/depend.make
CMakeFiles/cppcms.dir/src/application.cpp.o: /usr/include/cppcms/application.h
CMakeFiles/cppcms.dir/src/applications_pool.cpp.o: /usr/include/cppcms/application.h
CMakeFiles/cppcms.dir/src/http_context.cpp.o: /usr/include/cppcms/application.h
CMakeFiles/cppcms.dir/src/internal_file_server.cpp.o: /usr/include/cppcms/application.h
CMakeFiles/cppcms.dir/src/rpc_json.cpp.o: /usr/include/cppcms/application.h
CMakeFiles/cppcms.dir/src/service.cpp.o: /usr/include/cppcms/application.h
CMakeFiles/cppcms.dir/src/url_dispatcher.cpp.o: /usr/include/cppcms/application.h
וזה פלט של אותה פקודה לגמרה בעבודה עם CMake 2.8
$ grep application.h CMakeFiles/cppcms.dir/depend.make
CMakeFiles/cppcms.dir/src/application.cpp.o: ../cppcms/application.h
CMakeFiles/cppcms.dir/src/applications_pool.cpp.o: ../cppcms/application.h
CMakeFiles/cppcms.dir/src/http_context.cpp.o: ../cppcms/application.h
CMakeFiles/cppcms.dir/src/internal_file_server.cpp.o: ../cppcms/application.h
CMakeFiles/cppcms.dir/src/rpc_json.cpp.o: ../cppcms/application.h
CMakeFiles/cppcms.dir/src/service.cpp.o: ../cppcms/application.h
CMakeFiles/cppcms.dir/src/url_dispatcher.cpp.o: ../cppcms/application.h
ההבדל ברור? אם לא, אני אסביר.
למרות שדגלוני הקומפיילר אומרים באופן מפורש להסתכל בתיקיה ".." "בודק התלויות" של CMake (במרכאות כפולות ומכופלות) מתעקש להסתכל על ספריות המערכת למרות שזה לא נכון בעליל.
האמת? אני לא מבין כיצד KDE אימצו כלי כזה? אני מבין שיש ל־CMake לא מעט יתרונות, רק שהבעיה שאני יותר נלחם בו מאשר נעזר בו. האם תמיכה ב־MSVC שווה את זה?
אגב, autotools משתמש בקומפיילר עצמו כדי לבנות תלויות כך שהתלויות יוצאות מדויקות לגמרי.
טוב, נו... מספיק להתבכיין, נראה לי אצטרך לשדרג את ה־CMake שלי לגרסה האחרונה.
נ.ב.: ik_5, אפילו אל תגיב... אני יודע מראש מה אתה תרצה לומר
Wikipp רץ על CppCMS 1.x.x !
בשעה טובה, המרתי את Wikipp לגרסת אלפה של CppCMS 1.x.x והיא רצה סוף־סוף ב"ייצור".
עוד שלב חשוב לשחרור גרסת בטא של CppCMS 1.x.x - שבו תוכננו מחדש ושוכתבתו מחצית מהרכיבים פנימיים שלו.
עדכנו אותי אם אתם נתקלים בבעיות.
ספריית Boost.Locale הוגשה לביקורת רשימית
בשעה טובה, הגשתי את הספריית לוקליזציה Boost.Locale לביקורת רשמית (formal review) של קהילת ה־Boost. אם היא תעבור אותה היא תתקבל כחלק רשמי של Boost.
קישורים:
אעדכן אתכם על התפתחויות בהמשך.
עדכון: זה רשמי -- Boost.Locale התווספה לרשימת הספריות הממתינות לביקורת. עכשיו צריך שמישהו מהקהילה יתנדב להיות מנהל הביקורת (review manager).
הוספת הקשר לתרגומי Gettext
אחת התכונות החסרות ביותר ב־gettext הייתה עד לא מזמן היא העדר אפשרות להעביר מידע על הקשר המילה. כך למשל, מילה "exchange" יכולה להיות מתורגמת ל"החלף" כשמדובר בחפצים ו"המר" כשמדובר בכסף. אבל אם שתי המילים מופיעות באותה תוכנה הדורשות תרגומים שונים בהקשרים שונים, ב־gettext לא הייתה שום דרך להבחין ביניהם.
החל מגרסה 0.15 התווספה התמיכה במה שנקרא הקשר. אם עד היום התרגום של מחרוזת "exchange" נעשה כ:
gettext("exchange");
אז הפונקציה pgettext מאפשרת להוסיף הקשר לתרגום:
pgettext("currency","exchange");
שיכולה להיות מתורגמת בצורה נכונה בכל הקשר. בקטלוג מחרוזות po הדוגמה הזו תופיע כ־
msgctxt "currency"
msgid "exchange"
msgstr "המר"
חשוב לזכור:
- צריך להשתמש ב־gettext עדכני
צריך להשתמש בכלי תרגום עדכניים שיודעים לעבוד עם msgctxt.
כך KBabel כלל לא תומך ב־msgctxt, poedit לא מציג מידע על הקשר. עד כמה שידוע לי, גרסאות עדכניות של lokalize ו־gtranslator תומכות בהקשרים במילוני gettext.
כמובן שגם Boost.Locale תומך בהקשרים של Gettext.