0

I'm trying to force function caller from a specific class. For example this code bellow demonstrate my problem. I want to make 'use' function would be called only from class A. I'm using a global namespace all over the project.

a.h

#include "b.h"
namespace GLOBAL{

class A{
public:
    void doSomething(B);
}
}

a.cpp

#include "a.h"
using namespace GLOBAL;

void A::doSomething(B b){

    b.use();
}

b.h

namespace GLOBAL{

class B{
public:
    friend void GLOBAL::A::doSomething(B);
private:
    void use();
}

Compiler says:

‘GLOBAL::A’ has not been declared

‘void GLOBAL::B::use()’ is private

Can anyone help here ?

Thanks a lot,

Mike.

Christian Rau
  • 45,360
  • 10
  • 108
  • 185
MiP
  • 5
  • 3

3 Answers3

2

This is because in the friend delcaration you are refering to a member of a class.

For this to work the compiler must already have seen the full definition of A.

a.h

// #include "b.h"   // remove this line it is not needed.
namespace GLOBAL{

class B; // Forward declare the class here.

class A{
public:
    void doSomething(B&); // Note: This should probably be a reference.
                          // Change to doSomething(B&); 
}
}

b.h

// Add this line it is needed for the friend declaration.
// To have a member as a friend we need the definition of 'A'
#include "a.h"

namespace GLOBAL{

class B{
public:
    friend void GLOBAL::A::doSomething(B&);
private:
    void use();
}

a.cpp

#include "a.h"
// Add this line to include the B definition so you know how to call use()
#include "b.h"
using namespace GLOBAL;

void A::doSomething(B& b){ // b should be a reference otherwise you are copying it.

    b.use();
}
Martin York
  • 257,169
  • 86
  • 333
  • 562
  • And don't forget to add the `#include "b.h"` into `a.cpp`, because there it is needed (to be able to call b.use). – Jan Hudec Oct 01 '12 at 18:02
  • I can't change include because I've more functions using other types. it must be this way. – MiP Oct 01 '12 at 18:08
  • @user1697294: The requirement is that at the point of the friend declaration the compiler must have already seen a complete definition of the class A. If you can't change the include(s) then you have a problem. Otherwise it is fixed by the above. See also [Complex circular dependency](http://stackoverflow.com/a/5364069/14065) for cleaning up your dependencies. – Martin York Oct 01 '12 at 18:11
  • @PiotrNycz: Thanks for pointing that out (it was cut and paste from the original question). But makes it clearer what the problem was. Fixed with a forward declaration. – Martin York Oct 01 '12 at 18:14
  • Um, the `#include "a.h"` in `b.h` should be **outside** the `GLOBAL` namespace. – Pete Becker Oct 01 '12 at 18:35
1

The following compiles fine from a cpp file:

namespace GLOBAL
{
    class B;

    class A
    {
    public:
        void doSomething(B& b);
    };
};


namespace GLOBAL
{
    class B
    {
    public:
        friend void GLOBAL::A::doSomething(B&);
    private:
        void use()
        {
        }
    };
};

void GLOBAL::A::doSomething(B& b)
{
    b.use();
}

As best I can tell your problem arises from the fact that you include "b.h" from "a.h" that defines the B class before the A class is defined yet the B class makes a reference to the A class. So you have problems. However you can't forward declare an object of type B because you are copying via the stack. Hence why I use a reference to class B (as this doesn't require the B object to be known in advance).

Basically you have some fundamental problems with your structure that needs working out. You need to do some reading up on forward declarations and circular dependencies

Edit: Specifying purely that class A is a friend of B (rather than a specific function in A that references B) is actually a possibility as the friend definition provides a kind of forward declaration. As such the following code compiles:

namespace GLOBAL
{
    class B
    {
    public:
        friend class A;
    private:
        void use()
        {
        }
    };
};

namespace GLOBAL
{
    class A
    {
    public:
        void doSomething(B b);
    };
};

void GLOBAL::A::doSomething(B b)
{
    b.use();
}

As such in your code, originally posted, chaging the friend statement to, simply

friend class A;

should allow your code to compile.

Goz
  • 61,365
  • 24
  • 124
  • 204
  • I've requirements and must stick to that. What other ways are suggested to implement a restricted call function ? – MiP Oct 01 '12 at 18:21
  • Basically if you have to pass B without reference or not as a pointer there is nothing you can do as far as i can see. The code is very broken and can't possibly work because of the circular dependency alone. – Goz Oct 01 '12 at 18:25
  • It will if you haven't failed to forward declare and got your circular dependencies messed up. – Goz Oct 01 '12 at 21:02
  • is it possible to declare a friend class who will be able to get to private section and at base class inherit this class and manipulate the access ? – MiP Oct 01 '12 at 22:45
0

Move #include "b.h" from a.h to a.cpp after the first #include directive. The compiler doesn't need to see the friend declaration until it's compiling the function that the declaration applies to.

Pete Becker
  • 74,985
  • 8
  • 76
  • 165
  • @JanHudec - so you downvoted because I explained why the include directive was misplaced? – Pete Becker Oct 01 '12 at 18:03
  • can't do that because of the declaration: void doSomething(B); – MiP Oct 01 '12 at 18:47
  • @MiP - forward declare `B` and pass it by `const&`. – Pete Becker Oct 01 '12 at 18:50
  • I downvoted, because the answer is wrong. The compiler complains that it does not see enough and you suggest moving the directive to smaller scope (from `a.h` to `a.cpp` is smaller scope). That's not going to help. And yes, the compiler is complaining that it does not see enough -- namespaces and types need to be declared to be able to mention their members. Anywhere, always, because C++ syntax is ambiguous without knowledge what is namespace and what is type. – Jan Hudec Oct 01 '12 at 19:11
  • @JanHudec - sigh. The answer is incomplete, as illustrated by the subsequent comments. – Pete Becker Oct 01 '12 at 19:13
  • Incomplete answer is wrong. Complete it or delete it. Besides I don't see any comments on this answer that would complete it -- and comments elsewhere are irrelevant; in such case this answer is just redundant. Yes, I am aware the change is needed as part of correct solution, but no, that does not make the answer correct, because it does not include the important part. – Jan Hudec Oct 01 '12 at 19:16
  • @JanHudec - no, I'll just leave it with your downvote and you non-explanations. That, along with your constructive suggestion for another incomplete answer, says all that needs to be said. – Pete Becker Oct 01 '12 at 19:16
  • Upon compiling `b.h` (included from say, `c.cpp`, which does not need and thus does not include `a.h`; you have to get that implicit cases too) complains about that `GLOBAL::A` has not been declared on line 6. This answer says nothing about that problem. Yes, in `a.cpp`, if you include `a.h` and than `b.h`, it will compile, but it will appear again when somebody tries to use `B` and includes `b.h` only. So no, that's not a solution. – Jan Hudec Oct 02 '12 at 05:38
  • @JanHudec - it answers the question that was asked. Your insistence that you would have answered differently doesn't change that, nor does it make you an authority on how best to help beginners. – Pete Becker Oct 02 '12 at 11:48