سلسلة ++c للمحترفين الدرس 27: ملفات الترويسات (Header Files) ومعالجة التاريخ والوقت باستخدام الترويسة <chrono> في Cpp


محمد الميداوي

مثال بسيط

يحتوي المثال التالي على شيفرة يُراد تقسيمها إلى عدّة ملفّات مصدرية، سننظر الآن في كل ملف على حدة:

الملفات المصدرية

  • my_function.h

في هذا الملف، لاحظ أن الترويسة التالية تحتوي على تصريح للدالة فقط، ولا تعرِّف دوال الترويسة تطبيقات للتصريحات إلا إن وجب معالجة الشيفرة أكثر أثناء التصريف، كما هو الحال في القوالب. وعادة ما تحتوي ملفات الترويسة على واقيات معالج مسبق (Preprocessor Guards) حتى لا تضاف نفس الترويسة مرتين. ويُنفَّذ الواقي بالتحقق من كون المفتاح الرمزي (Token) الفريد للمعالج المسبق معرَّفًا أم لا، ولا تُضمَّن الترويسة إلا إن كانت غير مضمَّنة من قبل. سيتم التعرف على كل من global_value و ()my_function على أنهما نفس البُنية إن أضيفت هذه الترويسة من قبَل عدة ملفات.

// my_function.h
#ifndef MY_FUNCTION_H
#define MY_FUNCTION_H

const int global_value = 42;
int my_function();
#endif // MY_FUNCTION_H
  • my_function.cpp

في هذا الملف، لاحظ أن الملف المصدري المقابل للترويسة يدرِج الواجهة المعرَّفة في الترويسة، كي ينتبه المصرِّف إلى ما ينفذه الملف المصدري. ويتطلب الملف المصدري في هذا الحالة معرفة الثابت العام global_value المعرَّف في ملف my_function.h الذي استعرضناه قبل قليل، ولن يصرَّف هذا الملف المصدري بدون الترويسة.

// my_function.cpp
#include "my_function.h" // or #include "my_function.hpp"

int my_function() {
    return global_value; // 42;
}
  • main.cpp

تُدرج ملفّات الترويسة بعد ذلك في الملفّات المصدرية الأخرى التي ترغب في استخدام الوظائف المُعرّفة في واجهة الترويسة، دون الحاجة إلى معرفة تفاصيل تنفيذها، مما يساعد على اختزال الشيفرة. يستخدم البرنامج التالي الترويسة ‎my_function.h‎:

// main.cpp
#include <iostream>       // ترويسة مكتبة قياسية

#include "my_function.h"  // ترويسة خاصة

int main(int argc, char** argv) {
    std::cout << my_function() << std::endl;
    return 0;
}

عملية التصريف (The Compilation Process)

تكون ملفّات الترويسة غالبًا جزءًا من عملية التصريف، لذا يحدث ما يلي خلال عملية التصريف في العادةً:

على افتراض أنّ ملفّ الترويسة وملفّّ الشيفرة المصدرية موجودان في نفس المجلّد، فيمكن تصريف البرنامج عبر تنفيذ الأوامر التالية:

g++ - c my_function.cpp 
g++main.cpp my_function.o

السطر الأولُ في الشيفرة السابقة يصرِّف الملفَّ المصدري my_function.cpp إلى my_function.o، ويربط السطر الثاني ملفَّ الكائن الذي يتحوي تنفيذ ()int my_function إلى نسخة الكائن المصرَّفة من main.cpp ثم ينتج النسخة التنفيذية النهائية a.out.

بالمقابل، إذا رغبت في تصريف ‎main.cpp‎ إلى ملفّ كائنٍ أولًا، ثم ربط ملفّات التعليمات المصرّفة في النهاية، فيمكنك ذلك عبر الشيفرة التالية:

g++ -c my_function.cpp
g++ -c main.cpp

g++ main.o my_function.o

القوالب في ملفات الترويسات

تتطلّب القوالب إنشاء الشيفرة وقت التصريف: على سبيل المثال، تُحوّل دالّة مُقولَبة (templated function) إلى عدة دوالّ مختلفة بمجرد جعل الدالّة المُقَولبة معامِلًا (parameter) عبر استخدامها في الشيفرة المصدرية.

هذا يعني أنّه لا يمكن تفويض الدوالّ والتوابع وتعريفات الأصناف في القوالب إلى ملفّ مصدري آخر، ذلك أنّ أيّ شيفرة تستخدم بنية مُقولَبَة تحتاج إلى أن تَطَّلِع على تعريف تلك البنية لإنشاء الشيفرة المشتقّة. وعليه يجب أن تحتوي الشيفرة المُقولبَة على تعريفها في حال وُضِعت في الترويسات. هذا مثال على ذلك:

// templated_function.h

template < typename T >
    T* null_T_pointer() {
        T* type_point = NULL; // وما بعدها C++11 في NULL بدلا من nullptr أو 
        return type_point;
    }

معالجة التاريخ والوقت باستخدام الترويسة <chrono>

قياس الوقت باستخدام <chrono>

يمكن استخدام ‎system_clock‎ لقيَاس الوقت المنقضي منذ مرحلة معيّنة من تنفيذ البرنامج.

الإصدار = C++‎ 11

#include <iostream>
#include <chrono>
#include <thread>

int main() {
    auto start = std::chrono::system_clock::now(); { // الشيفرة المراد اختبارها
        std::this_thread::sleep_for(std::chrono::seconds(2));
    }
    auto end = std::chrono::system_clock::now();
    std::chrono::duration < double > elapsed = end - start;
    std::cout << "Elapsed time: " << elapsed.count() << "s";
}

استخدمنا في هذا المثال ‎sleep_for‎ لجعل الخيط النشط ينام (sleep) لفترة زمنية مُقاسة بالثواني std::chrono::seconds.

حساب عدد الأيام بين تاريخين

يوضّح هذا المثال كيفية حساب عدد الأيّام بين تاريخين، ويُحدَّد التاريخ بالصيغة سنة/شهر/يوم (year/month/day)، بالإضافة إلى ساعة/دقيقة/ثانية (hour/minute/second).

يحسب البرنامج التالي المستوحى من موقع cppreference عدد الأيام منذ عام 2000. سننشئ بُنية std::tm من التاريخ الخام على النحو التالي:

  • year يجب أن تكون 1900 أو أكبر.
  • month من يناير (1 - 12).
  • day اليوم من الشهر (1 - 31).
  • minutes الدقائق بعد الساعة (0 - 59).
  • seconds الثواني بعد الدقيقة (0 - 61)، و (0 - 60) منذ C++ 11.
#include <iostream>
#include <string>
#include <chrono>
#include <ctime>

std::tm CreateTmStruct(int year, int month, int day, int hour, int minutes, int seconds) {
    struct tm tm_ret = {
        0
    };

    tm_ret.tm_sec = seconds;
    tm_ret.tm_min = minutes;
    tm_ret.tm_hour = hour;
    tm_ret.tm_mday = day;
    tm_ret.tm_mon = month - 1;
    tm_ret.tm_year = year - 1900;

    return tm_ret;
}
int get_days_in_year(int year) {

    using namespace std;
    using namespace std::chrono;

    // نريد أن تكون النتيجة بالأيام
    typedef duration < int, ratio_multiply < hours::period, ratio < 24 > > ::type > days;

    // بداية الوقت
    std::tm tm_start = CreateTmStruct(year, 1, 1, 0, 0, 0);
    auto tms = system_clock::from_time_t(std::mktime( & tm_start));

    // نهاية الوقت
    std::tm tm_end = CreateTmStruct(year + 1, 1, 1, 0, 0, 0);
    auto tme = system_clock::from_time_t(std::mktime( & tm_end));

    // حساب الوقت الذي مرّ بين التاريخين
    auto diff_in_days = std::chrono::duration_cast < days > (tme - tms);

    return diff_in_days.count();
}
int main() {
    for (int year = 2000; year <= 2016; ++year)
        std::cout << "There are " << get_days_in_year(year) << " days in " << year << "\n";
}

هذا الدرس جزء من سلسلة دروس عن C++‎.

ترجمة -بتصرّف- للفصل Chapter 45: Header Files والفصل Chapter 66: Date and time using header من كتاب C++ Notes for Professionals





تفاعل الأعضاء


لا توجد أيّة تعليقات بعد



يجب أن تكون عضوًا لدينا لتتمكّن من التعليق

انشاء حساب جديد

يستغرق التسجيل بضع ثوان فقط


سجّل حسابًا جديدًا

تسجيل الدخول

تملك حسابا مسجّلا بالفعل؟


سجّل دخولك الآن