4

I have 3 or more struct , and I want that I have one function for print any struct

for example :

struct A 
{
  int a0;
  string a1;
  bool a2;
}

and

struct B
{
 CString b0;
 double b1;
 int b2;
 string b3
}

I want print this struct ( A and B) with same function

like this :

template<typename T>
inline void print(T)
{
  std::cout << // I don't know what is write here....
}

any body help me?

Joo
  • 142
  • 1
  • 10
  • How do you expect the `print` function to know *how* to print the contents? C++ doesn't have reflection (yet) – UnholySheep Feb 13 '18 at 13:20
  • 1
    Look around for *reflection*. Unfortunately C++ doesn't have it natively (yet), but there are library implementations such as Boost.Fusion which generate the necessary metadata with a bit of user help. – Quentin Feb 13 '18 at 13:21
  • You just want to use the same name or `print`ing, or do you want the function to divine the contents of the structure like UnholySheep asked? – StoryTeller - Unslander Monica Feb 13 '18 at 13:21
  • 1
    @Quentin can you explain about reflection? what is this? – Joo Feb 13 '18 at 13:24
  • Probably duplicate of: https://stackoverflow.com/questions/19059157/iterate-through-struct-and-class-members – dgrat Feb 13 '18 at 13:25
  • @StoryTeller no I want print – Joo Feb 13 '18 at 13:25
  • @Joo - Yes, you already said that. I asked you to clarify what does "I want print" means. Is it to have a common interface, or an actual unique entry point. – StoryTeller - Unslander Monica Feb 13 '18 at 13:26
  • @Joo a good keyword to input into your favorite search engine :) -- Reflection is the ability to (at least) list the contents of objects automatically, which is what you're looking for. But the discussion goes much further, so I encourage you to pursue your research. – Quentin Feb 13 '18 at 13:27
  • @StoryTeller it's common interface – Joo Feb 13 '18 at 13:30

2 Answers2

5

Usual practice in C++ is to define operator<<(std::ostream &, const T &) for your type:

std::ostream &operator<<(std::ostream &os, const A &value)
{
    // print here
    return os;
}

This should be done for each type you want to print and this function should be defined in the same namespace as that type.

After that, your data types can be printed to all output stream. This also allows things like boost::lexical_cast to work with your type as it prints value to std::stringstream internally.

  • 1
    @Joo Manually for each data type. Were asking for how to iterate over all fields? Because I don't see that being mentioned in the question. –  Feb 13 '18 at 13:27
  • @Joo If you actually need to _print_ it (e.g. need this for debugging or logging), then I strongly suggest you doing this manually. Custom printing can be way more useful than something automatically generated. –  Feb 13 '18 at 13:30
1

Also, another solution is to make a function called to_string for example (this is to directly convert your struct to string, but the performance is not good)

struct A
{
  int a0;
  string a1;
  bool a2;

  string to_string() const {
      return "{ " + std::to_string(a0) + ", " + a1 + ", " + (a2 ? "true" : "false") + " }";
  }
};

Then

template<typename T>
void print(const T &a) {
    cout << a.to_string() << "\n";
}

This is not good for printing, for printing, use the C++ convention of implementing operator<<

But.

This have a disadvantage, when an subclass want to change the output format. It can't.

So

class DynamicallyToStringConvertible {
public:
    virtual string to_string() const = 0;
    virtual ~DynamicallyToStringConvertible() {}
};

struct A: DynamicallyToStringConvertible
{
    ...
    virtual string to_string() const {
        return "{ " + std::to_string(a0) + ", " + a1 + ", " + (a2 ? "true" : "false") + " }";
    }
}

struct SubclassFromA: public A {
    virtual string to_string() const {
        return "Subclass: " + A::to_string();
    }
};

The function print is as is

Then

void tryPrintingAsA(const A &a) {
    print(a);
}

You will find now (after virtual functions), this will work for SubclassA. But if you tried the operator<< or the beginning of the solution (to_string without virtual), it will work for SubclassA as it was a vanilla A.

Then you would make something like that

friend ostream &operator<<(ostream &s, const DynamicallyToStringConvertible &p) {
    return s << p.to_string();
}

Inside your Base class, like this:

class DynamicallyToStringConvertible {
public:
    virtual string to_string() const = 0;
    virtual ~DynamicallyToStringConvertible() {}

    friend ostream &operator<<(ostream &s, const DynamicallyToStringConvertible &p) {
        return s << p.to_string();
    }
};

Now try the printing function for A to be:

void tryPrintingAsA(const A &a) {
    cout << a;
}
user9335240
  • 1,739
  • 1
  • 7
  • 14