Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling a template method from another template method in C++?

Tags:

c++

templates

I am currently having an issue with templated methods. I have this public class implementing a template method:

namespace Private { class InternalClass; }

namespace Public
{
    class PublicClass
    {
    public:
        PublicClass();
        virtual ~PublicClass();

        template<class T>
        bool Add(bool primary);

    private:
        Private::InternalClass* _pInternal;
    };

    template<class T>
    bool PublicClass::Add(bool primary) { return _pInternal->Add<T>(primary); }
}

The internal class is implemented that way:

namespace Private
{
    class InternalClass
    {
    public:
        InternalClass();
        virtual ~InternalClass();

        template <class T>
        bool Add(bool primary);
    };

    template<class T>
    bool InternalClass::Add(bool primary) { return false; }
}

As this internal class header won't be available with the provided sources, I must class forward it within the PublicClass header and I add the include to PrivateClass.h inside the PublicClass.cpp file.

1) Any idea why I would be getting the following error:

error : member access into incomplete type 'Private::InternalClass' / note: forward >declaration of 'Private::InternalClass'

2) What would be the best way of hiding my PublicClass::Add() implementation?

UPDATED

Reason for error at 1) is because of this as stated by Cornstalks.

For 2), how can I hide my implementation without including PrivateClass.h within the PublicClass header file?

like image 539
thewalrusnp Avatar asked Oct 20 '22 02:10

thewalrusnp


1 Answers

You have encountered a very interesting problem - you want to implement the PImpl idiom where the privately implemented class has a template method. Well, this can be solved, meaning you CAN hide the template's implementation, but only when you know which types will be used to instantiate your Add<T> method in your program.

Say, your template will work only with types AClass and BClass. Then you can split your files as follows (comments are inlined):

File public.h:

#ifndef PUBLIC_H
#define PUBLIC_H

// Forward declaration ! It's sufficient in this case !
namespace Private { class InternalClass; }

// Declare all classes your Add<T> method should work with
struct AClass {};
struct BClass {};

namespace Public
{
    class PublicClass
    {
    public:
        PublicClass() {}
        virtual ~PublicClass() {}

        template <typename T>
        bool Add(bool primary); // DO NOT implement this method, just declare

    private:
        Private::InternalClass* _pInternal;
    };

    // "Explicit instantiation declarations", for each type the method will work with:
    extern template bool PublicClass::Add<AClass>(bool primary);

    extern template bool PublicClass::Add<BClass>(bool primary);
}

#endif

File public.cpp:

#include "public.h"

// NOTE: this is hidden in CPP file, noone will see your implementation
namespace Private
{
    class InternalClass
    {
    public:
        InternalClass() {}
        virtual ~InternalClass() {}

        template <typename T>
        bool Add(bool primary);
    };

    // Magic! Here is the actual implementation of your private method
    template <typename T>
    bool InternalClass::Add(bool primary)
    {
        return false;
    }
}

namespace Public
{
    // Original definition moved to CPP file !
    template <typename T>
    bool PublicClass::Add(bool primary)
    {
        return _pInternal->Add<T>(primary);
    }

    // And again list the allowed types, this time using "explicit instantiation definitions"
    template bool PublicClass::Add<AClass>(bool primary);

    template bool PublicClass::Add<BClass>(bool primary);
}

File main.cpp:

#include "public.h"

int main()
{
    Public::PublicClass pc;
    pc.Add<AClass>(true); // works !
    pc.Add<BClass>(false); // works !

    // pc.Add<int>(true); linker error as expected,
    // becuase there is no explicit instantiation for Add<int>

    return 0;
}
like image 103
Piotr Skotnicki Avatar answered Oct 22 '22 15:10

Piotr Skotnicki