7
\$\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()!

(53 lines! Now I am proud of myself :D )

#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\$
  • \$\begingroup\$ What should I name my code? libhtml? \$\endgroup\$ – Gustavo6046 Jan 6 '16 at 15:58
3
\$\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\$
  • \$\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\$ – Gustavo6046 Jan 6 '16 at 17:09
  • \$\begingroup\$ Oh, and this is improved enough? paste.ubuntu.com/14422250 \$\endgroup\$ – Gustavo6046 Jan 6 '16 at 17:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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