Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Any difference for implementing in the class definition for header only libraries?

I'm working on a library that is heavily using templates so I decided to make it a header-only library. Since declarations and implementations are in the same file, I can now do both at the same time. So I have a choice between these two styles:

// seperate declaration and implementation

template <typename T>
class Klass {
public:
    void do_something();
};

template <typename T>
void Klass<T>::do_something()
{
    // do something
}


// or both at the same time

template <typename T>
class Klass {
public:
    void do_something()
    {
        // do something
    }
};

I was wondering if there is a difference between these two for the compiler. If not which one would you recommend as a better practice?

like image 252
none Avatar asked Sep 17 '14 12:09

none


People also ask

What is the difference between a library and a header?

Header File is the file where all the headers name are mentioned that going to be used or consumed in the main code file. On other hand Library is the file where the implementation code of each header is written down which is mentioned in the Header file.

Do class definitions go in header file?

Class definitions can be put in header files in order to facilitate reuse in multiple files or multiple projects. Traditionally, the class definition is put in a header file of the same name as the class, and the member functions defined outside of the class are put in a . cpp file of the same name as the class.

Why do you think libraries like GLM are implemented as a header-only library?

For example, header-only libraries sometimes increase code size & compilation times. "Having a header-only library also means you don't have to worry about different platforms where the library might be used": only if you don't have to maintain the library.

Can you put definitions in header files?

Header files can include any legal C source code. They are most often used to include external variable declarations, macro definitions, type definitions, and function declarations.


1 Answers

If you merge the definition and the implementation you will have problem solving cross/circular dependencies.



For example:

1. Using your second option

home.hpp

#ifndef HOME_HPP
# define HOME_HPP

# include <string>

# include "human.hpp"

template <typename T>
class   Home
{
public:
  Home()
    :
    color_("white"),
    inhabitant_(this)
  {
  }

  void  set_color(const std::string& color)
  {
    color_ = color;
  }

private:
  std::string   color_;
  Human<T>      inhabitant_;
};

#endif

human.hpp

#ifndef HUMAN_HPP
# define HUMAN_HPP

# include <string>

// Cannot include the other header here because it already includes us
//# include "house.hpp" 

// So we do a forward declaration:
template<typename T>
class Home;

template <typename T>
class   Human
{
public:
  Human(Home<T>* house)
    :
    house_(house)
  {
  }

  void  paint(const std::string& color)
  {
   // Oops, we can't use House in our implementation
   // because it is only forward declared
   //house_->set_color(color); 
  }

private:
  Home<T>*     house_;
};

#endif

You cannot use the other object.



Whereas if you split your implementation in another file:

2. Using your first option

home.hpp

#ifndef HOME_HPP
# define HOME_HPP

# include <string>

# include "human.hpp"

template <typename T>
class   Home
{
public:
  Home();

  void  set_color(const std::string& color);

private:
  std::string   color_;
  Human<T>      inhabitant_;
};

#endif

home.ipp

#ifndef HOME_IPP
# define HOME_IPP

# include "home.hpp"

template<typename T>
Home<T>::Home()
  :
  color_("white"),
  inhabitant_(this)
{
}

template<typename T>
void  Home<T>::set_color(const std::string& color);
{
  color_ = color;
}

#endif

human.hpp

#ifndef HUMAN_HPP
# define HUMAN_HPP

# include <string>

template<typename T>
class Home;

template <typename T>
class   Human
{
public:
  Human(Home<T>* house);

  void  paint(const std::string& color);

private:
  Home<T>*     house_;
};

#endif

human.ipp

#ifndef HUMAN_IPP
# define HUMAN_IPP

# include "human.hpp"
# include "house.ipp"

template<typename T>
Human<T>::Human(Home<T>* house)
  :
  house_(house)
{
}

template<typename T>
void  Human<T>::paint(const std::string& color)
{
 house_->set_color(color); 
}

#endif

Things works more smoothly



3. Other considerations

  • One could also argue that you should put your code in multiple files for the same reason you don't code your whole project in a single file, just to split things to make them more easy to understand.
  • And as a last bonus point, for symmetry with the .hpp/.cpp files, which will silence the perfectionist sleeping deep (very deep for some) inside of each developer :)
like image 59
Drax Avatar answered Sep 26 '22 03:09

Drax