2.3 - חישוב בזמן בנייה ומודולריות - פתרון
פתרון - הגבלה לפני יצירת הטבלה¶
הקוד הבא בונה טבלה בטווח שניתן לייצוג:
#include <array>
#include <cstddef>
#include <cstdint>
#include <cstdio>
template<std::size_t N>
constexpr auto powers() {
static_assert(N >= 1 && N <= 32);
std::array<std::uint32_t, N> result{};
for (std::size_t i = 0; i < N; ++i) {
result[i] = std::uint32_t{1} << i;
}
return result;
}
constexpr auto table = powers<8>();
constinit unsigned calls = 0;
int main() {
static_assert(table[0] == 1);
static_assert(table[7] == 128);
++calls;
std::printf("%u\n", static_cast<unsigned>(table[7]));
}
האינדקס האחרון עבור גודל שלושים ושתיים הוא שלושים ואחת, ולכן אינו שווה לרוחב הטיפוס. ה־static_assert פוסל גודל שלושים ושלוש לפני שמסתמכים על טבלה שנוצרה בחישוב לא חוקי.
המונה calls יכול להשתנות בזמן ריצה. האתחול שלו קבוע, אך גישה מכמה threadים עדיין דורשת סנכרון מתאים. זאת בדיוק ההפרדה בין מועד אתחול לבין מדיניות שינוי.
כתובת משותפת¶
הקובץ counter.hpp:
הקובץ first.cpp:
הקובץ second.cpp:
הקובץ main.cpp:
#include <cassert>
int* first_address();
int* second_address();
int main() {
assert(first_address() == second_address());
}
בונים יחד את שלושת קובצי cpp. כאשר המשתנה inline וההגדרות עומדות בכללי ODR, מדובר באותה ישות משותפת. כאשר משנים ל־static, כל יחידת תרגום מקבלת משתנה בעל linkage פנימי, ולכן צריך להפוך את הבדיקה ולצפות לכתובות שונות.
הניסוי אינו משתמש בשתי הגדרות גלובליות סותרות כדי לראות מה הלינקר יעשה. הוא משווה שני מודלים חוקיים שמייצגים מצב משותף שונה.
תוצרי המודול¶
שינוי גוף הפונקציה דורש יצירת object חדש, ובתצורת הדוגמה גם את ה־IFC המתאים לבנייה הזאת. הלינקוג צריך לצרוך את האובייקט החדש. מערכת בנייה עשויה לנהל תלות שדורשת גם בניית הלקוח מחדש, בהתאם לאופן שמירת הממשק והמידע המיוצא.
אין לערבב IFC ישן עם flags או מהדר שונים ולפרש שגיאת תאימות כבאג בשפת C++. תוצרי הביניים הם חלק מחוזה הכלים, ולכן manifest של build צריך לכלול גם אותם.