12 Replies - 486 Views - Last Post: 13 April 2019 - 09:22 PM Rate Topic: -----

#1 CppIsMyNarnia   User is offline

  • New D.I.C Head

Reputation: 3
  • View blog
  • Posts: 17
  • Joined: 10-April 19

What is a good way to format strings?

Posted 11 April 2019 - 06:04 PM

If I have a class and I want to give it a ToString method, what is the best way to create a string based on the class' fields and stuff like that?
Is This A Good Question/Topic? 0
  • +

Replies To: What is a good way to format strings?

#2 jimblumberg   User is online

  • member icon

Reputation: 5703
  • View blog
  • Posts: 17,481
  • Joined: 25-December 09

Re: What is a good way to format strings?

Posted 11 April 2019 - 08:54 PM

Probably the "best" way would be to overload a std::to_string() function for your class.

Jim
Was This Post Helpful? 0
  • +
  • -

#3 baavgai   User is offline

  • Dreaming Coder
  • member icon


Reputation: 7419
  • View blog
  • Posts: 15,373
  • Joined: 16-October 07

Re: What is a good way to format strings?

Posted 12 April 2019 - 05:59 AM

If you know how you'd print it, e.g.
struct Foo {
    void print(std::ostream &) const;



Then a to string would be a stringstream with that print.

Indeed, you could reasonably generalize this:
struct Printable {
    virtual void print(std::ostream &) const = 0;
    std::string to_string() const;
};

std::ostream &operator<<(std::ostream &, const Printable &);

struct Foo : public Printable {
    int n;
    Foo(int v) : n(v) {}
    void print(std::ostream &out) const { out << "Foo(" << n << ")";  }
};

int main() {
    Foo x(42);
    std::cout << "cout: " << x << std::endl;
    std::string s(x.to_string());
    std::cout << "str: " << s << std::endl;
    return 0;
}



Everything you want to have a to_string need only extend Printable and implement a print.
Was This Post Helpful? 1
  • +
  • -

#4 Skydiver   User is offline

  • Code herder
  • member icon

Reputation: 6818
  • View blog
  • Posts: 23,196
  • Joined: 05-May 12

Re: What is a good way to format strings?

Posted 12 April 2019 - 02:17 PM

For some reason, I understood the question to be more along the lines of what is the correct way to format the string returned by some kind of ToString() functionality.

For example, if I had:
class Person
{
public:
    string Name;
    int    Age;

private:
    string SSN;
};



Should ToString() return one of these?
Name = "Abe", Age = 16


or
Name = "Abe", Age = 16, SSN = "12356"


or
{ Name = "Abe", Age = 16 }


or
{ Name = "Abe", Age = 16, SSN = "123456" }


or
{
    Name = "Abe",
    Age = 16,
    SSN = "123456"
}


or
{
    Name = "Abe",
    Age = 16
}



Or is the JSON format more appropriate even though we are dealing with C++ object here?

And what happens if you have sub objects?
class Name
{
    string First;
    string Last;
};

class Address
{
    string Street1;
    string Street2;
    string City;
    string State;
    string ZipCode;
};

class Person
{
    Name name;
    Address address;
};



And what happens if you have pointers? Do you follow the object graph? What if there is a cycle in the graph?
Was This Post Helpful? 1
  • +
  • -

#5 CppIsMyNarnia   User is offline

  • New D.I.C Head

Reputation: 3
  • View blog
  • Posts: 17
  • Joined: 10-April 19

Re: What is a good way to format strings?

Posted 13 April 2019 - 06:22 AM

I don't want to assume the user of the class will print the object. I will later overload the << operator to do that. It doesn't matter what is the particular formatting of the std::string I return, I just want to know if there is a way to take a bunch of things (and create a formatted string from them in C++ (like C# String.Format, or even C sprintf)

Let's assume this is my class

//Person.h
#include<string>

using std::string;

class Person
{
    private:
    const string name;
    const string lastName;
    unsigned age;

    public:
    Person(string name, string lastName, unsigned age) : name(name), lastName(lastName), age(age) {};
    /*
     * image the other methods, like getters for name and last name, etc. Doesn't matter.
     */
    string ToString() const;
};

//Person.cpp
#include "Person.h"
#include <string>
#using std::string;


//this function creates several temp strings. It doesn't look efficient at all.
//I would feel better using a low-level function like C sprintf but for strings.
string ToString() const
{
    return string("Name: ") + name + "Last Name: " + lastName + "Age: " + std::to_string(age);
}


Was This Post Helpful? 0
  • +
  • -

#6 Skydiver   User is offline

  • Code herder
  • member icon

Reputation: 6818
  • View blog
  • Posts: 23,196
  • Joined: 05-May 12

Re: What is a good way to format strings?

Posted 13 April 2019 - 06:34 AM

As noted above take advantage of the stringstream class. It should make the code a little bit more legible to read, and the overhead of the class would not be as high as creating and destroying all those temporary strings.

Alternatively, you could use += operator to keep appending to a single string.
Was This Post Helpful? 1
  • +
  • -

#7 baavgai   User is offline

  • Dreaming Coder
  • member icon


Reputation: 7419
  • View blog
  • Posts: 15,373
  • Joined: 16-October 07

Re: What is a good way to format strings?

Posted 13 April 2019 - 08:04 AM

View PostCppIsMyNarnia, on 13 April 2019 - 08:22 AM, said:

I don't want to assume the user of the class will print the object.


Hmm... you don't seem to have looked into stringstream and, as a result, seem to be missing the point.

I'll spell it out, then:
#include <iostream>
#include <string>
#include <sstream>

class Person {
public:
    Person(const std::string &name, const std::string &lastName, unsigned age);
    std::string ToString() const;
private:
    std::string name, lastName;
    unsigned age;
};

int main() {
    Person p("Joe", "Young", 89);

    std::cout << p.ToString() << std::endl;

    return 0;
}


Person::Person(const std::string &n, const std::string &l, unsigned a) : name(n), lastName(l), age(a) { }
std::string Person::ToString() const {
    //return string("Name: ") + name + "Last Name: " + lastName + "Age: " + std::to_string(age);
    std::stringstream ss;
    ss << "Name: " << name << " Last Name: " << lastName + " Age: " << age;
    return ss.str();
}



Given how stringstream works, it should be immediately obvious why implementing an ostream based output is advantageous. And, why an extendable class helps eliminate rewriting the basic boiler plate. However, if it is still unclear:
#include <iostream>
#include <string>
#include <sstream>

class Person {
public:
    Person(const std::string &name, const std::string &lastName, unsigned age);

    std::string ToString() const;
    void ToStream(std::ostream &) const;

private:
    std::string name, lastName;
    unsigned age;
};

int main() {
    Person p("Joe", "Young", 89);

    std::cout << p.ToString() << std::endl;

    return 0;
}


Person::Person(const std::string &n, const std::string &l, unsigned a) : name(n), lastName(l), age(a) { }

void Person::ToStream(std::ostream &out) const {
    out << "Name: " << name << " Last Name: " << lastName + " Age: " << age;
}

std::string Person::ToString() const {
    std::stringstream ss;
    ToStream(ss);
    return ss.str();
}




Hope this helps.

This post has been edited by baavgai: 13 April 2019 - 08:05 AM
Reason for edit:: tag fail, again

Was This Post Helpful? 2
  • +
  • -

#8 CppIsMyNarnia   User is offline

  • New D.I.C Head

Reputation: 3
  • View blog
  • Posts: 17
  • Joined: 10-April 19

Re: What is a good way to format strings?

Posted 13 April 2019 - 09:01 AM

It helped... I wanted to have the result cached since once the object is instantiated, all the calls to ToString() will return the same value... I was trying to do this by declaring the stringstream as const for some reason... of course, when I tried to use the operator << on it, Visual Studio would give me one those C++ general errors messages that help very little. I would prefer something like "invalid operand const stringstream&. non-const stringstream& required" Or something like that...
Was This Post Helpful? 0
  • +
  • -

#9 Skydiver   User is offline

  • Code herder
  • member icon

Reputation: 6818
  • View blog
  • Posts: 23,196
  • Joined: 05-May 12

Re: What is a good way to format strings?

Posted 13 April 2019 - 09:21 AM

Post your code, and post the exact errors and warnings you are getting verbatim. Don't summarize or paraphrase. There are important clues in the error messages and warnings.

Stop and think about it. Making the stringstream constant doesn't really make logical sense. Recall that you need to change the state of the class to be able to set its initial value. What you can do is store the result into a std::string, and then have your function always return a const std::string &. In your function, you check to see if you need to populate the string the first time around or not.

Personally, I wouldn't prematurely optimize unless my profiler shows that the code is a hotspot that needs optimization.
Was This Post Helpful? 1
  • +
  • -

#10 baavgai   User is offline

  • Dreaming Coder
  • member icon


Reputation: 7419
  • View blog
  • Posts: 15,373
  • Joined: 16-October 07

Re: What is a good way to format strings?

Posted 13 April 2019 - 09:57 AM

View PostCppIsMyNarnia, on 13 April 2019 - 11:01 AM, said:

It helped... I wanted to have the result cached since once the object is instantiated, all the calls to ToString() will return the same value...

While I understand the rationale, this sounds a whole lot like a "premature optimization" issue. If you were to find that such caching offered a significant performance boost, then fine. However, it's a step you'd only really worry about if you found that ToString was measurably impacting performance. And, honestly, if it was, you'd probably have written it wrong.

An object, any object, should only store values related to state. Values that can be derived from that state should not be stored, they can always be recalculated. Even if the calculation is expensive.

In OOP, your code should always assume a method call is expensive and thus keep such calls to a minimum. e.g.
// bad
cout << "Foo = " << foo.calc() << endl;
cout << "Foo squared = " << foo.calc() * foo.calc() << endl;
// good
auto x = foo.calc();
cout << "Foo = " << x << endl;
cout << "Foo squared = " << x * x << endl;


Was This Post Helpful? 1
  • +
  • -

#11 CppIsMyNarnia   User is offline

  • New D.I.C Head

Reputation: 3
  • View blog
  • Posts: 17
  • Joined: 10-April 19

Re: What is a good way to format strings?

Posted 13 April 2019 - 01:12 PM

View PostSkydiver, on 13 April 2019 - 09:21 AM, said:

Post your code, and post the exact errors and warnings you are getting verbatim. Don't summarize or paraphrase. There are important clues in the error messages and warnings.


no operator "<<" matches these operands

View PostSkydiver, on 13 April 2019 - 09:21 AM, said:

Stop and think about it. Making the stringstream constant doesn't really make logical sense. Recall that you need to change the state of the class to be able to set its initial value. What you can do is store the result into a std::string, and then have your function always return a const std::string &. In your function, you check to see if you need to populate the string the first time around or not.


I know, it is obvious that I can't make a stream const unless I don't plan on using it at all. having a const std::string is the only thing that makes sense.

View PostSkydiver, on 13 April 2019 - 09:21 AM, said:

Personally, I wouldn't prematurely optimize unless my profiler shows that the code is a hotspot that needs optimization.


At this stage, it is not premature optimization because the entire application will be text based at first, like a debug version. I could use the debug utilities of the preprocessor, but that would cause such code pollution that I think I'm better off just adding comments marking pieces of code that won't necessarily make it to the final version... I know this is a newbism but I come from a C# background, which usually has such clean code that you can read it like a book.
Was This Post Helpful? 0
  • +
  • -

#12 Skydiver   User is offline

  • Code herder
  • member icon

Reputation: 6818
  • View blog
  • Posts: 23,196
  • Joined: 05-May 12

Re: What is a good way to format strings?

Posted 13 April 2019 - 03:09 PM

Unless your profiler is showing that you are spending a lot of CPU cycles or fragmenting memory rebuilding the same strings over and over again, it will actually keep your code cleaner to just generate the string on the fly.

Consider the difference in code between these two:
baavgai's code which generates as needed:
class Person {
public:
    Person(const std::string &name, const std::string &lastName, unsigned age);

    std::string ToString() const;
    void ToStream(std::ostream &) const;

private:
    std::string name, lastName;
    unsigned age;
};

Person::Person(const std::string &n, const std::string &l, unsigned a) : name(n), lastName(l), age(a) { }

void Person::ToStream(std::ostream &out) const {
    out << "Name: " << name << " Last Name: " << lastName + " Age: " << age;
}

std::string Person::ToString() const {
    std::stringstream ss;
    ToStream(ss);
    return ss.str();
}



vs. code that tries to cache the string:
class Person {
public:
    Person(const std::string &name, const std::string &lastName, unsigned age);
    Person(const Person &person);

    const std::string & ToString() const;
    void ToStream(std::ostream &) const;

private:
    std::string name, lastName;
    unsigned age;
    std::string toStringCache;
};

Person::Person(const std::string &n, const std::string &l, unsigned a)
    : name(n)
    , lastName(l)
    , age(a)
{
}

Person::Person(const Person &p)
    : name(p.name)
    , lastName(p.lastName)
    , age(p.age)
    , toStringCache(p.toStringCache)
{
}

void Person::ToStream(std::ostream &out) const {
    out << "Name: " << name << " Last Name: " << lastName + " Age: " << age;
}

const std::string & Person::ToString() const {
    if (toStringCache.empty()) {
        std::stringstream ss;
        ToStream(ss);
        toStringCache = ss.str();
    }
    return toStringCache;
}


Was This Post Helpful? 1
  • +
  • -

#13 CppIsMyNarnia   User is offline

  • New D.I.C Head

Reputation: 3
  • View blog
  • Posts: 17
  • Joined: 10-April 19

Re: What is a good way to format strings?

Posted 13 April 2019 - 09:22 PM

I decided to take you guys' advice. I don't cache strings anymore. It is pointless to optimize what is basically a proof of concept.
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1