O pré -processador C pode ser usado para saber se existe um arquivo?
-
02-07-2019 - |
Pergunta
Eu tenho uma base de código muito grande (leia -se: milhares de módulos) que possuem código compartilhado em vários projetos que são executados em diferentes sistemas operacionais com diferentes compiladores C ++. Escusado será dizer que manter o processo de construção pode ser uma tarefa bastante árdua.
Existem vários lugares na base de código em que limparia o código substancialmente se houvesse uma maneira de fazer com que o pré-processador ignorasse certos #includes
Se o arquivo não existir na pasta atual. Alguém sabe uma maneira de conseguir isso?
Atualmente, usamos um #ifdef
em volta do #include
no arquivo compartilhado, com um segundo arquivo específico do projeto que #decide se o #include
existe no projeto. Isso funciona, mas é feio. As pessoas geralmente esquecem de atualizar corretamente as definições quando adicionam ou removem arquivos do projeto. Eu pensei em escrever uma ferramenta pré-construção para manter esse arquivo atualizado, mas se houver uma maneira independente da plataforma de fazer isso com o pré-processador, prefiro fazer dessa maneira. Alguma ideia?
Solução
Geralmente, isso é feito usando um script que tenta executar o pré -processador na tentativa de incluir o arquivo. Dependendo se o pré -processador retornar um erro, o script atualiza um arquivo .h gerado com um #Define apropriado (ou #Undef). Em Bash, o script pode parecer vagamente assim:
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
Uma estrutura bastante completa para trabalhar portável com verificações de portabilidade como essa (assim como milhares de outras) é AutoConf.
Outras dicas
Pequena atualização
Alguns compiladores podem apoiar __has_include ( header-name )
.
A extensão foi adicionada ao C ++ 17 padrão (P0061R1).
Suporte do compilador
- Clang
- GCC de 5.x.
- Visual Studio do vs2015 Atualização 2 (?)
Exemplo (do site de Clang):
// Note the two possible file name string formats.
#if __has_include("myinclude.h") && __has_include(<stdint.h>)
# include "myinclude.h"
#endif
Fontes
Crie uma pasta especial para os cabeçalhos ausentes e faça com que essa pasta seja pesquisada por último
(que é específico do complilador - o último item em "inclui" variável de ambiente, algo assim)
Então se algum cabeçalho1.h pode estar ausente, crie nessa pasta um stub
Header1.h:
#define header1_is_missing
Agora você sempre pode escrever
#include <header1.h>
#ifdef header1_is_missing
// there is no header1.h
#endif
O próprio pré -processador não pode identificar a existência de arquivos, mas você certamente pode usar o ambiente de construção para fazê -lo. Estou familiarizado com Make, o que permitiria que você fizesse algo assim no seu Makefile:
ifdef $(test -f filename && echo "present")
DEFINE=-DFILENAME_PRESENT
endif
Obviamente, você teria que encontrar um análogo para isso em outros ambientes de construção como o VisualStudio, mas tenho certeza de que eles existem.
Você pode ter uma etapa de pré-construção que gera um arquivo de inclusão que contém uma lista de #Defines que representam os nomes dos arquivos existentes no diretório atual:
#define EXISTS_FILE1_C
#define EXISTS_FILE1_H
#define EXISTS_FILE2_C
Em seguida, inclua esse arquivo de dentro do seu código -fonte e, em seguida, sua fonte pode testar o EXISTS_*
define para ver se existe ou não um arquivo.
Até onde eu sei, o CPP não possui uma diretiva sobre a existência de um arquivo.
Você pode conseguir isso com um pouco de ajuda do Makefile, se estiver usando a mesma marca entre plataformas. Você pode detectar a presença de um arquivo no Makefile:
foo.o: foo.c
if [ -f header1.h ]; then CFLAGS+=-DHEADER1_INC
Como menciona o @Greg Hewgill, você pode fazer com que seus #includes sejam condicionais:
#ifdef HEADER1_INC
#include <header1.h>
#endif
Outra possibilidade: preencha um diretório em algum lugar com versões de comprimento zero de todos os cabeçalhos que você deseja incluir opcionalmente. Passe um argumento -i para este diretório como o último Essa opção.
O GCC CPP pesquisa seus diretórios de inclusão em ordem, se encontrar um arquivo de cabeçalho em um diretório anterior, ele o usará. Caso contrário, ele encontrará o arquivo de comprimento zero e será feliz.
Presumo que outras implementações do CPP também pesquisem seus diretórios incluídos na ordem especificada.
Eu tive que fazer algo semelhante para o sistema operacional simbiano. Foi assim que eu fiz: digamos que você queira verificar se o arquivo "file_strange.h" existe e você deseja incluir alguns cabeçalhos ou link para algumas bibliotecas, dependendo da existência desse arquivo.
Primeiro, crie um pequeno arquivo em lote para verificar a existência desse arquivo.
O Autoconf é bom, mas um pouco de matar para muitos pequenos projetos.
---------- 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 termina
Então eu criei um arquivo gnumake
---------- 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 termina
Inclua o arquivo check.mk no seu arquivo Bld.inf, ele deve estar antes dos arquivos MMP
PRJ_MMPFILES
gnumakefile checkmedialist.mk
Agora, na hora da compilação, o arquivo file_strange_supported.h
terá um conjunto de bandeira apropriado. Você pode usar este sinalizador em seus arquivos CPP ou mesmo no arquivo MMP, por exemplo, no MMP
#include "../inc/file_strange_supported.h"
#ifdef NEW_API_SUPPORTED
LIBRARY newapi.lib
#else
LIBRARY oldapi.lib
#endif
e em .cpp
#include "../inc/file_strange_supported.h"
#ifdef NEW_API_SUPPORTED
CStrangeApi* api = Api::NewLC();
#else
// ..
#endif
Ao contrário de algumas reivindicações aqui e na internet, o Visual Studio 2015 não suporta o __has_include
Recurso - pelo menos de acordo com minha experiência. Testado com a atualização 3.
Os rumores podem ter surgido do fato de que o VS 2017 também é chamado de "versão 15"; O VS 2015 é referido como "versão 14". O suporte ao recurso parece ter sido oficialmente apresentado com "Visual Studio 2017 Versão 15.3".