Skip to main content
GameDev.net gamedev.net
🔒 Locked

Friend classes weirdness

Started by Ilici Mar 4, 2009 at 4:51 AM 14 replies 2.2k views
Original Post
Ilici
Ilici
This has been bugging me lately... Let's say I have 3 classes: EDIT: changed private to protected in base, TestFriend* with tpl* in BaseTpl, added more members, replaced A with TestFriend

template <tpl>
class BaseTpl<tpl>
{
 BaseTpl(tpl* owner) { pOwner = owner; }
protected:
  tpl* pOwner;
}

class InheritTpl : public BaseTpl<TestFriend>
{
  void doStuff()
  {
     pOwner->f();     //COMPILE ERROR - cannot access private member
     pOwner->m_i = 0; //COMPILE ERROR - cannot access private member
  }
}

class ManyStates<tpl>
{ 
   ManyStates(tpl parent) { m_states[0] = new InheritTpl(parent); }
   BaseTpl<tpl>* m_states[];
}

class TestFriend
{
 template<class> friend class BaseTpl;
 ManyStates m_StateMachine;

 TestFriend() : m_StateMachine(this) { }

protected: //tried private: too
 
  void f();
  int m_i;
}
So, is there any restriction on friend classes when using inheritance? Or is it some kind of compiler weirdness? I'm using CodeWarrior, which may increase the likelihood of the latter. [Edited by - Ilici on March 4, 2009 8:39:14 AM]
wicked357
wicked357
A private member is not accessible from a derived class of the base class. If you made it protected instead of private it should work or you can just make it public.
Morrandir
Morrandir
Quote:

pOwner->f();     // WORKS OKpOwner->m_i = 0;  //COMPILE ERROR - cannot access private member



In fact, neither should work since pOwner is declared private in the base class.
Ravyne
Ravyne
I don't think that friendship is inherited, and because you are friend-ing the base class, rather than the derived, this is an issue. You could make the derived class a friend, but you seem to want to do it the way you have for interface reasons.

One way around it is to put the functionality you need in the base, or, if this does not make sense, create "accessors" to the friend class's data in the base class.
throw table_exception("(? ???)? ? ???");
Ilici
Ilici
Quote:
Original post by wicked357
A private member is not accessible from a derived class of the base class. If you made it protected instead of private it should work or you can just make it public.


I mistyped private in Base, it is actually 'protected'. The error has something to do with the friendship of the classes.

Ravyne, yeah, that's what I thought at first, but have doubts since the 'f' method is accessible.

I think I'll have to try friend-ing derived classes in VC++ to check how it works using standard C++.
Morrandir
Morrandir
I tried your code with VS2008, and it gives me an error on both accesses.

I'm not sure I reproduced your template usage exactly right, but that shouldn't matter.
dascandy
dascandy
Quote:
Original post by Ilici
I think I'll have to try friend-ing derived classes in VC++ to check how it works using standard C++.


What's the relation between standard C++ and MSVC++ ?

Try Comeau or GCC.
Ilici
Ilici
Quote:
Original post by Morrandir
I tried your code with VS2008, and it gives me an error on both accesses.

I'm not sure I reproduced your template usage exactly right, but that shouldn't matter.


It started giving me errors for 'f' as well, I guess it was not recompiling some things or caching previous intermediary files.

Is there any way 'friend' the base class to 'friend' all its derived clases? I have lots of derived classes and I don't want to add them all to the friends list.
Morrandir
Morrandir
Quote:
Original post by Ilici
Is there any way 'friend' the base class to 'friend' all its derived clases? I have lots of derived classes and I don't want to add them all to the friends list.


I can't think of a way to do that.

Your best bet is probably to do what Ravyne has suggested, provide accessors for the friend classes members in the base class. Even better would of course be implementing a design that doesn't need the friend functionality :)
Ilici
Ilici
Quote:
Original post by Morrandir
Quote:
Original post by Ilici
Is there any way 'friend' the base class to 'friend' all its derived clases? I have lots of derived classes and I don't want to add them all to the friends list.


I can't think of a way to do that.

Your best bet is probably to do what Ravyne has suggested, provide accessors for the friend classes members in the base class. Even better would of course be implementing a design that doesn't need the friend functionality :)


It's actually the 'f' method I wanted to prevent access to, so I guess i'm left with the dreaded redesign solution [smile].
rip-off
rip-off
Can you give a high level explanation of what you are trying to achieve?

This is an example that allows access to the member while not allowing access to the function:
class TestFriend{    class Helper    {    public:        int &access() { return ptr->m_i; }    private:        template<class> friend class BaseTpl;        Helper(TestFriend &f) : ptr(&f) {}        TestFriend *ptr;    };private:    void f();    int m_i;};template <class tpl>class BaseTpl{protected:    TestFriend::Helper owner;};class A {};class InheritTpl : public BaseTpl{    void doStuff()    {        // owner.f();       // COMPILE ERROR        owner.access() = 0; // WORKS OK    }};

The inner class helper can only be created by the BaseTpl class, probably during construction:
BaseTpl::BaseTpl(TestFriend &f):    owner(f){}

It isn't pretty, but without more information it is hard to give a better solution.
Ilici
Ilici
Quote:
Original post by rip-off
Can you give a high level explanation of what you are trying to achieve?

This is an example that allows access to the member while not allowing access to the function:
*** Source Snippet Removed ***
The inner class helper can only be created by the BaseTpl class, probably during construction:
*** Source Snippet Removed ***


I have a generic State Machine, with each possbile state represented as a class (ConcreteState) which inherits the base state class (GenericState).

From the ConcreteState class I want to access private members of T.
Ilici
Ilici
Quote:
Original post by rip-off
What is T used as?


It's the object which may have multiple states. In the code i'm working on, it's the on screen pointer (cursor) that the user can move with the controller.
phresnel
phresnel
Your initial code does not make the slightest sense.

  1. You are missing certain semicola.
  2. You use "class" in template parameter lists, which is an atavism.
  3. Your first class is an explicit specialization on a type "tpl", where neither a template declaration precedes the specialization, nor does a type named "tpl" exist.
  4. In class BaseTpl you use class TestFriend, which is non-dependent and hence must be declared before that point.
  5. Your declare class InheritTpl as a derivation of BaseTpl. What is A?
  6. It does not make sense to introduce friends in that context, if you use protected. protected means: allow derived classes access. friends are needed to allow access to private members.


My first suggestion: Post code that actually compiles.

But I made the work for you to guess the right thing, does that match what you mean:

#include <iostream>template <typename T> struct get_type {        typedef T type;};class TestFriend {private:        template<typename> friend class BaseTpl;        void f() {}        int m_i;};template <typename T> class BaseTpl {private:        //friend class get_type<T>::type;        friend class InheritTpl;        T* pOwner;};class InheritTpl : public BaseTpl<TestFriend> {        void doStuff() {                std::cout << pOwner << std::endl;                //pOwner->f();                //pOwner->m_i = 0;        }};int main(){}


This should compile fine in a conforming compiler. Note how you can actually access pOwner inside InheritTpl, but not what it points to. This is because you granted InheritTpl access to private members of BaseTpl, not more.

One choice is to modify TestFriend:
class TestFriend {private:        template<typename> friend class BaseTpl;        friend class InheritTpl;        void f() {}        int m_i;};


Or we simplify the example to the following:
#include <iostream>class A {protected:        friend class B;        friend class C;        void f() {}        int m_i;};class B {protected:        //friend class C; // redundant! protected!        A* pOwner;        void doStuff() {                // everything okay                std::cout << pOwner << std::endl;                pOwner->f();                pOwner->m_i = 0;        }};class C : B {        void doStuff() {                // okay: access to member 'pOwner'                std::cout << pOwner << std::endl;                // error without "friend class C" inside A                pOwner->f();                pOwner->m_i = 0;        }};int main(){}


Maybe it is now more obvious. Inside B, you access as promised by A. Inside C, you have access to the members of B due to the "protected" derivation. But everything what pOwner points to does not belong to class B (principally because B is not an A, but B has an A), but rather to class A. So, class A must grant access to it's members to C.

But even if you had derived B from A (now assuming you remove the friend declaration in A), C could still not access the protected content through pOwner, because that content is not part of C itself, and access via protected derivation is only granted to the derivations themselves, not to their indirections.

The following would, e.g., works:
#include <iostream>class A {protected:        void f() {}        int m_i;};class B : public A {};class C : public B {        void doStuff() {                f();                m_i = 0;        }};int main(){}



edit: Maybe the formulation "a class must define who has access to it's entities" explains it all, hence "a class must define who can call it's member functions and who can read/write from/to it's member variables" [smile]

[Edited by - phresnel on March 4, 2009 7:51:28 AM]
Ilici
Ilici
Quote:
Original post by phresnel
Your initial code does not make the slightest sense.

  1. You are missing certain semicola.
  2. You use "class" in template parameter lists, which is an atavism.
  3. Your first class is an explicit specialization on a type "tpl", where neither a template declaration precedes the specialization, nor does a type named "tpl" exist.
  4. In class BaseTpl you use class TestFriend, which is non-dependent and hence must be declared before that point.
  5. Your declare class InheritTpl as a derivation of BaseTpl. What is A?
  6. It does not make sense to introduce friends in that context, if you use protected. protected means: allow derived classes access. friends are needed to allow access to private members.


My first suggestion: Post code that actually compiles.


There's a mistake in my code, BaseTpl has tpl* pOwner instead of TestFriend.

I just typed the code in instead of copying from the project since I don't know if it's ok to post company code. A is another class.

Quote:

But I made the work for you to guess the right thing, does that match what you mean:

*** Source Snippet Removed ***


Not exactly: I don't want to put in friend declarations for the derived classes like you suggest with 'friend class InheritTpl;' in BaseTpl. Moreover, it's not the value of pOwner that i want to access in InheritedTpl but one of its members.

Quote:

One choice is to modify TestFriend:
*** Source Snippet Removed ***

Or we simplify the example to the following:
*** Source Snippet Removed ***

Maybe it is now more obvious. Inside B, you access as promised by A. Inside C, you have access to the members of B due to the "protected" derivation. But everything what pOwner points to does not belong to class B (principally because B is not an A, but B has an A), but rather to class A. So, class A must grant access to it's members to C.

But even if you had derived B from A (now assuming you remove the friend declaration in A), C could still not access the protected content through pOwner, because that content is not part of C itself, and access via protected derivation is only granted to the derivations themselves, not to their indirections.

The following would, e.g., works:
*** Source Snippet Removed ***


Inheriting TestFriend from InheritTpl is not what I want.

Quote:

edit: Maybe the formulation "a class must define who has access to it's entities" explains it all, hence "a class must define who can call it's member functions and who can read/write from/to it's member variables" [smile]


In that respect, my problem boils down to: Is class friendship inherited? If C is friends with A, is C friends with B:A? Can B : A access C's private members like A ?

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.