Importancia de un archivo .inl en C ++

108

¿Cuáles son las ventajas de tener declaraciones en un archivo .inl? ¿Cuándo debería usar el mismo?

Yuzu
fuente
3
FWIW, odio los archivos .inl. ¿Por qué dividir su código más de lo necesario?
Shog9
10
@ shog9: Para separar la interfaz de la implementación. Siempre he odiado los archivos C # y Java porque es muy difícil leer la interfaz debido a todos los detalles de implementación de Messey.
Martin York
8
@Martin: desafortunadamente C ++ nos da una mala combinación de ambos mundos: la interfaz y parte de la implementación en el encabezado, el resto de la implementación en el archivo .cpp. Incluso si evita las funciones en línea (o las coloca en archivos .inl), debe saturar la interfaz con los molestos detalles de los miembros privados, a menos que pueda usar religiosamente el idioma pimpl.
Michael Burr
5
Sí, nunca he entendido el argumento de que los encabezados separan la interfaz de la implementación. Obviamente no lo hacen. Una interfaz no debe contener todos los miembros privados.
jalf
@LokiAstari: Para ser justos, Java / C # tiene muy buenas herramientas que proporcionan el esquema de la interfaz automáticamente. Se podría decir al revés: en C ++ tienes que resolver manualmente un problema, que puede ser resuelto completamente por computadoras.
bluenote10

Respuestas:

139

.inlLos archivos nunca son obligatorios y no tienen un significado especial para el compilador. Es solo una forma de estructurar su código que proporciona una pista a los humanos que podrían leerlo.

Utilizo .inlarchivos en dos casos:

  • Para obtener definiciones de funciones en línea.
  • Para obtener definiciones de plantillas de funciones.

En ambos casos, puse las declaraciones de las funciones en un fichero de cabecera, que se incluye por otros archivos, entonces #includela.inl archivo en la parte inferior del archivo de cabecera.

Me gusta porque separa la interfaz de la implementación y hace que el archivo de encabezado sea un poco más fácil de leer. Si le interesan los detalles de implementación, puede abrir el .inlarchivo y leerlo. Si no lo hace, no es necesario.

Nick Meyer
fuente
2
De hecho, se trata principalmente de separar la interfaz de la implementación.
Pavel Minaev
1
También he visto .ipp y .ixx utilizados para definiciones en línea y .tpp y .txx para la plantilla uno.
Programador
1
Por ejemplo, la biblioteca GNU Standard C ++ se utiliza .tccpara archivos de implementación de plantillas.
musiphil
1
@NickMeyer glmusa .hpp y .inl exactamente de la misma manera que mencionaste anteriormente. Es bueno saberlo, gracias por la gran respuesta :)
legends2k
Entonces, ¿es como un encabezado?
Aaron Franke
90

Nick Meyer tiene razón: al compilador no le importa la extensión del archivo que estás incluyendo, así que cosas como ".h", ".hpp", ".hxx", ".hh", ".inl", ".inc", etc. son una convención simple, para dejar claro lo que se supone que contienen los archivos.

El mejor ejemplo son los archivos de encabezado STL que no tienen extensión alguna.

Por lo general, los archivos ".inl" contienen código en línea (de ahí la extensión ".inl").

Esos archivos ".inl" son una necesidad cuando tiene un ciclo de dependencia entre el código de encabezado .

Por ejemplo:

// A.hpp
struct A
{
    void doSomethingElse()
    {
       // Etc.
    }

    void doSomething(B & b)
    {
       b.doSomethingElse() ;
    }
} ;

Y:

// B.hpp
struct B
{
    void doSomethingElse()
    {
       // Etc.
    }

    void doSomething(A & a)
    {
       a.doSomethingElse() ;
    }
} ;

No hay forma de que lo compile, incluido el uso de la declaración de avance.

Entonces, la solución es dividir la definición y la implementación en dos tipos de archivos de encabezado:

  • hpp para declaración / definición de encabezado
  • inl para la implementación del encabezado

Que se desglosa en el siguiente ejemplo:

// A.hpp

struct B ;

struct A
{
    void doSomethingElse() ;
    void doSomething(B & b) ;
} ;

Y:

// A.inl
#include <A.hpp>
#include <B.hpp>

inline void A::doSomethingElse()
{
   // Etc.
}

inline void A::doSomething(B & b)
{
   b.doSomethingElse() ;
}

Y:

// B.hpp

struct A ;

struct B
{
    void doSomethingElse() ;
    void doSomething(A & a) ;
} ;

Y:

// B.INL
#include <B.hpp>
#include <A.hpp>

inline void B::doSomethingElse()
{
   // Etc.
}

inline void B::doSomething(A & a)
{
   a.doSomethingElse() ;
}

De esta manera, puede incluir cualquier archivo ".inl" que necesite en su propia fuente, y funcionará.

Nuevamente, los nombres de los sufijos de los archivos incluidos no son realmente importantes, solo sus usos.

paercebal
fuente
5
Esto explica el beneficio real (o la necesidad) de la separación, y debería haber sido elegido como la respuesta.
musiphil
Si la función no estuviera en línea, ¿usaría un archivo .cpp estándar para la parte de implementación?
Bublafus
@Bublafus If the function were not inline, you would you standard .cpp file for the implementation part?:: Posiblemente. Las plantillas son ejemplos de código que normalmente no se pueden ocultar en archivos .CPP, por lo que en ese caso, el archivo .INL sería obligatorio.
paercebal
32

Como nadie más lo ha mencionado:

El uso de archivos .inl para almacenar sus funciones en línea puede resultar útil para acelerar las compilaciones.

Si solo incluye las declaraciones (.h) donde necesita declaraciones, y solo incluye implementaciones en línea (.inl) donde las necesita (es decir, probablemente solo en .cpp y otros archivos .inl, no .h), puede tener un efecto beneficioso en sus dependencias de encabezado.

Esto puede ser una ventaja significativa en proyectos más grandes con muchas clases interactivas.

Andy J. Buchanan
fuente
7
+1: el mundo es definitivamente un lugar diferente cuando administra millones de líneas de código y miles de archivos.
gatorfax
¿Entonces nunca debería incluir .inl en los archivos de encabezado? Siempre tuve la sensación de que .inl debería colocarse al final de los archivos de encabezado, ya que las funciones en línea requieren que la declaración y la implementación sean accesibles a la vez.
Icebone1000
1
Icebone1000 no todos los módulos que incluyen el encabezado necesariamente quieren usar las funciones en línea, por lo que no necesitan que se lean las implementaciones, no es necesario que estén presentes si no se usan.
Andy J Buchanan
1
No entiendo cómo puede ser más rápido, ya que el compilador tiene que trabajar más para incluir y combinar las unidades de traducción.
Nikos
1
@Nikos Creo que se refería a más rápido en relación con poner todas sus funciones en línea en sus archivos de encabezado.
CoffeeTableEspresso
3

En mi experiencia, los archivos .inl se utilizan para definir funciones en línea. Cuando están en un archivo .inl, el archivo se puede incluir en un encabezado para obtener funciones en línea y en un archivo .c para obtener definiciones de funciones regulares.

De esta manera, la misma fuente puede trabajar más fácilmente con compiladores que no tienen soporte de funciones en línea, así como con compiladores que sí lo tienen.

Por lo general, se usan con código C directo, no a menudo con código C ++, ya que todos los compiladores de C ++ admiten funciones en línea.

Michael Burr
fuente
No veo el sentido de hacer esto solo para obtener soporte de C. Para C, solo condicionalmente #define inline static, y definiría sus funciones en línea en el encabezado.
Pavel Minaev
Supongo que esto evita que múltiples copias de la misma función terminen en el binario. Solo digo que he visto archivos .inl usados ​​de esta manera, no que sea la única técnica (o incluso la mejor).
Michael Burr
1

Creo que es solo una convención de nomenclatura para un archivo de "encabezado" que incluye código en línea. es para que los archivos .h puedan contener definiciones y los archivos .inl contengan código en línea que es necesario para las plantillas.

No creo que haya nada más que una convención de nomenclatura para aclarar el propósito del archivo

jcoder
fuente