views:

272

answers:

5

I'm trying to use the pimpl idiom to hide some grungy template code, but I can't give derived classes of the body class friend access to the handle class. I get an error C2248 from MSVC 9 sp1. Here's some code to duplicate the error:

//
// interface.hpp
//
namespace internal{
    template<class T>
    class specific_body;
}

class interface
{
    struct body;
    body *pbody_;
    interface(body *pbody);

    template<class T>
    friend class internal::specific_body;

public:

    ~interface();

    interface(const interface &rhs);

    bool test() const;

    static interface create( bool value );
};

//
// interface.cpp
//
struct interface::body
{
    virtual ~body(){}

    virtual bool test() const = 0;

    virtual interface::body *clone() const = 0;
};

class true_struct {};
class false_struct {};

namespace internal {

template< class T>
class specific_body : public interface::body
{ // C2248
public:

    specific_body(){}

    virtual bool test() const;

    virtual interface::body *clone() const
    {
        return new specific_body();
    }
};

bool specific_body<true_struct>::test() const
{
    return true;
}

bool specific_body<false_struct>::test() const
{
    return false;
}

} //namespace internal

interface::interface(body *pbody) : pbody_(pbody) {}

interface::interface(const interface &rhs) : pbody_(rhs.pbody_->clone()) {}

interface::~interface() { delete pbody_; }

bool interface::test() const
{
    return pbody_->test();
}

interface interface::create(bool value )
{
    if ( value )
    {
        return interface(new internal::specific_body<true_struct>());
    }
    else
    {
        return interface(new internal::specific_body<false_struct>());
    }
}

//
// main.cpp
//
// #include "interface.hpp"
//

int _tmain(int argc, _TCHAR* argv[])
{
    interface object( interface::create(true));

    if ( object.test() )
    {
        // blah
    }
    else
    {
    }
    return 0;
}

Any help would be appreciated, I'm trying to hide interface::body and specific_body implementations from the users of interface if that's not obvious from my question.

A: 

You haven't qualified specific_body. Try

template<class T>
friend class internal::specific_body;

as your friend declaration.

Troubadour
Thanks for the suggestion, it doesn't help though
Queueless
Out of interest, does it all start working if you remove namespace `internal` i.e. just put `specific_body` in the global namespace?
Troubadour
A: 

Try using typename maybe? I think I read in Sutter that typename will work to get to class inside of an unknown scope, while class won't.

rz
Where would I use typename?
Queueless
I think you put it after or before friend
messenger
A: 

In addition to the unqualified specific_body mentioned by Troubadour, your specialization attempt of specific_body<>::test for true_struct and false_struct seems incorrect. You have to specialice the full class.

To solve the problem, I'd simply declare body in the public section. Declaring specific_body to be a friend of interface::body in addition doesn't help either.

sellibitze
I'm not sure why fulling specializing the `specific_body` class would matter in this case.I one case in my code declaring the body public helped, but I have another case where the `specfic_body` needs access to the private constructor of `interface` too; So I would like to solve the friend access issue, or restructure the code to achieve the same end where I can inherent from the body class.
Queueless
There is no way around full specialization to make it C++ standards compliant. If you don't get any errors on that part your compiler might support it as an extension, I don't know. But GCC rejects it. Just saying...
sellibitze
What exactly do you see as non-compliant here? Specializing just a member functon of a template class without specializing the entire class is perfectly legal in C++. Speaking informally, each member function of class template is by itself an independent template, which can be specialized independently. This is a rarely used feature, but it is there. 14.7.3/16 contains an example of such specialization.
AndreyT
He tries to specialize it without the template keyword
sellibitze
Oh, yeah, I missed it. I does indeed need the typical explicit specialization syntax with `template<>` bit. But still there's no need to "specialize the full class".
AndreyT
+1  A: 

You need to add template<> in the explicit instantiation of the template test method

template<> // add this line
bool specific_body<true_struct>::test() const
{
    return true;
}
fnieto
Yeah, I forgot to add those lines to the specialization's of the test method in my example, but they don't help. Thanks for the input though.
Queueless
Check if you are not missing something in your example. Fixing that line it compiles with gcc 4.4.1 and works ok.
fnieto
Well, they don't seem to make a difference with Microsoft's compiler, so it looks like a compiler conformance issue then. Thanks for for your input.
Queueless
A: 

Well, I was able to "solve" this problem by making the body a public declaration in the interface. That solves the C2248 error during the declaration of the specific_body. I also made the body a friend to the interface class and added a method to the body struct:

static interface create( body *pbody )
{
    return interface(pbody);
}

so that a specific_body can create an interface if there is a nested relationship between instances of specific_body

Queueless