לדלג לתוכן

פתרון wrapper ששומר משמעות כשל

ה־wrapper הבא מצורף להצהרת Native מן ההרצאה. הוא מחזיר tuple שאינו מערבב קוד שגיאה עם attributes תקפים, ומפריד שגיאת שימוש בחוזה מקלט שהמערכת אינה מצליחה לפתוח.

static (bool Success, uint Attributes, int Error) TryGetAttributes(string path)
{
    ArgumentNullException.ThrowIfNull(path);
    if (path.Contains('\0')) throw new ArgumentException("embedded NUL", nameof(path));
    uint attributes = Native.GetFileAttributes(path);
    if (attributes != uint.MaxValue) return (true, attributes, 0);
    int error = System.Runtime.InteropServices.Marshal.GetLastPInvokeError();
    return (false, 0, error);
}

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

#include <stdint.h>
typedef struct sum_options_v1 {
    uint32_t size;
    uint32_t version;
} sum_options_v1;
int32_t sum_bytes_v1(const sum_options_v1* options,
    const uint8_t* input, uint32_t length, uint64_t* result);

החוזה יכול להתיר input שהוא NULL רק כשהאורך אפס, ולדרוש options ו־result תקפים תמיד. הספרייה אינה שומרת אף pointer לאחר החזרה, אינה משנה את הקלט, ומחזירה status של unsupported version או invalid size לפני עיבוד. האפשרות לבדוק NULL אינה מוכיחה שהמצביעים האחרים תקפים; זאת עדיין חובת הקורא בגבול C.

הטיפוס DWORD הוא uint בן 32 ביט, וה־BOOL של WinAPI הוא int בן 32 ביט או bool עם marshalling מתאים. ה־HANDLE הוא ערך בגודל מצביע שעדיף לעטוף ב־SafeHandle. הטיפוס size_t מתאים ל־nuint, ואילו uint64_t מתאים ל־ulong גם בתהליך 32 ביט. הבחנה זו מונעת החלפה שגויה בין long של C בווינדוס לבין long של C#.