Search code examples
c++oopc++11friend-function

declare a function in a .h (library) file as a friend to a class


I’m writing a simple class for list of products and there I need to overload both extraction >> operator and insertion << operator to write into file and read from file

student_projectV1/list.h

#ifndef STUDENT_PROJECTV1_LIST
#define STUDENT_PROJECTV1_LIST


#include <fstream>



namespace list {

    class list {

        private:

            string name;
            int price;
            short quantity;


        public:

            ofstream ofs;
            ifstream ifs;


            // file_mutators
            void set_created_file () noexcept(false) ;
            void set_readable_file ();

            // constructors
            list() noexcept ( noexcept ( set_created_file() ) )  ;

            list ( string  , int , short  ) noexcept(false) ;
            list ( class list &) noexcept ( noexcept ( set_created_file() ) ) ;

            // initialization to cover after construction of an obj
            void initialize ( string , int , short ) noexcept(false) ;

            // mutators
            void set_name ( string ) noexcept(false);
            void set_price ( int ) noexcept(false) ;
            void set_quantity ( short ) noexcept(false) ;


            // accessors

            string get_name ( ) const  noexcept;
            int get_price () const noexcept;
            int get_quantity () const noexcept;


            // Enqueries
            bool check_created_file () noexcept;
            bool check_opened_file();

            // destructor
            ~list();



            // friend global functions
            // overloaded operators

            friend ofstream & friend_global_funcs :: operator << ( ofstream & , class list::list &) ;
            friend ifstream &  friend_global_funcs :: operator >> ( ifstream & , class list::list &) ;


    };
}


#endif

now I plan to place the definition of these two overloaded functions in another friend_global_funcs.h file inside a namespace

friend ofstream & friend_global_funcs :: operator << ( ofstream & , class list::list &) ;
friend ifstream &  friend_global_funcs :: operator >> ( ifstream & , class list::list &) ;

friend_global_funcs.h

//
// Created by solo-l-ub on 2/27/22.
//

#ifndef STUDENT_PROJECTV1_FRIEND_GLOBAL_FUNCS_H
#define STUDENT_PROJECTV1_FRIEND_GLOBAL_FUNCS_H





namespace friend_global_funcs {

    ofstream & operator<< (ofstream &ofs, class list::list &l)  {

        if (!l.check_created_file())
            throw new file_missing::file_missing(
                    "can not write info to file something wrong with acquiring file in constructor of obj \n");


        ofs << l.name() << "\t" << l.price << "\t" << l.quantity << "\n";

        return ofs;

    }


    ifstream & operator>>(ifstream &ifs, list :: list &l) {

        l.set_readable_file();

        if (!l.check_opened_file())
            throw new file_missing::file_missing(
                    "can't retrieve data cuz file is not associated with obj currently I'm in operated >> overloaded fuc \n");


        ifs >> l.name >> l.price >> l.quantity;

        return ifs;
    }

}

#endif //STUDENT_PROJECTV1_FRIEND_GLOBAL_FUNCS_H

now My scenario of the order of including files in main.cpp file I’ve included first the class list.h and then the friend_global_funcs.h file

main.cpp



#include <iostream>
using namespace std;

// using namespaces


// classes
#include "file_missing.h"
#include "empty.h"
#include "list.h"


// libraries
#include "friend_global_funcs.h"

int main() {



    //////////////////////////////////////

    return 0;
}


now when I try to compile in terminal using g++

g++ main.cpp -o out

I get the error that overloaded functions in list.h are not declared even though I’ve used the scope resolution operator to tell compiler to exactly where to look for the function friend_global_funcs :: operator <<

g++ terminal error

In file included from main.cpp:33:
list.h:64:31: error: ‘friend_global_funcs’ has not been declared
   64 |             friend ofstream & friend_global_funcs :: operator << ( ofstream & , class list::list &) ;

now I’ve uploaded my project which is a very very light project just to practice writing and reading to files from inside classes to github in case you want to take a look at it and guide me the proper sequence to use the friendship of a function which is defined in another .h file in a class

github src https://github.com/moji2013/student_projectV1.git


Solution

  • The problem is that in your current list.h, you have 2 friend declarations that uses the scope resolution operator :: to refer to operator<< and operator>> inside the namespace friend_global_funcs. And at the point of these friend declaration the compiler doesn't know about the namespace friend_global_funcs.

    So to solve this you need to tell the compiler that there is a namespace called friend_global_funcs that has the declarations for operator<< and operator>> as shown below(Working Demo):

    list.h

    #ifndef STUDENT_PROJECTV1_LIST
    #define STUDENT_PROJECTV1_LIST
    
    
    #include <fstream>
    #include <string>
    namespace list {
        class list;
    }
    namespace friend_global_funcs {
    
        std::ofstream & operator << ( std::ofstream & , const list::list &) ; //const added here and keyword class removed from second parameter
        std::ifstream & operator >> ( std::ifstream & , list::list &) ;//keyword class removed from second parameter
    }
    
    namespace list {
    
        class list {
    
            private:
    
                std::string name;
                int price;
                short quantity;
    
    
            public:
    
                std::ofstream ofs;
                std::ifstream ifs;
    
    
                // file_mutators
                void set_created_file () noexcept(false) ;
                void set_readable_file ();
    
                // constructors
                list() noexcept ( noexcept ( set_created_file() ) )  ;
    
                list ( std::string  , int , short  ) noexcept(false) ;
                list ( class list &) noexcept ( noexcept ( set_created_file() ) ) ;
    
                // initialization to cover after construction of an obj
                void initialize ( std::string , int , short ) noexcept(false) ;
    
                // mutators
                void set_name ( std::string ) noexcept(false);
                void set_price ( int ) noexcept(false) ;
                void set_quantity ( short ) noexcept(false) ;
    
    
                // accessors
    
                std::string get_name ( ) const  noexcept;
                int get_price () const noexcept;
                int get_quantity () const noexcept;
    
    
                // Enqueries
                bool check_created_file () const noexcept;//const added here
                bool check_opened_file();
    
                // destructor
                ~list();
    
    
    
                // friend global functions
                // overloaded operators
    
                friend std::ofstream & friend_global_funcs :: operator << ( std::ofstream & , const list &) ;//const added here
                friend std::ifstream &  friend_global_funcs :: operator >> ( std::ifstream & , list &) ;
    
    
        };
        
    }
    
    #endif
    

    list.cpp

    #include "list.h"
    #include"empty.h"
    #include "file_missing.h"
    namespace list{
    // constructors
    list :: list () noexcept ( noexcept ( set_created_file() ) )  {
        // give 'em a valid obj
        // ,and a most common one
        name = "not_set" , price = 1 , quantity = 1;
    
        // not sure weather to call the
        set_created_file();
        // or simply call this
    //    ofs.open("list.txt", ios::app);
    }
    
    list :: list ( std::string name , int price , short quantity ) noexcept(false) {
    
        set_name ( name );
        set_price ( price );
        set_quantity ( quantity );
    
        set_created_file();
    }
    
    list :: list ( class list &r ) noexcept ( noexcept ( set_created_file() ) )  {
    
        name = r.name;
        price = r.price;
        quantity = r.quantity;
    
        // how to copy file location then?
    
    //    ofs = r.ofs;
        set_created_file();
    }
    ////
    
    
    // initialization to cover after construction of an obj
    void list :: initialize ( std::string name , int price , short quantity ) {
        set_name ( name );
        set_price ( price );
        set_quantity ( quantity );
        set_created_file();
    }
    ////
    
    // mutators
    void list :: set_name ( std::string name ) noexcept(false) {
    
        if ( name.empty() )
            throw new empty::empty ( "name can not be left out enter something \n");
    
        (*this).name = name;
    }
    
    void list :: set_price ( int price )  noexcept(false) {
    
        if ( !price )
            throw new empty :: empty ( "price can not be zero \n" );
    
        (*this).price = price;
    }
    
    void list :: set_quantity ( short quantity ) noexcept(false) {
    
        if ( !quantity )
            throw new empty :: empty ( "quantity can not be zero \n" );
    
        (*this).quantity = quantity;
    }
    
    /////
    
    
    // file mutators
    void list :: set_created_file () noexcept(false) {
    
        if ( !ofs.is_open() )
            ofs.open("student_list_file.txt", std::ios::app);
    
        if ( !ofs.is_open() )
            throw new file_missing :: file_missing ( "file couldn't be created or opened \n" );
    
    }
    
    void list :: set_readable_file () {
    
        if ( !ifs.is_open() )
            ifs.open ( "student_list_file.txt" );
    
        if ( !ifs.is_open() )
            throw new file_missing :: file_missing ( "file couldn't be opened by set_readable_file function \n" );
    
    }
    
    
    
    ////
    
    
    // accessors
    std::string list :: get_name () const noexcept {
        return name;
    }
    int list :: get_price () const  noexcept {
        return price;
    
    }
    int list :: get_quantity () const  noexcept {
        return quantity;
    }
    
    ///
    
    // enqueries
    bool list :: check_created_file () const noexcept{
    
        return ofs.is_open();
    }
    
    
    bool list :: check_opened_file (){
    
        return ifs.is_open();
    }
    
    
    // destructive
    list :: ~list() {
        // release resources
        // close file
        // close connection
        // release heap memory
    
        ofs.close();
        ifs.close();
    
    }
    }
    

    friend_global_funcs.cpp

    
    #include "list.h"
    #include "file_missing.h"
    #include "empty.h"
    namespace friend_global_funcs {
    
        std::ofstream & operator<< (std::ofstream &ofs, const list::list &l)  { //const added here
    
            if (!l.check_created_file())
                throw new file_missing::file_missing(
                        "can not write info to file something wrong with acquiring file in constructor of obj \n");
    
    
            ofs << l.name << "\t" << l.price << "\t" << l.quantity << "\n"; //changed l.name() to l.name
    
            return ofs;
    
        }
    
    
        std::ifstream & operator>>(std::ifstream &ifs, list :: list &l) {
    
            l.set_readable_file();
    
            if (!l.check_opened_file())
                throw new file_missing::file_missing(
                        "can't retrieve data cuz file is not associated with obj currently I'm in operated >> overloaded fuc \n");
    
    
            ifs >> l.name >> l.price >> l.quantity;
    
            return ifs;
        }
    
    }
    
    

    Working Demo

    Some of the changes that i made include:

    1. Added namespace friend_global_funcs inside file list.h. This namespace contains the forward declarations for the operator<< and operator>>.
    2. Added a low-level const to the second parameter of overloaded operator<< inside files list.h and friend_global_funcs.cpp.
    3. Changed the name of the file friend_global_funcs.h to friend_global.funcs.cpp since it contains implementation.

    PS: It took me around 30 minutes to create a working example out of your given github repo. There may be other logical errors in your program that i haven't checked because the repo is quite big. I was focusing on the problem at hand(which was to overload operator<< and operator>>).