简体   繁体   中英

C++ Insertion << operator overload

How can I (in an external function) differentiate between cout << thingToShow and outfile << thingToWrite with different formats? I have a single file that I am supposed to build all this implementation around without changing anything, and there are instances of

    cout << "\nx matrix is: \n";
    cout << x;
    cout << "\ny matrix is: \n";
    cout << y;

which are supposed to be formatted as

x matrix is: 
         ---              ---
        |       30  50      |
        |                   |
        |       25  40      |
         ---              ---


y matrix is: 
         ---              ---
        |       2   3       |
        |                   |
        |       1   1       |
         ---              ---

and I have instance of

w = r * w;

outfile << w;

which are supposed to be formatted simply as

-20    40
-80    60

my implementation is

ostream& operator<< (ostream &out, const Matrix& y){
    out << "\t\t" << " ---\t\t\t\t    ---\n"<< "\t\t|\t\t"<<y.tl<< "\t"<<y.tr<< "\t\t|\n\t\t|\t\t\t\t\t|\n\t\t|\t\t"<<y.bl<< "\t"<<y.br<< "\t\t|\n" << "\t\t" << " ---\t\t\t\t    ---\n\n";
    return out;
}

which works for the standard cout << thingToShow output format, but obviously messes up the outfile << thingToWrite format. is there a property of the (stream &out,) argument I can put in an if statement? or is there some other way to differentiate between cout and outfile?

You can test if your ostream is cout:

if (&out == &std::cout)

And you can take the appropriate action, depending upon the result. But if you want my unsolicited opinion, I think it would be better to let the user decide how it prints out, using some kind of formatting class:

std::cout << format(my_matrix, fancy);
outfile << format(my_matrix, not_fancy);

In this example, format would be a function which returns an object of some class which holds a Matrix object (by reference), and formatting options. This class would have its operator<< overloaded to produce the appropriate output to the stream based on the options that were given to it. Alternatively, you could make your own specialized i/o manipulators.

You need to maintain some short of state to decide whether the output is going to be in short format or the long format. The best place to maintain that data is, I think, in the class Matrix itself.

Then, provide functions that can be used to set the format. You can also provide an overloaded operator<< function so you can use them with an ostream .

Here's a working version.

#include <iostream>

struct Matrix
{
   int tl;
   int tr;
   int bl;
   int br;

   static void setLongFormat();

   static void setShortFormat();

   enum OutputFormat
   {
      LONG_FROMAT,
      SHORT_FORMAT
   };

   static OutputFormat outputFormat;
};

Matrix::OutputFormat Matrix::outputFormat = Matrix::SHORT_FORMAT;

void Matrix::setLongFormat()
{
   outputFormat = LONG_FROMAT;
}

void Matrix::setShortFormat()
{
   outputFormat = SHORT_FORMAT;
}

std::ostream& operator<< (std::ostream &out, const Matrix& y)
{
   if ( Matrix::outputFormat == Matrix::LONG_FROMAT )
   {
      out << "\t\t" << " ---\t\t\t\t    ---\n"
         << "\t\t|\t\t"<<y.tl<< "\t"<<y.tr<< "\t\t|\n"
         << "\t\t|\t\t\t\t\t|\n"
         << "\t\t|\t\t"<<y.bl<< "\t"<<y.br<< "\t\t|\n"
         << "\t\t" << " ---\t\t\t\t    ---\n\n";
   }
   else
   {
      out << y.tl<< "\t" << y.tr<< "\n"
         << y.bl<< "\t" << y.br<< "\n";
   }

   return out;
}

std::ostream& operator<<(std::ostream &out, void (*fun)())
{
   fun();
   return out;
}


int main()
{
   Matrix a{30, 50, 25, 40};
   std::cout << Matrix::setLongFormat << a << std::endl;
   std::cout << Matrix::setShortFormat << a << std::endl;
};

Output:

         ---                    ---
        |       30  50      |
        |                   |
        |       25  40      |
         ---                    ---


30  50
25  40

PS. The tabs don't look quite right here. I am sure the function can be modified to keep the tabs in their correct location.

Not quite as easy as I thought.

It can be done by overloading std::ifstream . That means you have to overload it for all types so a catch-all template overload is needed in addition to your Matrix overload.

struct Matrix
{
    int a, b;
};

// Matrix for std::cout
std::ostream& operator<<(std::ostream& os, const Matrix& t)
{
    os << "[\n\t" << t.a << "\n\t" << t.b << "\n]";
    return os;
}

// overload of general types for files
template<typename Type>
std::ofstream& operator<<(std::ofstream& ofs, const Type& t)
{
    // this cast prevents infinite recursion
    static_cast<std::ostream&>(ofs) << t;
    return ofs;
}

// Matrix overload for files
std::ofstream& operator<<(std::ofstream& ofs, const Matrix& t)
{
    // this cast prevents infinite recursion
    static_cast<std::ostream&>(ofs) << "{" << t.a << ", " << t.b << "}";
    return ofs;
}

int main()
{
    std::ofstream ofs("test.txt");

    Matrix m {3, 7};

    std::cout << 4 << m << 9 << '\n';
    ofs << 4 << m << 9 << '\n';
}

Output: std::cout

4[
    3
    7
]9

Output: ofs

4{3, 7}9

NOTE: This approach distinguishes between std::ofstream and its parent class std::ostream . This means you have to pass your std::ifstream explicitly typed to any functions if you want this behaviour to be available within the function.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM