هل يمكن استخدام المعالج المسبق للغة C لمعرفة ما إذا كان الملف موجودًا؟
-
02-07-2019 - |
سؤال
لدي قاعدة تعليمات برمجية كبيرة جدًا (اقرأ:الآلاف من الوحدات النمطية) التي تحتوي على تعليمات برمجية مشتركة عبر العديد من المشاريع التي تعمل جميعها على أنظمة تشغيل مختلفة مع مترجمين مختلفين لـ C++.وغني عن القول أن الحفاظ على عملية البناء يمكن أن يكون عملاً روتينيًا.
هناك العديد من الأماكن في قاعدة التعليمات البرمجية حيث يمكن تنظيف التعليمات البرمجية بشكل كبير إذا كانت هناك طريقة فقط لجعل المعالج المسبق يتجاهل بعضًا #includes
إذا لم يكن الملف موجودًا في المجلد الحالي.هل يعرف أحد طريقة لتحقيق ذلك؟
في الوقت الحاضر، نستخدم #ifdef
حول ال #include
في الملف المشترك، مع ملف ثان خاص بالمشروع يحدد ما إذا كان #include
موجود في المشروع.هذا يعمل، لكنه قبيح.غالبًا ما ينسى الأشخاص تحديث التعريفات بشكل صحيح عند إضافة ملفات أو إزالتها من المشروع.لقد فكرت في كتابة أداة إنشاء مسبق لإبقاء هذا الملف محدثًا، ولكن إذا كانت هناك طريقة مستقلة عن النظام الأساسي للقيام بذلك باستخدام المعالج المسبق، فأنا أفضل أن أفعل ذلك بهذه الطريقة بدلاً من ذلك.أيه أفكار؟
المحلول
يتم ذلك بشكل عام عن طريق استخدام برنامج نصي يحاول تشغيل المعالج المسبق في محاولة لتضمين الملف.اعتمادًا على ما إذا كان المعالج المسبق قد قام بإرجاع خطأ، يقوم البرنامج النصي بتحديث ملف .h الذي تم إنشاؤه باستخدام #define (أو #undef) المناسب.في bash، قد يبدو البرنامج النصي غامضًا كما يلي:
cat > .test.h <<'EOM'
#include <asdf.h>
EOM
if gcc -E .test.h
then
echo '#define HAVE_ASDF_H 1' >> config.h
else
echo '#ifdef HAVE_ASDF_H' >> config.h
echo '# undef HAVE_ASDF_H' >> config.h
echo '#endif' >> config.h
fi
إطار عمل شامل جدًا للعمل المحمول مع اختبارات قابلية النقل مثل هذا (بالإضافة إلى الآلاف من الاختبارات الأخرى). com.autoconf.
نصائح أخرى
تحديث بسيط
قد يدعم بعض المترجمين __has_include ( header-name )
.
تمت إضافة الامتداد إلى معيار سي++17 (P0061R1).
دعم المترجم
- رنة
- دول مجلس التعاون الخليجي من 5.X
- Visual Studio من تحديث VS2015 2 (؟)
مثال (من موقع clang):
// Note the two possible file name string formats.
#if __has_include("myinclude.h") && __has_include(<stdint.h>)
# include "myinclude.h"
#endif
مصادر
قم بإنشاء مجلد خاص للعناوين المفقودة، واجعل هذا المجلد آخر ما يتم البحث فيه
(هذا خاص بالمترجم - العنصر الأخير في متغير البيئة "INCLUDES"، شيء من هذا القبيل)
ثم إذا كان من الممكن أن يكون بعض header1.h مفقودًا، فقم بإنشاء كعب روتين في هذا المجلد
header1.h:
#define header1_is_missing
الآن يمكنك الكتابة دائمًا
#include <header1.h>
#ifdef header1_is_missing
// there is no header1.h
#endif
لا يستطيع المعالج المسبق نفسه التعرف على وجود الملفات ولكن يمكنك بالتأكيد استخدام بيئة البناء للقيام بذلك.أنا على دراية بـ make، والذي سيسمح لك بفعل شيء مثل هذا في ملف makefile الخاص بك:
ifdef $(test -f filename && echo "present")
DEFINE=-DFILENAME_PRESENT
endif
بالطبع، سيتعين عليك العثور على مثيل لذلك في بيئات بناء أخرى مثل VisualStudio، لكنني متأكد من وجودها.
يمكن أن يكون لديك خطوة ما قبل البناء التي تنشئ ملف تضمين يحتوي على قائمة #defines التي تمثل أسماء الملفات الموجودة في الدليل الحالي:
#define EXISTS_FILE1_C
#define EXISTS_FILE1_H
#define EXISTS_FILE2_C
بعد ذلك، قم بتضمين هذا الملف من داخل كود المصدر الخاص بك، وبعد ذلك يمكن لمصدرك اختبار EXISTS_*
يحدد لمعرفة ما إذا كان الملف موجودا أم لا.
بقدر ما أعرف، ليس لدى CPP توجيه بخصوص وجود الملف.
قد تتمكن من تحقيق ذلك بقليل من المساعدة من Makefile، إذا كنت تستخدم نفس الطراز عبر الأنظمة الأساسية.يمكنك اكتشاف وجود ملف في Makefile:
foo.o: foo.c
if [ -f header1.h ]; then CFLAGS+=-DHEADER1_INC
كما يذكر @Greg Hewgill، يمكنك بعد ذلك جعل #تضميناتك مشروطة:
#ifdef HEADER1_INC
#include <header1.h>
#endif
امكانية اخرى:قم بملء دليل في مكان ما بإصدارات ذات طول صفري لجميع الرؤوس التي ترغب في تضمينها اختياريًا.قم بتمرير وسيطة -I إلى هذا الدليل باسم آخر مثل هذا الخيار.
يبحث مجلس التعاون الخليجي cpp في أدلة التضمين الخاصة به بالترتيب، إذا عثر على ملف رأس في دليل سابق فسوف يستخدمه.وإلا، فسوف يجد في النهاية الملف ذي الطول الصفري، وسيكون سعيدًا.
أفترض أن تطبيقات cpp الأخرى تبحث أيضًا في أدلة التضمين الخاصة بها بالترتيب المحدد.
كان علي أن أفعل شيئًا مشابهًا لنظام التشغيل Symbian OS.هذه هي الطريقة التي فعلت ذلك:لنفترض أنك تريد التحقق من وجود الملف "file_strange.h" وتريد تضمين بعض الرؤوس أو الارتباط ببعض المكتبات اعتمادًا على وجود هذا الملف.
قم أولاً بإنشاء ملف دفعي صغير للتحقق من وجود هذا الملف.
يعد autoconf أمرًا جيدًا ولكنه يقتل العديد من المشاريع الصغيرة.
----------check.bat
@echo off
IF EXIST [\epoc32\include\domain\middleware\file_strange] GOTO NEW_API
GOTO OLD_API
GOTO :EOF
:NEW_API
echo.#define NEW_API_SUPPORTED>../inc/file_strange_supported.h
GOTO :EOF
:OLD_API
echo.#define OLD_API_SUPPORTED>../inc/file_strange_supported.h
GOTO :EOF
----------انتهى check.bat
ثم قمت بإنشاء ملف gnmake
----------checkmedialist.mk
do_nothing :
@rem do_nothing
MAKMAKE :
check.bat
BLD : do_nothing
CLEAN : do_nothing
LIB : do_nothing
CLEANLIB : do_nothing
RESOURCE : do_nothing
FREEZE : do_nothing
SAVESPACE : do_nothing
RELEASABLES : do_nothing
FINAL : do_nothing
----------انتهى check.mk
قم بتضمين ملف check.mk في ملف bld.inf الخاص بك، ويجب أن يكون قبل ملفات MMP الخاصة بك
PRJ_MMPFILES
gnumakefile checkmedialist.mk
الآن في وقت ترجمة الملف file_strange_supported.h
سيكون لها مجموعة العلم المناسبة.يمكنك استخدام هذه العلامة في ملفات CPP الخاصة بك أو حتى في ملف MMP على سبيل المثال في MMP
#include "../inc/file_strange_supported.h"
#ifdef NEW_API_SUPPORTED
LIBRARY newapi.lib
#else
LIBRARY oldapi.lib
#endif
وفي .CPP
#include "../inc/file_strange_supported.h"
#ifdef NEW_API_SUPPORTED
CStrangeApi* api = Api::NewLC();
#else
// ..
#endif
على عكس بعض الادعاءات هنا وعلى الإنترنت، فإن Visual Studio 2015 لا يدعم __has_include
الميزة - على الأقل وفقا لتجربتي.تمت التجربة مع التحديث 3
ربما نشأت الشائعات من حقيقة أن VS 2017 يُشار إليه أيضًا باسم "الإصدار 15"؛يُشار إلى VS 2015 بدلاً من ذلك باسم "الإصدار 14".يبدو أن دعم هذه الميزة قد تم تقديمه رسميًا مع "Visual Studio 2017 الإصدار 15.3".