هل يمكن استخدام المعالج المسبق للغة C لمعرفة ما إذا كان الملف موجودًا؟

StackOverflow https://stackoverflow.com/questions/142877

  •  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".

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top