HansMusterWhatElse
HansMusterWhatElse

Reputation: 671

Friend functions - Declaration Order

I have two classes called Screen and Window_mgr.

Screen is allowing Window_mgr to modify its private / protected members via the friend function declaration.

As a result Window_mgr defines at the very end of the code a non-member function called Window_mgr::clear which is supposed to make use of it.

Unfortnately, I get some ridiculous errors which I can't explain myself.

What am I missing?

enter image description here

Screen.h

#pragma once

#ifndef SCREEN_H
#define SCREEN_H

#include <string>
#include <vector>

class Window_mgr {
public:
    // location ID for each screen on the window
    using ScreenIndex = std::vector<Screen>::size_type;
    // reset the Screen at the given position to all blanks
    void clear(ScreenIndex);
private:
    std::vector<Screen> screens{ Screen(24, 80, ' ') };
};

class Screen {

public:

    // Friends
    friend void Window_mgr::clear(ScreenIndex);
    //friend class Window_mgr;

    // Fields
    // typedef => creates an alias
    // typedef std::string::size_type pos;
    // alternative way to declare a type member using a type alias
    using pos = std::string::size_type;

    // Constructors
    Screen() = default; // needed because Screen has another constructor
                        // cursor initialized to 0 by its in-class initializer
    Screen(pos ht, pos wd, char c) : height(ht), width(wd), contents(ht * wd, c) {} // get the character at the cursor
    Screen &display(std::ostream &os) // function is in the class body => implicitly inline
    {
        do_display(os);
        return *this;
    }
    const Screen &display(std::ostream &os) const // function is in the class body => implicitly inline
    {
        do_display(os);
        return *this;
    }

    // Methods
    char get() const { return contents[cursor]; } // implicitly inline
    inline char get(pos ht, pos wd) const; // explicitly inline
    Screen &move(pos r, pos c); // can be made inline later
    Screen &set(char);
    Screen &set(pos, pos, char);

private:
    // Fields
    mutable size_t access_ctr;
    pos cursor = 0;
    pos height = 0, width = 0;
    std::string contents;

    // Methods
    void do_display(std::ostream &os) const { os << contents; }
};

inline Screen &Screen::set(char c)
{
    contents[cursor] = c; // set the new value at the current cursor location
    return *this; // return this object as an lvalue
}

inline Screen &Screen::set(pos r, pos col, char ch)
{
    contents[r*width + col] = ch; // set specified location to given value
    return *this; // return this object as an lvalue
}

// we can specify inline on the definition
inline Screen &Screen::move(pos r, pos c) {
    pos row = r * width; // compute the row location
    cursor = row + c; // move cursor to the column within that row
    return *this; // return this object as an lvalue
}

char Screen::get(pos r, pos c) const // declared as inline in the class
{
    pos row = r * width; // compute row location
    return contents[row + c]; // return character at the given column
}

void Window_mgr::clear(ScreenIndex i)
{
    // s is a reference to the Screen we want to clear
    Screen &s = screens[i];
    // reset the contents of that Screen to all blanks
    s.contents = string(s.height * s.width, ' ');
}

#endif

Upvotes: 3

Views: 550

Answers (2)

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726809

You cannot declare a vector of Screen objects inside your Window_mgr class, because Screen is not known to the compiler at that point in your code. If you were to declare a vector of pointers, you could fix it by forward-declaring Screen, but for a vector of actual objects full definition must be available.

You need to switch around the order of Window_mgr and Screen, and declare friendship to Window_mgr class:

class Screen {
public:
    friend class Window_mgr;
    ...
};
class Window_mgr {
public:
    // location ID for each screen on the window
    using ScreenIndex = std::vector<Screen>::size_type;
    // reset the Screen at the given position to all blanks
    void clear(ScreenIndex);
private:
    std::vector<Screen> screens{ Screen(24, 80, ' ') };
};

why does the compiler know Window_mgr but not Window_mgr::ScreenIndex

C++ has a special rule for class names used in friendship declarations:

If the name of the class that is used in the friend declaration is not yet declared, it is forward declared on the spot.

That is how the compiler "knows" of Window_mgr (i.e. it does not; it takes your word for it). There is no such rule for member functions or member types declared inside "befriended" classes. That is why the compiler does not know about Window_mgr::ScreenIndex.

Upvotes: 5

amdn
amdn

Reputation: 11582

I recommend not using "friend" for this.. just add a clear() public member function to class Screen, and call it from the window manager

// declaration
Screen &clear();

// definition
inline Screen &Screen::clear() {
    contents.resize(height * width, ' ');
    return *this; // return this object as an lvalue
}

// use
void Window_mgr::clear(ScreenIndex i)
{
    // s is a reference to the Screen we want to clear
    Screen &s = screens[i];
    // reset the contents of that Screen to all blanks
    s.clear();
}

See it live in Coliru

Upvotes: 0

Related Questions