8
\$\begingroup\$

As a follow-up to my code's v0.1 revision, we now introduce V0.3, with capability for storage of attributes and a struct for each HTML tag. Just start the struct, call its functions, change its variables then use <fstream> to make the complete HTML file using generateHTML()!

#include <iostream>
#include <fstream>
#include <vector>

struct HtmlTag {
    std::string tagname;
    std::string tagcontent;
    std::vector<std::string> tagattributes;
    std::vector<std::string> tagattrcontent;
    void addAttribute(std::string attrName, std::string attrCont) {
        tagattributes.push_back(attrName);
        tagattrcontent.push_back(attrCont);
    }
    int removeAttribute(int pos) {
        tagattributes.erase(tagattributes.begin() + pos);
        tagattrcontent.erase(tagattrcontent.begin() + pos);
        return tagattributes.size();
    }
    std::string returnTag() {
        std::string attributes = "";
        if (tagattributes.size() != tagattrcontent.size()) {
            return "ERR:UNMATCHINGLENGTH";
        }
        unsigned short i;
        if(tagattributes.size() > 0 && tagattrcontent.size() > 0) {
            for(i = 0; i < tagattributes.size(); i++) {
                attributes = attributes + " " + tagattributes[i] + "=\"" + tagattrcontent[i] + "\"";
            }
        }
        else {
            return "<" + tagname + ">" + tagcontent + "</" + tagname + ">";
        }
        return "<" + tagname + attributes + ">" + tagcontent + "</" + tagname + ">";
    }
};

std::string generateHTML(std::string doctype, std::string head, std::string body) {
    return "<!DOCTYPE " + doctype + " />\n\n<html>\n    <head>\n        " + head + "\n    </head>\n    <body>\n        " + body + "\n    </body>\n</html>";
}

int main()
{
    HtmlTag myHTML;
    myHTML.addAttribute("spam", "eggs");
    myHTML.tagcontent = "bar";
    myHTML.tagname = "foo";
    unsigned int i2;
    for(i2 = 0; i2 < myHTML.tagattrcontent.size(); i2++) {
        std::cout << myHTML.tagattrcontent[i2] << ":" << myHTML.tagattributes[i2] << std::endl;
    }
    std::cout << myHTML.returnTag() << std::endl;
    std::cout << generateHTML("html", "<title>Hello World!</title>", myHTML.returnTag()) << std::endl;
}
\$\endgroup\$
1
  • \$\begingroup\$ What should I name my code? libhtml? \$\endgroup\$
    – wallabra
    Commented Jan 6, 2016 at 15:58

1 Answer 1

4
\$\begingroup\$

I see some things that may help you improve your code.

Prefer class to struct

The way it's currently defined, any code can modify the contents of the structure. Better would be to make the data members private and provide necessary accessors to assure that the class is always complete and coherent.

Don't use parallel structures

Right now, the tag attributes and tag contents are stored in parallel vectors. The only association between an attribute and its value is that they have the same position within both vectors. Instead, use a std::pair and create a vector of those. That way, each attribute is a single entity. We can replace the two vectors with this one:

std::vector<std::pair<std::string,std::string>> attributes;

Use "range-for" to simplify code

C++11 and newer allow the use of "range-for" which can really simplify code. For instance, the current code has this loop:

unsigned int i2;
for(i2 = 0; i2 < myHTML.tagattrcontent.size(); i2++) {
    std::cout << myHTML.tagattrcontent[i2] << ":" << myHTML.attributes[i2] << std::endl;
}

If we follow the previous point and have a single attributes vector instead, we can rewrite this as:

for (const auto &attr : myHTML.attributes) {
    std::cout << attr.first << ":" << attr.second << std::endl;
}

Use const where practical

In the returnTag routine, the underlying HtmlTag is not altered. Make this explicit by declaring that method const:

std::string returnTag() const {

Think of the user

As a user of this code, I think I'd prefer to remove an attribute by name rather than by position. It's also not clear that returning the new count of attributes would be useful if the caller already has to keep track of the indexing.

Provide a constructor

It seems to me that it would be nice to be able to write code like this:

HtmlTag myHTML{"foo","bar",{{"spam","eggs"}}};

We can do that by providing the appropriate constructor. In this case, it's actually quite simple:

HtmlTag(std::string name, std::string content, std::vector<std::pair<std::string,std::string>> attr) :
    tagname{name},
    tagcontent{content},
    attributes{attr}
{}

Note that I'm assuming the attributes are now pairs as previously suggested.

Use your own code

Why doesn't the generateHTML code use any HtmlTags? Here's a way it could be rewritten to do so:

std::string generateHTML(std::string doctype, std::string headtext, std::string bodytext) {
    HtmlTag body{"body",bodytext};
    HtmlTag head{"head",headtext};
    HtmlTag html{"html",head.returnTag()+body.returnTag()};
    return "<!DOCTYPE " + doctype + " />\n\n" + html.returnTag();
}

If that's not the way you'd like to write the code, it may suggest some improvements that might be made to the HtmlTag class.

\$\endgroup\$
2
  • \$\begingroup\$ I can't use C++11 standards for I use Code::Blocks inbuilt compiler, but thanks anyway, I'll put the most improvements of these I can on the code :) \$\endgroup\$
    – wallabra
    Commented Jan 6, 2016 at 17:09
  • \$\begingroup\$ Oh, and this is improved enough? paste.ubuntu.com/14422250 \$\endgroup\$
    – wallabra
    Commented Jan 6, 2016 at 17:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.