Quais são os benefícios de um caminho relativo como “../include/header.h” para um header?

Eu revi as perguntas Como usar a diretiva include corretamente e a semântica # include do C ++ e nem aborda isso – nem os outros sugeridos pelo SO quando eu digitei o título …

Quais são, se houver, os benefícios da escrita:

#include "../include/someheader.h" #include "../otherdir/another.h" 

comparado com o uso de apenas um nome de arquivo simples:

 #include "someheader.h" #include "another.h" 

ou talvez um nome relativo sem o ‘ .. ‘:

 #include "include/someheader.h" #include "otherdir/another.h" 

Os problemas que vejo são:

  • Você não pode mover um header sem se preocupar com quais arquivos de origem o incluem.
  • Você pode acabar com caminhos muito longos para headers em dependencies e relatórios de erros. Eu tinha um hoje com ” ../dir1/include/../../include/../dir2/../include/header.h “.

O único mérito que consigo ver é que, embora não seja necessário mover os arquivos, talvez você consiga fugir sem sempre usar diretivas ‘ -I ‘ para encontrar headers, mas a perda de flexibilidade e a complexidade de compilar em sub-arquivos -sub-diretórios, etc, parece superar o benefício.

Então, estou negligenciando um benefício?


Obrigado pelas inputs. Eu acho que o consenso é que não há grandes benefícios para a notação usando “..” que eu estou negligenciando. Em termos gerais, gosto da notação “somewhere / header.h”; Eu uso em novos projetos. O que estou trabalhando é tudo menos novo.

Um dos problemas é que existem vários conjuntos de headers, geralmente com um prefixo como rspqr.h , rsabc.h , rsdef.h , rsxyz.h . Eles estão todos relacionados ao código no diretório rsmp , mas alguns dos headers estão no rsmp e outros estão no diretório central include, que não possui subdiretórios como o rsmp . (E repita para as várias outras áreas do código; existem headers em vários locais, necessários aleatoriamente por outros bits de código.) Mover coisas ao redor é um grande problema porque o código ficou tão complicado ao longo dos anos. E os makefiles não são consistentes em que as opções -I são fornecidas. Tudo sumdo, é uma triste história de negligência não tão benigna ao longo de um período de décadas. Consertar tudo sem quebrar nada será um trabalho longo e tedioso.

Eu prefiro a syntax do caminho, pois deixa bem claro qual namespace ou módulo o arquivo de header pertence.

 #include "Physics/Solver.h" 

É muito auto-descritivo, sem exigir que cada módulo prefixe seu nome nos arquivos de header.

Eu quase nunca uso a syntax “..”, em vez disso, eu tenho meu projeto inclui especificar os locais de base corretos.

O problema com #include "../include/header.h" é que ele geralmente funciona por acidente, e então uma alteração aparentemente não relacionada fará com que pare de funcionar mais tarde.

Por exemplo, considere o seguinte layout de origem:

 ./include/header.h ./lib/library.c ./lib/feature/feature.c 

E digamos que você esteja executando o compilador com um caminho de inclusão de -I. -I./lib -I. -I./lib . O que acontece?

  • ./lib/library.c pode fazer #include "../include/header.h" , o que faz sentido.
  • ./lib/feature/feature.c também pode fazer #include "../include/header.h" , mesmo que não faça sentido. Isso ocorre porque o compilador tentará a diretiva #include relação ao local do arquivo atual e, se isso falhar, tentará a diretiva #include relativa a cada input -I no caminho #include .

Além disso, se depois você remover -I./lib do caminho #include , então você quebra ./lib/feature/feature.c .

Acho que algo como o seguinte é preferível:

 ./projectname/include/header.h ./projectname/lib/library.c ./projectname/lib/feature/feature.c 

Eu não adicionaria nenhuma input de caminho de inclusão diferente de -I. e, em seguida, library.c e feature.c usariam #include "projectname/include/header.h" . Assumindo que “projectname” provavelmente seja exclusivo, isso não deve resultar em colisões de nomes ou ambigüidades na maioria das circunstâncias. Você também pode usar o recurso VPATH do caminho de inclusão e / ou make para dividir o layout físico do projeto em vários diretórios, se for absolutamente necessário (para acomodar código gerado automaticamente específico da plataforma, por exemplo; isso é algo que realmente se quebra quando você usa #include "../../somefile.h" ).

IANALL, mas eu não acho que você deveria estar colocando .. em arquivos de origem C ou C ++, porque isso não é portátil eo padrão não suporta isto. Isso é semelhante ao uso do Windows. Só faça isso se o seu compilador não puder trabalhar com qualquer outro método.

Pense na sua tree de fonts como um namespace nested e o caminho de inclusão permite que você puxe diretórios para a raiz deste namespace. A questão é, então, a de formar um namespace lógico para sua base de código, independentemente de como o código é organizado no disco.

Eu evitaria caminhos como:

  • "include/foo/bar.h" – o “include” parece ilógico e redundante
  • "../foo/bar.h" – o “..” assume uma localização relativa e é frágil
  • "bar.h" – a menos que bar.h esteja no diretório atual, isso polui o namespace global e está pedindo ambigüidades.

Pessoalmente, eu tendem a adicionar um caminho como o seguinte para os meus projetos incluem o caminho – "..;../..;../../..;../../../.." .

Isso permite que você aplique uma espécie de regra de ocultação ao #include e permita certa liberdade de movimentação de headers sem quebrar outro código. É claro que isso é à custa de introduzir um risco de binding ao arquivo de header incorreto se você não for cuidadoso, pois nomes não totalmente qualificados podem ser (ou tornar-se ao longo do tempo) ambíguos.

Eu tenho a tendência de qualificar totalmente #include s em headers públicos para que terceiros que consumam meu código não precisem adicionar o "..;../..;../../..;../../../.." para o seu projeto – é apenas uma conveniência para o meu código privado e sistema de compilation.

Porque você coloca o arquivo em relação à raiz do projeto, e quando você o verifica no controle de origem e outro desenvolvedor o verifica em um local diferente no sistema local, as coisas ainda funcionam.

Outro problema nas janelas com caminhos relativos é MAX_PATH. Isso irá desencadear problemas de compilation quando, por exemplo, compilar cruzado para o Android e seu caminho crescer com tamanho maior que 260.

Comece o caminho das suas diretivas #include "" com uma seqüência de um ou mais ” ../ ” s quando:

  • você deseja include um arquivo cuja colocação com o arquivo incluído é fixa e
  • você está construindo em um sistema POSIX ou com VC ++ e
  • você deseja evitar a ambigüidade sobre qual arquivo será incluído.

É sempre fácil fornecer um exemplo de onde sua base de código contém um erro e onde isso subsequentemente causa uma falha difícil de diagnosticar. No entanto, mesmo que seu projeto esteja livre de falhas, ele poderá ser usado abusivamente por terceiros se você confiar em caminhos absolutos para especificar arquivos localizados em relação um ao outro.

Por exemplo, considere o seguinte layout do projeto:

 ./your_lib/include/foo/header1.h ./your_lib/include/bar/header2.h ./their_lib/include/bar/header2.h 

Como deve o seu_lib / include / foo / header1.h include your_lib / include / bar / header2.h ? Vamos considerar duas opções:

  1. #include

    Assumindo que your_lib / include e their_lib / include são citados como caminhos de busca de header (por exemplo, usando as opções -I ou -isystem do GCC), a escolha de qual header2.h será escolhido é sensível à ordem em que esses dois caminhos são pesquisados .

  2. #include "../bar/header2.h"

    O primeiro local em que o compilador pesquisará é o local de your_lib / include / foo / header1.h , que é your_lib / include / foo / . Primeiro, ele tentará your_lib / include / foo /../bar / header2.h, o que reduz para your_lib / include / bar / header2.h onde ele encontrará o arquivo correto. Os caminhos de busca de header não serão usados ​​e há pouco espaço para ambigüidade.

Eu recomendaria fortemente a opção 2) neste caso por razões dadas.

Em resposta a alguns argumentos em outras respostas:

  • @ andrew-grant diz :

    … deixa bem claro qual namespace ou módulo o arquivo de header pertence.

    Talvez. Mas caminhos relativos podem ser interpretados como “neste mesmo módulo”. Eles fornecem clareza no caso de existirem vários diretórios com o mesmo nome localizados em módulos diferentes.

  • @ bk1e diz :

    … vai funcionar muitas vezes por acidente …

    Eu diria que os caminhos relativos só funcionarão por acidente em casos muito raros em que o projeto foi quebrado desde o início e poderia ser facilmente corrigido. Experimentar essa colisão de nomes sem causar um erro no compilador parece improvável. Um cenário comum é quando o arquivo de um projeto dependente inclui um de seus headers, o que inclui outro de seus headers. Compilar seu conjunto de testes deve resultar em um erro “Nenhum arquivo ou diretório” quando compilado isoladamente desse projeto dependente.

  • @singlenegationelimination diz

    … isso não é portátil e o padrão não suporta isso.

    O padrão ISO C pode não especificar todos os detalhes dos sistemas sob os quais um programa é compilado ou executado. Isso não significa que eles não são suportados, apenas que o padrão não especifica em excesso as plataformas nas quais o C será executado. (A distinção entre como "" e <> é interpretada em sistemas modernos comuns provavelmente se origina no padrão POSIX.)

  • @ daniel-paull diz

    o “..” assume uma localização relativa e é frágil

    Frágil como? Presumivelmente sensível à colocação dos dois arquivos. Assim, ” .. ” só deve (e sempre) ser usado quando o autor do arquivo incluído controla sua localização.