2

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?

Rubens
  • 14,478
  • 11
  • 63
  • 92
  • 1
    You're overwriting the `\n` part. – chris Jan 02 '13 at 17:04
  • @chris Thanks for pointing it out, but it does not really affect the problem here. I've edited the code, anyway. – Rubens Jan 02 '13 at 17:06
  • @Rubens I'm not sure I understood the sentence "Happens though I cannot simply stuff the contents in a file, because the data mixed with the HTML structure will change during the execution." - could you please clarify? What are you trying to achieve? – Andy Prowl Jan 02 '13 at 17:08
  • @AndyProwl Does the edit clarify that section? – Rubens Jan 02 '13 at 17:11
  • @Rubens I think I start to understand, but I'm wondering why can't you just write to the file after the strings have taken their final values. Do you want to do something like write to a file and update the file automatically every time those strings change their value? If so, why would you want to do that? (I'm just curious, not questioning that you shouldn't want to do that) – Andy Prowl Jan 02 '13 at 17:14
  • did you have already tried to serialize ? otherwise you store the fixed part in an SQL database and read them and put everything togheter at runtime – user1797612 Jan 02 '13 at 17:15
  • I don't get what's your actual problem. Why do you need to store the HTML page in a file anyway? A typical server application would just send the content through the network. – user1610015 Jan 02 '13 at 17:15
  • @AndyProwl That's exactly what I want. And I'm doing it due to what I said in the very beginning: this is a simple server, that responds to some http requests sending a dynamically written webpage. – Rubens Jan 02 '13 at 17:16
  • @Rubens If your purpose is to write a simple HTTP server then I think you do not need to store anything into a file, as a user pointed out in a previous comment, just send the content of the stream over the net – Andy Prowl Jan 02 '13 at 17:18
  • @AndyProwl This is what I'm doing, currently; I think my page is way too simple to use anything special to write it, but I wouldn't like to write the page structure itself by the way I'm doing it now. I'd like to have the very simple page structure in a file, and fill the *string gaps* with the content I want. – Rubens Jan 02 '13 at 17:21
  • 1
    @Rubens OK, then you do not actually want to *write* a file, but rather *read* a template structure from a file, fill it in programmatically, and then send it as a response. Is that correct? – Andy Prowl Jan 02 '13 at 17:22
  • @AndyProwl Yes, that's exactly what I want, as well as pointed by "Bart van Ingen Schenau" too. – Rubens Jan 02 '13 at 17:25
  • I'll stick to `(s)printf`; thank you all for the patience and attention! – Rubens Jan 02 '13 at 17:35
  • @Rubens I think you should not stick to `printf`. I just posted an answer with sample program that should do what you want. – Andy Prowl Jan 02 '13 at 17:40

4 Answers4

4

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.

Community
  • 1
  • 1
vitaut
  • 49,672
  • 25
  • 199
  • 336
  • 1
    http://en.cppreference.com/w/cpp/io/c/fprintf - Technically, standard C++ does include things like `sprintf`. Still, using a template framework is a good suggestion. – Nate Kohl Jan 02 '13 at 17:26
  • @NateKohl But is it in `stdio.h` or in `iostream`? – Rubens Jan 02 '13 at 17:27
  • It's in (also ), not . But it's still part of C++. – Nate Kohl Jan 02 '13 at 17:28
  • @NateKohl: You are right, I corrected the answer. What I really meant is that (s)printf is a C heritage, not a purely C++ thing and it has some limitations. – vitaut Jan 02 '13 at 17:29
2

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);
Periodic Maintenance
  • 1,698
  • 4
  • 20
  • 32
  • I think the OP wants to read an HTML template from a file and then substitute the arguments. – vitaut Jan 02 '13 at 17:21
  • This is what I'm doing, currently; I'd like to get rid of this *html coding* in C++, pretty much using *place holders*, but I wanted to have this facility from some standard C++ method -- like C's standard `(s)printf`. – Rubens Jan 02 '13 at 17:33
2

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.
Bart van Ingen Schenau
  • 15,488
  • 4
  • 32
  • 41
  • This is exactly what I've been planning to do; I simply would like to know if I could have the simplicity of `%s` replacement with `(s)printf`, but, as @vitaut said, there seems to be no *direct equivalent* for it. – Rubens Jan 02 '13 at 17:23
  • @Rubens: You can still use printf if you want. – vitaut Jan 02 '13 at 17:26
  • @vitaut Yes, I know, as well as I could use `printf` for some of `std::cout`'s job; I was just wondering if there was anything more C++ *standardized* to accomplish this. Seems I'll have to stick to `sprintf`, anyway \= – Rubens Jan 02 '13 at 17:29
  • @Rubens: You could look into Boost.Format for a C++ equivalent of `printf`'s behaviour. Be aware that using format strings from external sources can be a security risk. – Bart van Ingen Schenau Jan 02 '13 at 17:30
  • @BartvanIngenSchenau: Boost Format is extremely slow, I'd recommend https://github.com/c42f/tinyformat instead. It also provides printf's functionality. – vitaut Jan 02 '13 at 17:34
1

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;
}
Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
  • Andy, I'm really thankful for your promptness, but this is an implementation of "Bart van Ingen Schenau" suggested; my will was really to know if there was some precious function from C++ hidden maybe in `iostream` that would do quite the same of `(s)printf`. +1 for your patience and promptness. Sorry if I haven't been clear enough by the beginning. – Rubens Jan 02 '13 at 17:46
  • no problem, we're all here to learn, and sometimes making the right question is harder than giving the right answer. cheers – Andy Prowl Jan 02 '13 at 17:49
  • 1
    BTW, Boost.Format might be something like what you're looking for. It is not as efficient as `printf`, but it does the same things and ensures type safety. – Andy Prowl Jan 02 '13 at 17:50
  • Thanks, I'll be checking it out! – Rubens Jan 02 '13 at 17:52
  • @AndyProwl: "Not as efficient" is an understatement. In fact Boost Format is horribly slow. – vitaut Jan 03 '13 at 02:48
  • @vitaut: Agree. I just did not want to be too drastic, after all it does the job, it is type safe, and for a simple program it shouldn't be a performance killer. Of course, if the goal is to create a heavy load, scalable HTTP server, then it will likely have a serious impact. But then again I believe it would be better to rely on some serious framework, as other users suggested. – Andy Prowl Jan 03 '13 at 11:43