Rubens
Rubens

Reputation: 14778

C++ standard replacement for (s)printf

I'm doing a server application in C++, and it provides an HTML page as response to HTTP requests.

The problem is that, currently, my webpage is written as a constant string in my code, and I insert other strings using << operator and std::stringstream, still during the writing of the string itself. See the example to get it clearer:

std::string first("foo");
std::string second("bar");
std::string third("foobar");

std::stringstream ss;
ss << "<html>\n"
    "<head>\n"
    "<title>Bitches Brew</title>\n"
    "</head>\n"
    "<body>\n"
    "First string: "
    << first << "\n"
    "Second string: "
    << second << "\n"
    "Third string: "
    << third << "\n"
    "</body>\n"
    "</html>";

Happens though I cannot simply stuff the contents in a file, because the data mixed with the HTML structure will change during the execution. This means I can't simply write the entire page in a file, with the string values of first, second, and third, because these values will change dynamically.

For the first request I'd send the page with first = "foo";, whereas in the second request I'd have first = "anything else".

Also, I could simply go back to sscanf/sprintf from stdio.h and insert the text I want -- I'd just have to replace the string gaps with the proper format (%s), read the HTML structure from a file, and insert whatever I wanted.

I'd like to do this in C++, without C library functions, but I couldn't figure out what to use to do this. What would be the C++ standard solution for this?

Upvotes: 2

Views: 6444

Answers (4)

vitaut
vitaut

Reputation: 55605

Standard C++ doesn't have a direct equivalent to (s)printf-like formatting other than (s)printf itself. However, there are plenty of formatting libraries that provide this functionality, like the cppformat library that includes a C++ implementation of Python's str.format and safe printf.

That said, I'd recommend using a template engine instead, see C++ HTML template framework, templatizing library, HTML generator library .

Or you can always reinvent the wheel and write your own template engine by reading a file and replacing some placeholders with arguments.

Upvotes: 4

Andy Prowl
Andy Prowl

Reputation: 126502

If you don't want to use a framework as other answers (correctly) suggest, I guess you can take inspiration from this little program:

#include <iostream>
#include <map>

using namespace std;

string instantiate_html(string const& templateHTML, map<string, string> const& replacements)
{
    string outputHTML = templateHTML;
    for (auto& entry : replacements)
    {
        string placeholder = "<$" + entry.first + "$>";
        size_t it = outputHTML.find(placeholder);
        if (it != string::npos)
        {
            outputHTML.replace(it, placeholder.size(), entry.second);
        }
    }

    return outputHTML;
}

int main()
{
    map<string, string> replacements;
    replacements["name"] = "Mark";
    replacements["surname"] = "Brown";

    // Normally you would read this string from your template file
    string templateHTML = "<html><body><$name$><$surname$></body></html>";

    string outputHTML = instantiate_html(templateHTML, replacements);

    cout << outputHTML;

    return 0;
}

Upvotes: 1

Bart van Ingen Schenau
Bart van Ingen Schenau

Reputation: 15768

You can get the desired result like this:

  1. Store your static HTML in a file, with placeholders for the dynamic text
  2. Read the HTML file into a std::string
  3. For each piece of dynamic text, locate its placeholder in the string (std::string::find) and replace the placeholder with the dynamic text (std::string::replace).
  4. Write the modified string to the final destination.

Upvotes: 2

Periodic Maintenance
Periodic Maintenance

Reputation: 1777

What about:

void RenderWebPage(std::stringstream& ss, std::string& first, std::string& second, std::string& third)
{
    ss << "<html>\n"
        "<head>\n"
        "<title>Bitches Brew</title>\n"
        "</head>\n"
        "<body>\n"
        "First string: "
        << first << "\n"
        "Second string: "
        << second << "\n"
        "Third string: "
        << third << "\n"
        "</body>\n"
        "</html>";
}

And you can call it like this:

std::stringstream ss;
std::string first("foo");
std::string second("bar");
std::string third("foobar");

RenderWebPage(ss, first, second, third);

first = "anything else";
RenderWebPage(ss, first, second, third);

second = "seconds too";
RenderWebPage(ss, first, second, third);

Upvotes: 2

Related Questions