[C++] Inline functions: in .cpp of in header?

Pagina: 1
Acties:

  • phaas
  • Registratie: Augustus 2001
  • Laatst online: 23-01-2025
Het was me eigenlijk nog nooit opgevallen dat inline member-functies alltijd in de header worden gezet en niet in implementatie file.
Weet iemand waarom dit is? En is het echt verplicht?
Ik vind het zo'n rommel in m'n headers worden... anders zou ik een aparte header voor de inline functies moeten gebruiken en deze onderaan de eigenlijke header moeten includen.

Dank!

  • MisterData
  • Registratie: September 2001
  • Laatst online: 06-05 16:16
Als je een library maakt die inline functies bevat kan de compiler ze niet inlinen als ze in de .cpp zouden staan, omdat die dan al gecompileerd is (een library gebruiker krijgt meestal alleen een setje include-files). De compiler moet de body van de functie weten om hem te kunnen inlinen :)

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 05-05 22:23
phaas schreef op maandag 20 juni 2005 @ 20:23:
Het was me eigenlijk nog nooit opgevallen dat inline member-functies alltijd in de header worden gezet en niet in implementatie file.
Weet iemand waarom dit is? En is het echt verplicht?
Het is niet verplicht, en het moet alleen als die inline functies in meerdere compilatie units worden gebruikt.

Overigens is er een 'export' keyword in de maak ( MSalters? ) die het mogelijk zou moeten maken die inlines wel weer ergens anders te definieren.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 02-05 01:32
Op 'export' kun je wachten tot je een ons weegt, zeker als je portability belangrijk vindt.

Wat je trouwens wel kunt doen om de boel een beetje overzichtelijk te houden, is in je headerfiles alleen alle declaraties te stoppen en dan twee source files maken, eentje met definities van inline functions (en dingen als templates) en eentje met alle code die van te voren gecompileerd mag worden. Het bestand met inline definities include je dan in je header-file:

C++:
1
2
3
4
5
6
7
8
9
10
// file.h
#ifndef FILE_H_INCLUDED
#define FILE_H_INCLUDED

void foo();
template<class T> T bar();
inline int bar();

#include "file.inl"
#endif //ndef FILE_H_INCLUDED
C++:
1
2
3
4
5
6
7
// file.cpp
#include "file.h"

void foo()
{
    // doe iets...
}
C++:
1
2
3
4
5
6
7
8
9
10
// file.inl

template<class T> T bar()
{
    return T(); // ofzo....
}

inline int baz() {
    return 123; // ofzo...
}


Je hebt dan nog steeds een scheiding tussen 'gewone' code en inline/template code, maar je header file is overzichtelijk omdat die alleen declaraties bevat. Iemand die je code gebruikt zal normaal gesproken ook alleen in je header kijken (en je moet dus geen extra declaraties in file.inl plaatsen, maar dat spreekt voor zich).

[ Voor 8% gewijzigd door Soultaker op 20-06-2005 21:23 ]


  • phaas
  • Registratie: Augustus 2001
  • Laatst online: 23-01-2025
@Soultaker: dat is idd wat ik bedoelde met een 'aparte header'.

Bedankt iig iedereen :)

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
farlane schreef op maandag 20 juni 2005 @ 20:54:
Het is niet verplicht, en het moet alleen als die inline functies in meerdere compilatie units worden gebruikt.

Overigens is er een 'export' keyword in de maak ( MSalters? ) die het mogelijk zou moeten maken die inlines wel weer ergens anders te definieren.
Nee, export is niet voor inline functies maar voor templates.

inline functies moeten inderdaa overal waar ze gebruikt worden gedefinieerd zijn, i.t.t. gewone functies die overal gedeclareerd moeten zijn en in 1 .cpp gedefinieerd. Dat is nou eenmaal de regel. In de praktijk maak je dus functies pas inline als de profiler laat zien welke functies daarvoor in aanmerking komen.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
Soultaker schreef op maandag 20 juni 2005 @ 21:22:
Op 'export' kun je wachten tot je een ons weegt, zeker als je portability belangrijk vindt.
Valt mee. Como draait ongeveer overal, en als ik de release notes van ICC9.0 lees hebben die nu ook export (kan goed, is ook EDG, en hun 8.1 compiler was al gebaseerd op een export-capable EDG frontend)

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • farlane
  • Registratie: Maart 2000
  • Laatst online: 05-05 22:23
MSalters schreef op dinsdag 21 juni 2005 @ 09:59:
Nee, export is niet voor inline functies maar voor templates.
O ja das waar ook.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Verwijderd

Toch vind er wel degelijk een ontwikkeling plaats in compiler land om functies cross-object file te inlinen. In dat geval moet de linker de ene definitie vinden en deze als het ware "pasten" op de plek waar inlining plaats vind. Hier zitten wel wat haken en ogen aan, maar inlines hoeven dus niet perse overal expliciet door de user gedefineerd te worden in alle situaties.

In de CUJ van pak hem beet een jaar terug stond er een interesant stuk over dit onderwerp. Ik zal eens kijken of ik het juiste nummer nog kan vinden.

  • phaas
  • Registratie: Augustus 2001
  • Laatst online: 23-01-2025
Nog even tussendoor een optimalisatie vraagje, hoef ik daar geen nieuw topic voor te openen :)
In hoeverre is het zinvol om const-reference te gebruiken (in functie's die deze waarde alleen kopieëren) voor de standaard datatypen als int en float?

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Voor built-in types kan je het beste by value passen. Hoewel een compiler het vaak wel optimaliseert als je een const reference gebruikt...maar niets is zeker. Je kan dit in het algemene geval bereiken door een type traits template class te maken, met een member typedef 'reference'. Die specialize je dan voor built-in types om by value te gaan:

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
template <typename T>
struct Type_Traits {
   typedef const T& reference;
};
template <>
struct Type_Traits<int> {
   typedef int reference;
};

template <typename T>
typename Type_Traits<T>::reference foo(typename Type_Traits<T>::reference x) {
   // ... do stuff
   return T(x);
}


Boost en andere libraries definieren dit soort traits classes al voor je, en ook nog veel beter en uitgebreider. Dit was maar een voorbeeld, waar ongetwijfeld dingen mis aan zijn in bepaalde situaties.

(Het 'waarom' is misschien belangrijker voor je... by-value wordt het object in kwestie gekopieerd, en by-reference wordt er alleen een pointer gekopieerd. Het kopieeren van een zwaar/groot object kost meestal veel tijd; het kopieeren van een pointer kan meestal in een cpu register, en kost zo goed als geen tijd. Aan de andere kant het 'dereferencen' van een pointer, dus zijn(haar?) waarde opvragen kost meer tijd dan voor een kopie. Hoewel dat ook meevalt, maar voor bv. integers wordt de totale tijd van de operatie verveelvoudigd. Bij zware objecten weegt het niet op tegen de tijd dat kopieeren zou kosten. Vandaar dat als iets in een cpu register past, je het beste by-value kan passen, en anders by-(const)reference.)

[ Voor 33% gewijzigd door Zoijar op 22-06-2005 12:15 ]


  • farlane
  • Registratie: Maart 2000
  • Laatst online: 05-05 22:23
phaas schreef op woensdag 22 juni 2005 @ 11:44:
Nog even tussendoor een optimalisatie vraagje, hoef ik daar geen nieuw topic voor te openen :)
In hoeverre is het zinvol om const-reference te gebruiken (in functie's die deze waarde alleen kopieëren) voor de standaard datatypen als int en float?
Kijk ook even naar http://www.possibility.com/Cpp/const.html
Const heeft niet alleen te maken met optimalisatie :)

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


  • phaas
  • Registratie: Augustus 2001
  • Laatst online: 23-01-2025
farlane schreef op woensdag 22 juni 2005 @ 13:18:
[...]


Kijk ook even naar http://www.possibility.com/Cpp/const.html
Const heeft niet alleen te maken met optimalisatie :)
Hehe, ik bedoelde ook pass by const-reference vs. by value, dat is wat anders natuurlijk :)

Verder is het dus redelijk safe om als vuistregel te gebruiken als <32bits -> const-reference, en alles >32bits by-value.
long long ints kunnen dus wèl const-reference terwijl ints gewoon by value gaan.

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
Nou, je bedoelt waarschijnlijk andersom, en de echte vuistregel is meer als sizeof(T)<=sizeof(void*) dan als T (by value) en anders als T const&.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

MSalters schreef op woensdag 22 juni 2005 @ 19:11:
Nou, je bedoelt waarschijnlijk andersom, en de echte vuistregel is meer als sizeof(T)<=sizeof(void*) dan als T (by value) en anders als T const&.
Dan wordt het zoiets :)
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
template <bool expr, typename T1, typename T2>
struct IfThenElse {
   typedef T1 Result;
};

template<typename T1, typename T2>
struct IfThenElse<false, T1, T2> {
   typedef T2 Result;
};

template <typename T>
struct Trait {
   typedef typename IfThenElse<sizeof(T) <= sizeof(void*), T, const T&>::Result reference;
};

void foo(typename Trait<int>::reference x, typename Trait<Employee>::reference e);

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 02-05 01:32
Het is maar een vuistregel hoor, ik zou niet zo ingewikkeld doen en het gewoon hardcoden; als je dan een keer een 64-bits ding by-reference passt op een 64-bits architectuur, jammer dan. De preciese effecten weet je toch nooit zeker van te voren en met dit soort micro-optimalisaties hou je je alleen bezig als je aan het optimaliseren bent; het is erg jammer om je normale code daar helemaal mee te vervuilen.

Voor gebruik in generieke functies is het natuurlijk wel erg nuttig, maar het gaat me dus om het gebruik in combinatie met primitives als 'int'; als iets per definitie een value type is dan is het wel een int!

[ Voor 21% gewijzigd door Soultaker op 22-06-2005 21:04 ]

Pagina: 1