התחליף של sprintf עם std::string

ilankt

New member
התחליף של sprintf עם std::string

מהו? ואפשר דוגמא? בתודה מראש
 

eyalbd

New member
אין בסטנדרד

אבל אפשר לכתוב בעצמך. הנה משהו שכבר שלחתי לכאן פעם. #include <cstdarg> #include <string> #include <vector> std::string string_printf(const char* format, ...) { va_list arg_ptr; va_start(arg_ptr, format); char buf[256]; int size = vsnprintf(buf, sizeof buf, format, arg_ptr); if (size < sizeof(buf)) return std::string(buf, buf + size); std::vector<char> buffer(size + 1); size = vsnprintf(&buffer[0], buffer.capacity(), format, arg_ptr); return std::string( buffer.begin(), buffer.begin() + size ); }
 

eyalbd

New member
התקפשש הישור...

#include <cstdarg> #include <string> #include <vector> std::string string_printf(const char* format, ...) { va_list arg_ptr; va_start(arg_ptr, format); char buf[256]; int size = vsnprintf(buf, sizeof buf, format, arg_ptr); assert(size >= 0); if (size < sizeof(buf)) return std::string(buf, buf + size); std::vector<char> buffer(size + 1); size = vsnprintf(&buffer[0], buffer.capacity(), format, arg_ptr); assert (size < buffer.capacity()); return std::string( buffer.begin(), buffer.begin() + size ); }​
 

אלדד28

New member
ובמקום 256

אפשר לעשות strlen למחרוזת הפורמט, לעבור על הפרמטרים, להוסיף X מוערך לכל s% ו-16 לכל פרמטר אחר, להוסיף עוד איזשהו מקדם ביטחון, וזה נותן יותר סיכוי לגודל מתאים.
 

Milo M

New member
אלדד? באג קופץ

אימה ופחד להעריך גודל של s% = הזמנה לבאגים בזמן ריצה כבר עדיף 256 למרות שגם זה לא מושלם וחוץ מזה למה לא להשתמש במחלקה CString שנותנת את כל הדרוש? (אלדד לא מצאתי אף דליפת זיכרון במחלקה אשמח אם תאיר את עיני)
 

אלדד28

New member
אימה ופחד? ../images/Emo6.gif

הערכה של הגודל הצפוי של המחרוזת טובה משמעותית מקביעת מספר מסוים. לא להשתמש ב-CString כי יש בה דליפות זיכרון, ואם אני לא משכנע מספיק, אתה מוזמן להסתפק בטיעון הפורטביליות. המחלקה לא פורטבילית וזו סיבה מספקת להשתמש ב-string במקומה.
 

ברנדל

New member
שום דליפות זיכרון

ואם שואלים אותי CString עדיפה בהרבה על string. הבעיה היא שזה לא פוטבלי.
 

eyalbd

New member
הייתי סקרן לדעת במה עדיף

מהם הפיצ'רים שבגללם אתה אומר ש CString עדיף על std::string? או אולי מה חסר לזו הסטנדרטית?
 

ברנדל

New member
היא תומכת ביותר דברים

והעבודה איתה פחות מסורבלת. סתם לדוגמא שאלו כאן קודם שאלה לגבי איך מעבירים ל string את הפונקציונליות של sprintf. בעצמך אתה יודע איזה מסורבל זה. ב CString אתה פשוט ניגש למתודה format של המחלקה, שמקבלת כפרמטרים את מה ש sprintf מקבלת. או שהקוד הבא:
std::string h; h = "eyal"; char *p = h;​
לא עובד כי אין למחלקה תמיכה באופרטור const char * ויש עוד מלא דברים שאני יכול להמשיך לפרט שהופכים את השימוש ב CString לאינטואיטיבי ומהיר בהרבה. מה עוד שאם בין כה וכה אתה משתמש בסביבת העבודה של windows אז CString תומכת בפעולות נוספות כגון LoadString שמקבלת סימן מזהה של קובץ המשאבים וטעינת המחרוזת בדומה ל sprintf.
 

ברנדל

New member
חוץ מזה

ב CString יש לך אפשרות לקבל פוינטר ל char שהוא לא const לשנות את המחרוזת לדוגמא ע"י קריאה לפונקציית api שדורשת char * (לא const). ובאמצעות פקודה של המחלקה לעדכן את ה status החדש של CString (שנשענת עדיין על אותה מחרוזת שהשתנתה) משתמשים בזה המון!!! איך עושים את זה ב std::string?
 

voguemaster

New member
לא עושים! וטוב שכך!

אם אתה מחזיר מצביע ל-char שהוא לא const אז מה הקטע בלעבוד עם CString ?? זה מגוחך בצורה שלא תיאמן וזה מתקון להרבה באגים. בינתיים לא נתת דוגמא למשהו קונקרטי... (דוגמאות של קוד יהיו גם במקום ד"א)
 

ברנדל

New member
איך אתה אומר שלא נתתי שום דוגמא

אם נתתי 3. נחזור לשלושת היתרונות שציינתי (יש הרבה יותר) א. format:
CString str; str.Format("%s%d", "voguemaster" , 123);​
אני בטוח שכל אחד היה רוצה שמחלקת ה string שלו תתמוך בזה. זה שימושי מעין כמוהו. עם מחלקת std::string זו פעולה מורכבת הרבה יותר כמו שהופיע בשרשור. ב. חפיפה ל const char *. מפשט את השימוש ב CString ומקל את השימוש בה לעין ערוך. לדוגמא בשליחת פרמטרים לפונקציה כדוגמת strcpy. פשוט שולחים את האוביקט עצמו ומעתיקים. ג. קבלת buffer של char * מהאוביקט. לא יודע, אצלי זה אף פעם לא גרם לשום bug זה רק הקל עלי את העבודה חבל"ז. יש המון פונקציות שמקבלות בתור פרמטר char * ואמורות למלא אותו. בוא נקשה את העניין ונגיד שבזמן קומפילציה עדיין אין לך מושג מה אורך המחרוזת שתצטרך. אם אתה לא משתמש ב CString תאלץ תחילה להקצות הקצאה דינמית (לפי נתונים שהתקבלו בזמן ריצה) למערך char, לאחר מכן לקלוט את המחרוזת לאחר מכן להעתיק אותה ל string ולאחר מכן לעשות delete למחרוזת ההתחלתית. (אני מניח כאן שאנחנו רוצים עדיין להשתמש במחרוזת לצרכים נוספים כגון ניתוח המחרוזת למשל, ולכן מעתיקים אותה ל string, אחרת חבל על כל הדיון) אם לעומות זאת אתה משתמש ב CString העניין פשוט הרבה יותר:
CString I; GetPrivateProfileString("aa","aa","aa",I.GetBuffer(255),2,"aa"); I.ReleaseBuffer();​
ויש לך אוביקט I מוכן לפעולה. ואם כבר מציגים את הדוגמא הזו, שים לב שכל שאר הפרמטרים של הפונקציה שהם const, היו יכולים גם לקבל אוביקטים של CString כי יש חפיפה אוטומיטית (סעיף ב'). ובנוסף כמו שציינתי, אם בלא וכי אתה עובד עם windows אז CString מאפשר לך LoadString ישירות מ ID של קובץ המשאבים.
 

אלדד28

New member
תשובות.

1. format אכן חסר ב-std::string. מצד שני, יש StringStream, ויש אפשרות לכתוב את מה שאייל כתב, או אפילו לרשת מ-std::string ולהוסיף לו פונקציית format. לא כזה סיפור גדול. 2. אין ארוחות חינם. בגלל שההתייחסות ל-CString היא כמו ל-* Char, יש לזה הרבה משמעויות נגזרות. למשל, שאי אפשר לרשת ממנה. למשל, שהיא עושה משחקי זיכרון שמבלבלים תוכנות כמו Purify וכמו BoundsChecker. אני לא רואה בזה דבר חיובי. אגב, זה לא * const char, אלא * char פשוט. 3. שוב, אני חוזר על מה שאלי אמר. אם אתה מתייחס אל זה בתור * char, איבדת את כל המשמעות של ה-class. למעשה, CString אמורה להסתיר ממך את מה שקורה בקרביים שלך. אין פה שום encapsulation של הנתונים (הכמסה, לאוהבי העברית), וזה מתכון בטוח לבאגים. 4. LoasString היא גם WIN32 API פשוט שלא דורש CString בכלל.
 

ברנדל

New member
הערות

1. למה אנחנו רוצים לעטוף במחלקה אם לא בשביל לפשט את הענינים זו לא הדוגמא היחידה. זה שאפשר ב std עם StringStream, איפה פה הטענה אתה מנסה להוכיח שזה יותר מורכב? 2. לא נכון , יש לך חפיפה ל const ולא ל char * רגיל. כלומר אתה יכול לשלוח את האוביקט לפונקציה מחרוזת , המחרוזת תתקבל כ const. לכן קטע הקוד הבא לא יעבוד:
CString x; strcpy(x,"eldad");​
כי אתה לא יכול לשלוח const כפרמטר שעלול לשנות את הפרמטר. לעומת זאת אם תשלח את האוביקט כפרמטר השני אז אין לך שום בעיה. 3. גם ב string אתה יכול לגשת לקרביים c_str (מעניין למה צריך) היתרון של CString הוא שאתה יכול לגשת גם לקרביים באופן שהוא לא const, ע"י GetBuffer. וכן זה יתרון כי כמו שציינתי יש פונקציות שמקבלות char * וממלאות אותו, כגון strcpy, לאחר המילוי אתה רוצה להתיחס למחרוזת הממולאת כל אוביקט, ובזה היתרון (תסתכל בקוד בתגובה הקודמת). כלומר רק לצורך המילוי בשליחה לפונקציה התיחסת כקרביים ולאחר מכן אתה מתיחס כאוביקט. זה שימושי מעין כמותו ואין שום דליפות. ולמי ששואל למה אני צריך קרביים - אז זו הסיבה. 4. יופי טופי אבל אז אתה מקבל את זה למחרוזת (קרביים) ואם אתה רוצה לעבוד עם std::string אתה צריך להעתיק את זה לשם אחר כך. עם CString זה מידי. 5. אני לא מבין על מה הויכוח , זה כאילו אני מתווכח על משהו ברור. הרי מדובר ביתרונות שימושיים ביותר.
 

אלדד28

New member
אתה סותר את עצמך.

1. תחליט. או שעוטפים במחלקה, או שלא עוטפים במחלקה. מחלקה שנותנת לך גישה חופשית לכל הקרביים שלה היא לא מחלקה שעוטפת משהו. היא סתם אוסף UTILITIES מסוכן למדי. וכמו שאמרתי, להוסיף format ל-std:string הוא עניין קליל. 2. אתה טועה. כתובת התחלת ה-CString היא כתובת התחלת ה-*char. הם עושים שמיניות באוויר שם בפנים, רק כדי שזה יקרה. וזה מכוער לגמרי. 3. c_str נותנת לך ייצוג CONST-י של ה-buffer שלה. זה set/get בסיסי, ווטסון. לעומת זאת, std::string עושה טוב כשהיא לא נותנת לך לגשת לתוכן שלה. זה מאפשר לה גמישות בעתיד, דבר שאי אפשר לומר על CString. לא, זה לא יתרון. בכל מקרה אתה צריך לציין את הגודל, ב-GetBuffer, ולכן אין שום הבדל בין הקוד שנתת לבין:
char temp[256]; blablabla(...,temp,...); s = temp;​
תראה איזה קטע, זה אפילו אותו מספר שורות - ופורטבילי!!!
4. "מיידי" זה לקרוא ל-ReleaseBuffer?
זה לא מיידי, ברנדל. 5. ברור? ברור לך, ואתה טועה. היתרונות שהצגת מוטלים בספק, וגם אם הם יתרונות, יש מאחוריהם כל כך הרבה חסרונות, שלי לא ברור על מה אנחנו מתווכחים. הרי ברור שפתרון סטנדרטי עדיף בהרבה על פתרון מונפץ של מונופול.
 

ברנדל

New member
לא!

1. סעיף 1 דיבר על format אל תערבב , מי ישמע כמה דברים מצפים להם כבר ממחלקה מסוג זה. format זה יתרון חשוב מאוד ובנוסף UpperCase ו Trimminig משני הכיוונים שאין אוטומטית ב string 2. אתה טועה:
CString::eek:perator LPCTSTR operator LPCTSTR ( ) const; Return Value A character pointer to the string’s data. Remarks This useful casting operator provides an efficient method to access the null-terminated C string contained in a CString object. No characters are copied; only a pointer is returned. Be careful with this operator. If you change a CString object after you have obtained the character pointer, you may cause a reallocation of memory that invalidates the pointer.​
ציטוט מ MSDN אם אתה רוצה char * תזדקק ל GetBuffer. 3. לגבי אותו מספר שורות , אכן כן (ממלא את פי מיים) בכל מקרה ב std::string אתה זקוק למבנה נתונים נוסף לשם כך!!. ב CString אתה לא זקוק, כפי שהראתי בדוגמא. ובכלל אין עם זה שום בעיה כמו שאתה מתיחס ל c_str כפונקציית שרות שמביאה לך ייצוג, כך אני מתיחס ל GetBuffer. למי שטוען שזה מסוכן (לא שאני מבין איך, שלא ישתמש, אף אחד לא מכריח, לא בכוח) 4. LoadString לא דורש שום Release זה מידי, וזה שם. 5. אכן std::string הוא פורטבלי ומכאן יתרונו. בפונקציונליות CString לוקח בגדול. 6. צירפתי קישור למאמר נחמד, מעניין שאין מאמר שעושה פעולה הפוכה
 

אלדד28

New member
נו באמת, ממך הייתי מצפה ליותר.

1. כמו שאמרתי, format אפשר להוסיף בקלילות למחלקה, אם זה מה שחסר לך. ומה זאת אומרת "אל תערבב"? אנחנו מדברים על מחלקה אחת, שיש בה "יתרונות" ויש בה המון חסרונות. ברור שאני "אערבב", ברנדל. המחלקה היא גוש אחד, וצריך להתייחס אליה ככזו, על כל מגרעותיה. היא לא OOP-ית והיא לא פורטבילית. 2. האופרטור שהבאת מתייחס ל-LPCTSTR. נסה להמיר את המחרוזת ל-*char. 3. "מבנה נתונים נוסף"? אתה מדבר על מערך של char-ים? זה "מבנה נתונים", זה? תעשה לי טובה
. ושנית, c_str מחזירה לך CONST, ואילו GetBuffer נותנת לך לשנות את תוכן ה-STRING. זה פתח לצרות. למשל, מה יקרה אם ביקשתי 10 תווים ובטעות דרסתי אותם? 4. אתה צודק, דיברתי על מיידי בקונטקסט של (3). לגבי LoadString, אתה צודק, בשימוש ב-std::string אני איאלץ להעתיק את ה-BUFFER אל המחרוזת. ללא ספק, לתוכנות בהן 90% מהפעילות היא LoadString הייתי ממליץ לעבוד עם CString
. 5. שטויות, ברנדל. הבעיה היא ש-CString היא מאוד, מאוד ספציפית. אתה מבין, כשתכננו את std::string תכננו אותה לכל מחרוזת שהיא, בכל פלטפורמה שהיא. זה היתרון הגדול שלה. אם אתה תוכניתן WINDOWS שנשבע אמונים ל-MFC ולא מתכנן לעולם לעבור למשהו אחר, אולי עדיף לך לעבוד עם CString. אישית זה נראה לי כמו לכתוב year בשתי ספרות, אבל זו כבר הבעיה שלך. אתה אח"כ תצטרך לעשות שמיניות באוויר כדי לעבור ל-string נורמלית, פורטבילית, וגמישה. בד בבד, זה ש-string גמישה מונע מבעדה להחזיק באי אילו פונקציונליות. מחלקה כמו CString שחושפת את הקרביים שלה קובעת הלכה למעשה גישה מאוד מסויימת שאי אפשר יהיה לשנות אותה אח"כ. אתה מסתמך היום על כך ש-GetBuffer מחזירה לך כך וכך. מחר יכול להיות שירצו לשנות, ואז יצטרכו לעשות CString2. נהדר
6. ברור שאין מאמר שעושה פעולה הפוכה, כי אין צורך. CString קנתה אותך תמורת נזיד עדשים. כמו שכל בר דעת יכול לראות במאמר, הפונקציונליות של CString ממומשת במספר שורות קוד פשוטות, ולכן אין שום סיבה בעולם לוותר על הגמישות ועל הפורטביליות של std::string.
 

ברנדל

New member
בוא נסיים את זה

רק כמה דברים: 2. זה בדיוק מה שאמרתי יש אופרטור העמסה במחלקה ל const ולא למצביע רגיל (LPCTSTR) זה const , לכן לא ניתן להמיר. אתה טענת שזה אופרטור ל char * (בתחילת הויכוח) וזה לא , יש פונקציית שרות GetBuffer ודרכה אתה משיג את זה לא דרך האופרטור. כללי: נכון std::string היא סטנדרט ולכן זו אולי הסיבה שעדיף להשתמש בה על פני CString (פורטביליות) אבל זה לא מה שהופך אותה למימוש טוב יותר. אני בטוח שאם CString היתה חלק מ std ו string היתה המצאה של מיקרוסופט והייתי בא בטענה ש ההמצאה החדשה של מיקרוסופט - string טובה יותר, היית צוחק לי בפנים ונותן לי את כל הטענות שקיבלת ממני. כל שאני אומר, זה שאם אתה מנהל פרויקט שבין כה וכה משתמש ב MFC (כך שאכלת אותה עם הפורטביליות) לפחות תשתמש במימוש טוב יותר. אולי נוותר גם על string ונשתמש רק ב char[] ואז זה גם יהיה פורטבילי לקומפיילרים של c בלבד. (תתעלם מהערה האחרונה , היא נאמרה ברוח שטות , ובאה להדגיש שהויכוח שלי הוא על מימוש טוב יותר ולא על מה נכון יותר לעשות)
 

voguemaster

New member
זה פשוט לא משנה

מה הטעם בלעבוד עם אובייקט שמסתיר את המימוש הפנימי של ה-buffer אם אתה נותן פונקצית שרות שמחזירה מצביע אל ה-buffer הזה ? זה נוגד את עיקרון ה-OO בצורה הכי ברורה שיש וזה רעיון גרוע לכל הדעות. אם אתה מאפשר גישה חופשית למחרוזת הפנימית בעצם לא הרווחת כלום מהמחלקה הזו, זה כאילו שעבדת עם buffer פשוט ויש לך אוסף של פונקציות helper לביצוע פעולות עליה. מה גם, הרעיון של שימוש ב-CMemoryException הוא דבר גרוע. בתנאי עומס עלולה להיווצר בעיית הקצאה זמנית (מכל מיני סיבות) ואז הקוד שלך עובד הרבה יותר לאט בגלל כמות ה-exceptions שנזרקות עליך. אבל חופשי, זה בקטנה
ל-string יש יותר פונקציות עזר בכל מקרה. יש לה גם עוד יתרון: היא ממומשת בתור STL container ולכן אפשר להפעיל עליה את כל (או רוב) האלגוריתמים שה-STL מספקת. הרבה יותר פונקציונליות מזו של CString, תהיה בטוח! דבר אחרון כמו שאלדד אמר: אם מחר MS יחליטו לשנות את GetBuffer, אתה אכלת אותה אם אתה מסתמך על המימוש הפנימי של CString (כלומר, שאתה מקבל LPTSTR). לא מסתמכים על מימוש פנימי של מחלקה, נקודה! בשביל מה המציאו את החוקים של השפה אם אתה לא עובד לפיהם ומפר את הרעיונות האלה ? זה אידיוטי. טוב, אני צריך לזוז
 
למעלה