Home > Back-end >  declare a function in a .h (library) file as a friend to a class
declare a function in a .h (library) file as a friend to a class

Time:03-18

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

CodePudding user response:

list.h includes friend_global_funcs.h and friend_global_funcs.h includes list.h. This cannot work. A cannot be before B while B is also before A. The header guards prevent infinite recursion, but you end up with one header being first, and thus not having the declartions that it depends on.

A correct solution is to follow this order:

  • Declare list::list
  • Declare friend_global_funcs::operator<< and friend_global_funcs::operator>>
  • Define list::list
  • Define friend_global_funcs::operator<< and friend_global_funcs::operator>>

P.S. If you define a function in a header file, then you should make that function inline so that you don't accidentally violate one definition rule by including the non-inline definition into multiple translation units. You should also think about why you want to define the functions within a header file.

CodePudding user response:

I know that I can place the definition of friend functions in list.cppfile and save all the trouble but I want to practice and discover if its possible having declaring friendship to a function which is in another (library.h) file

CodePudding user response:

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>>).

  • Related