1

I run into a dilemma when I am writing a generic toString-like template class, which gives a QString representative of common data structures that I use. I wish I can include minimal numbers of other header files in this header file (#include in .h or .c / .cpp?), but also wish to have it not rely on the order it is included (Header file order).

The basic code is like this:

// Some headers

template <typename T>
class PMsg
{
    // Public routines
    public:
        static QString fromType();
        static QString fromValue(T const &val);
}; // PMsg
// Inline definition of all methods in PMsg<T>::fromValue
// Some specialisation
// To be continued...

And here comes the problems: container classes. Should I do the following to pulling all container headers to make it work:

// To the beginning
#include <QtCore/QList>
#include <QtCore/QVector>

// After PMsg<T>
template <typename T>
class PMsg<QList<T> >
{
    // Public routines
    public:
        static QString fromType();
        static QString fromValue(QList<T> const &val);
}; // PMsg
// Some definitions

template <typename T>
class PMsg<QVector<T> >
{
    // Public routines
    public:
        static QString fromType();
        static QString fromValue(QVector<T> const &val);
}; // PMsg
// Some definitions

Or to use macros to detect what headers are included as:

#if defined(QLIST_H)
template <typename T>
class PMsg<QList<T> >
{
    // Public routines
    public:
        static QString fromType();
        static QString fromValue(QList<T> const &val);
}; // PMsg
// Some definitions
#endif

#if defined(QVECTOR_H)
template <typename T>
class PMsg<QVector<T> >
{
    // Public routines
    public:
        static QString fromType();
        static QString fromValue(QVector<T> const &val);
}; // PMsg
// Some definitions
#endif

I might eventually include more than 20 different headers (including some non-Qt classes like those in Eigen), or I might rely on something Qt can change without telling downstream developers and ask my colleagues to include Qt headers before this header for sure.

I am also not sure how much effect on compiling time both ways would cause at this time (which probably should not be considered if I haven't get to this problem).

Is there any other better way to solve this or which way should I follow?

Sorry for the bad question title and thank you for your help.

nocte107
  • 273
  • 2
  • 10
  • 1
    The library provider is responsible for include-guards in their header(s); not you. Include what you need for compilation to be successful. You cannot (and should not have to) assume the name of include-guard macros of a 3rd-party library, much less hard-code them in *your code*. And *none* of this is compile-time; its the *preprocessor* step that manages these, and if properly implemented by the included headers, will have near-zero effect on your eventual compilation time. Just include the headers as in your first sample. Let their include-guards and the preprocessor sort it out. – WhozCraig Aug 24 '14 at 13:36
  • And your second linked question is a duplicate of a question who's first answer lists the mantra usually taken to assist in sorting out incorrect dependency assumptions. [**See this answer**](http://stackoverflow.com/a/614322/1322972). – WhozCraig Aug 24 '14 at 13:40
  • 2
    You might want to consider a separate header file for each specialization as long as the primary template (unspecialized) is not defined. – Vaughn Cato Aug 24 '14 at 13:44
  • @WhozCraig, Thank you for your help. I agree with you that I should never rely on something out of my control. I read that answer before I post this question. One of the question it did not solve but you solved is whether it is suitable or not to rely on guard detection and include order, with which my program would work actually. – nocte107 Aug 24 '14 at 13:48
  • @VaughnCato, I think I need to provide more information. We have a downstream class called PLogger which use a template method to call PMsg::fromValue() and to write down messages. If I get it right, I need all specialisation included in that class, and this is the main usage of PMsg class. I doubt whether seperating specialisations would help. – nocte107 Aug 24 '14 at 13:53
  • @VaughnCato, I found my reply silly now. It is used many other places and those are the reason I am in this trouble. Sorry about that. – nocte107 Aug 24 '14 at 13:57
  • If you provide both the header containing this template and the header containing the potentially-specialized-on types *as a unit*, you might want to just use a forward-declaration. – Deduplicator Aug 24 '14 at 14:02
  • @VaughnCato, I do have all methods in my primary template (unspecialized) defined. By that I think you mean it would cause trouble that the code without including specialised header would work but does the wrong thing. Is that why you add that requirement? – nocte107 Aug 24 '14 at 14:12
  • @nocte107: Yes, exactly. Not only that, the class should not even be defined. Each specialization is an entirely separate class, so unless you actually want default behavior, you don't even need to define the primary template class at all, just declare it. – Vaughn Cato Aug 24 '14 at 14:25
  • @VaughnCato, Thank you. I just talked to other guys and they confirmed they need a default one. I think I'd include everything all together in this case. – nocte107 Aug 24 '14 at 14:43
  • Thank you, guys. The problem is solved. If you want, please write a simple answer so that I can accept it. – nocte107 Aug 24 '14 at 14:43

1 Answers1

2

Based on your description, you are providing a component with a customization point (PMsg<T>) to be used for components being added. It seems that you also want to have a certain set of already known components to take advantage of this customization. This leads to the following reasoning:

  1. To use your component it is necessary to include a specific header (you didn't name it so I'll use "pmsg.h").
  2. Newly created components can, and probably should, provide the relevant customization in their respective header, i.e., there is no need to customize these components in "pmsg.h".
  3. Existing components (e.g. QVector<T>) are not aware of this customization and to seamlessly integrate with your component their customization needs to be declared in "pmsg.h". Without declaring the customization in "pmsg.h" all users would need to remember to also include the header declaring the customization in addition to the header of the existing component. Assuming there is a default implementation for the customization, that would not just have unexpected results but can easily lead to ODR violations when different translations units are inconsistent with respect to the headers they include.
  4. For non-template components you can forward declare the component in "pmsg.h" to declare the customization and then implement it without any includes in the source file implementing the declarations from "pmsg.h". That is, for non-template components being customized there is no need to include the respective header with the declarations, a declaration suffices.
  5. In general you can't safely forward-declare a class template (primarily because default arguments can only be declared for the first declaration). In addition, you'll probably need to provide a definition for the templated customization in the header "pmsg.h". That is, you'll need to include the declarations of all template classes you want/need to customize in your "pmsg.h".

tl;dr: if you want to provide customization for existing components you'll need to declare them in your header and to do so you'll need include their respective declarations, too.

4pie0
  • 29,204
  • 9
  • 82
  • 118
Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380