0

For example, if I wanted to have a default version of outputting with << and a detailed version.

myClass myObject(//Constructor parameters); cout << myObject << "\n"; cout << myObject.detailed << "\n"; 

I tried making a modifier within my class but that didn't seem to work.

class myClass { public: friend std::ostream& operator<<(std::ostream& output, const myClass& myObject); std::ostream& detailed(std::ostream& output); } 

That gave the error "Reference to non-static member function must be called".

8
  • 1
    cout << myObject.detailed << "\n"; is ::operator<<(::operator<<(cout, myObject.detailed), "\n");. It explains where you are wrong. Commented Nov 30, 2023 at 5:25
  • 2
    You perhaps want detailed to be a steam manipulator. Commented Nov 30, 2023 at 5:26
  • @273K Yes but how can the "detailed" flag be stored in the stream? Or is there a better way? Commented Nov 30, 2023 at 5:30
  • 1
    Let myClass::detailed() return std::string. Commented Nov 30, 2023 at 5:34
  • 2
    Normally the syntax would be << detailed(my_object). But since it's now widely agreed that the whole ostream infrastructure is inferior to std::format, I suggest to learn about writing custom std::formatters instead. Commented Nov 30, 2023 at 6:03

4 Answers 4

9

Here's how it is supposed to be done according to the iostreams design.

#include <iostream> class MyClass { static const int flagId; // private format flag public: int x, y; // for exposition only static std::ios_base& detailed(std::ios_base& str) { str.iword(flagId) = true; return str; } static std::ios_base& abridged(std::ios_base& str) { str.iword(flagId) = false; return str; } friend std::ostream& operator<< (std::ostream& stream, const MyClass& myClassObj) { if (stream.iword(flagId)) { stream << "X=" << myClassObj.x << " Y=" << myClassObj.y; // optionally reset the flag to false } else { stream << myClassObj.x; } return stream; } }; const int MyClass::flagId = std::ios_base::xalloc(); int main() { MyClass myObject{1, 42}; std::cout << MyClass::detailed << myObject << "\n"; std::cout << MyClass::abridged << myObject << "\n"; } 
Sign up to request clarification or add additional context in comments.

1 Comment

The key here is, of course, the use of xalloc and iword, to allow adding custom formatting flags. (Depending on what the difference is in the formatting, it might make sense to hijack one of the existing flags, but in general, I don't think that this is a good idea.) You might also consider whether you want to make any change permanent (which is what the above, and is consistent with most of the standard manipulators), or reset it to some default value each time it's used, like setw.
3

You can't pass a non-static class method to operator<<, it does not support the ability to call that method, hence the error.

For what you are attempting to do, you can change detailed to be a stream manipulator object instead of a member function, eg:

struct detailed { const myClass &cls; detailed(const myClass &cls) : cls(cls) {} friend std::ostream& operator<<(std::ostream& os, const detailed& det) { // print details about det.cls as needed... return os; } }; myClass myObject(...); cout << detailed(myObject) << "\n"; 

For example, this is how standard parametered manipulators like std::(re)setiosflags, std::set(base|fill|precision|w), std::quoted, etc are implemented.

Comments

0

I think my approach is the same as what @RemyLebeau but more generic.

#include <iostream> #include <string> //--------------------------------------------------------------------------------------------- // class templated to be able to apply // detailed mechanism to different types template<typename type_t> class detailed { public: detailed(const type_t& object) : m_object{ object } { } friend std::ostream& operator<<(std::ostream& os, const detailed<type_t>& detailed_object) { detailed_object.m_object.detailed_output(os); return os; } private: const type_t& m_object; }; //--------------------------------------------------------------------------------------------- // like your class struct test_class_t { public: friend std::ostream& operator<<(std::ostream& os, const test_class_t& object) { os << object.basic; return os; } void detailed_output(std::ostream& os) const // <- const is important { os << basic << ", " << details; } private: std::string basic{ "basic" }; std::string details{ "details" }; }; //--------------------------------------------------------------------------------------------- int main() { test_class_t object; std::cout << detailed{object} << "\n"; } 

2 Comments

I think you need to define another type template above the operator<< overload. I had a problem with overloading << in a different template class and that would fix it. Also I just tried your solution and it worked as intended (although I use detailed() instead of detailed{} ). Thank you!
You're welcome. I use {} to show it is a constructor and not a function call.
0

Edit: I just saw the answer by @n. m. could be an AI. He show how to create an I/O manipulator without an argument! I thought it could not be done. There is still a lot of good information below, however, so, read on, as you wish.

The OP is trying to code an I/O manipulator that does not take an argument, and use it to output the members of a class. The first part of this answer explains why that won't work [unless, that is, you take a different tack, and use the method of n. m.]. You have to use a manipulator with an argument in order to achieve the OP's goal. The second part of the this answer explains how to do that.

The other answers posted here do not use the forward declaration, nor the extra friend declaration, used by this answer. [Except for the answer by n. m.,] that means that the I/O manipulators they define can only access the public members of class myClass. This answer shows how to get access to the private members.

I/O Manipulators with no arguments

I/O manipulators, such as std::boolapha, are functions that take a stream reference as their only argument.

It is as though boolalpha (for output streams) had the following definition:

std::ostream& boolalpha( std::ostream& ost) { // Set the appropriate format flag, and then... return ost; } 

Note that this is not how boolapha is actually implemented. It is, however, how you can implement your own manipulator:

std::ostream& myManipulator( std::ostream& ost) { // Use `ost` in some way, either by outputting something, // or by modifying its settings. Then... return ost; } 

When you insert a manipulator that takes no arguments into a stream, you invoke operator<< with the address of the manipulator function.

 std::cout << myManipulator; // Uses the address of function `myManipulator` 

This works because there are special overloads of operator<< that are defined precisely to match the signatures of functions like myManipulatr. Those are part of the Standard Library. You do not have to write them.

Let's try it.

// main.cpp #include <iomanip> #include <iostream> std::ostream& myManipulator(std::ostream& ost) { ost << "myManipulator sets the width to 25, and the fill character to '*'.\n" << std::setfill('*') << std::setw(25); return ost; } int main() { std::cout << myManipulator << 42 << '\n'; return 0; } // end file: main.cpp 

Output:

myManipulator sets the width to 25, and the fill character to '*'. ***********************42 

No room for the this pointer

Given the mechanics described above, there is no mechanism for passing the this pointer in an I/O manipulator that does not take any arguments.

You might try coding your own operator<< overload that takes a pointer to a member function as its argument, but that won't work. A pointer-to-member can only be invoked by providing an object.

Here is what they say at isocpp.org

In C++, member functions have an implicit parameter which points to the object (the this pointer inside the member function). Normal C functions can be thought of as having a different calling convention from member functions, so the types of their pointers (pointer-to-member-function vs pointer-to-function) are different and incompatible. C++ introduces a new type of pointer, called a pointer-to-member, which can be invoked only by providing an object.

It is not possible, therefore, to create the I/O manipulator described in the OP [unless, that is, you use the very different method of n. m.].

But don't despair. You can create a manipulator that takes an argument.

An I/O manipulator with an argument

For this example, we give myClass two string variables. One should be output when detail is requested. Otherwise, the regular string should be output. Both are private.

Note the forward declaration of class detailed. That's needed for the second operator<< function, which takes a reference to a detailed object as one of its arguments.

class detailed; class myClass { std::string regular{ "Regular: no detail" }; std::string detail{ "Detailed: too much information" }; public: friend std::ostream& operator<< (std::ostream& ost, myClass const& mc) { // Call this function for "regular" output. ost << mc.regular; return ost; } // The friend declaration below is the only modification you need to // make in `myClass`. Friendship give access to the private members, // so you don't need to clutter `myClass` with debugging details used // for "detailed" output. friend std::ostream& operator<< (std::ostream& ost, detailed const& d); }; 

Next, define class detailed, which holds a reference to a myClass object. We are using const&, because we don't want the I/O manipulator to modify a myClass object. Class detailed uses the hidden friend idiom to define the operator<< function used for detailed output. That's where the clutter goes.

class detailed { // Storing a reference can lead to UB, so you take a risk if you // use this class for anything besides its intended use, i.e., // as a manipulator. myClass const& mc; public: explicit detailed(myClass const& mc) : mc{ mc } {} friend std::ostream& operator<< (std::ostream& ost, detailed const& d) { // Call this function for "detailed" output. // Put all the "clutter" here. You can access all the members // of class `myClass`, both public and private. ost << d.mc.detail << '\n'; return ost; } }; 

When you want detailed output, invoke the constructor of class detailed with a myClass object as its argument. The constructor creates a temporary, detailed object as a proxy , which is then output by operator<<.

 myClass myObject; std::cout << detailed(myObject) << '\n'; 

Here is the complete program, including a two-line test driver.

// main.cpp #include <iostream> #include <string> class detailed; class myClass { std::string regular{ "Regular: no detail" }; std::string detail{ "Detailed: too much information" }; public: friend std::ostream& operator<< (std::ostream& ost, myClass const& mc) { // Call this function for "regular" output. ost << mc.regular; return ost; } // The friend declaration below is the only modification you need to // make in `myClass`. Friendship give access to the private members, // so you don't need to clutter `myClass` with debugging details used // for "detailed" output. friend std::ostream& operator<< (std::ostream& ost, detailed const& d); }; class detailed { // Storing a reference can lead to UB, so you take a risk if you // use this class for anything besides its intended use, i.e., // as a manipulator. myClass const& mc; public: explicit detailed(myClass const& mc) : mc{ mc } {} friend std::ostream& operator<< (std::ostream& ost, detailed const& d) { // Call this function for "detailed" output. // Put all the "clutter" here. You can access all the members // of class `myClass`, both public and private. ost << d.mc.detail << '\n'; return ost; } }; int main() { myClass myObject; std::cout << myObject << '\n' << detailed(myObject) << '\n'; return 0; } // end file: main.cpp 

Output:

Regular: no detail Detailed: too much information 

Function info – A simple alternative

In my own projects, I often add member function info to a class when I need debugging information. Function info returns a std::string which contains the state of an object. This is the solution suggested by @dimich in the comments.

These days, I like to use std::format to create the information string, but in the past, I generally used a std::stringstream.

class myClass { std::string regular{ "Regular: no detail" }; std::string detail{ "Detailed: too much information" }; public: auto info(std::string_view heading) const { return std::format( "{}" "\n regular : {}" "\n detail : {}" "\n" , heading, regular, detail ); } // the rest of `myClass`... }; 

Getting debug information is trivial. Here, I am "debugging" function foo:

void foo(myClass& myObject) { std::cout << myObject.info("myObject, entering function `foo`"); // ... std::cout << myObject.info("myObject, exiting function `foo`"); } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.