0

I'm trying to debug my C++ program as I build it part-by-part. Right now, All I'm trying to do is test my procedure of reading an html file into a string (By the way, will that work as straightforwardly as reading from a text file? It's basically the same thing, right?), and am getting an error that is referencing different parts of my of class implementation, even though that implementation is not at all used in the running of my code. My compiler is XCode 6 on Mac OSX Yosemite on a Macbook Pro with Intel Core i5. It is not flagging any errors until I actually try to compile.

Error:

Undefined symbols for architecture x86_64:
  "HtmlProcessor::HtmlProcessor()", referenced from:
      _main in main.o
  "HtmlProcessor::~HtmlProcessor()", referenced from:
      _main in main.o
  "DocTree::_hp", referenced from:
      DocTree::setTree(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) in CSS_Generator.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Since I'm not allowed to post images inline here, check out this link, the full error: https://i.stack.imgur.com/1BEqn.png

Without doing a complete "code dump", I'll post an abbreviated version of my files, including what seem to be the relevant parts.

main.cpp:

#include <iostream>
#include "CSS_Generator.h"

int main(int argc, const char * argv[]) {
    node dummyNode;
    HtmlProcessor HP;
    HP.processFile("sample.html", dummyNode);

    return 0;
}

CSS_Generator.h

#ifndef __CSS_Template_Generator__CSS_Generator__
#define __CSS_Template_Generator__CSS_Generator__

#include <stdio.h>
#include <string>
#include <vector>
#include <utility>
#include <set>

struct node
{
    std::string element_type;
    std::set<std::string> class_list;
    std::string iden;
    std::vector<node*> children;
};

class HtmlProcessor
{
public:
    HtmlProcessor();
    ~HtmlProcessor();
    void processFile(const std::string &, node &);

private:
    bool _getNextBracket(std::pair<std::string::const_iterator &, const std::string::const_iterator &> &, bool &);
    bool _processTag(std::string::const_iterator, const std::string::const_iterator, node &);
    bool _hasNextAttribute(std::string::const_iterator &, const std::string::const_iterator &, std::pair<std::string, std::string> &);
    bool _processAttributes(const std::vector<std::pair<std::string, std::string>> &, std::set<std::string> &, std::string &);
    std::vector<std::string> _splitWithoutQuotes(std::string::const_iterator, std::string::const_iterator);
    bool _splitAttribute(const std::string &, std::pair<std::string, std::string> &);
    static const std::string _elementTypeChars;
    static const std::string _attributeTypeChars;
    static const std::string _attributeValChars;
};

class DocTree
{
public:
    DocTree();
    ~DocTree();
    void setTree(const std::string &);
    void getCSS(const std::string &);

private:
    node * _root;
    void _destroyTree(node *);
    std::string _tree2Str(node *);
    std::string _innerTree2Str(node *, unsigned);
    static HtmlProcessor _hp;
};


#endif /* defined(__CSS_Template_Generator__CSS_Generator__) */

CSS_Generator.cpp:

#include "CSS_Generator.h"

#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <algorithm>
#include <utility>
#include <fstream>

/* -------------------------- DocTree class implementation begin ---------------------------*/


void DocTree::setTree(const std::string & F)
{
    _destroyTree(_root);
    try
    {
        _hp.processFile(F, *_root);
    }
    catch (const std::string & error_message)
    {
        std::cout << error_message << std::endl;
    }
}

void DocTree::_destroyTree(node * rt)
{
    for (std::vector<node*>::iterator it(rt->children.begin()), offend(rt->children.end()); it != offend; ++it)
        _destroyTree(*it);

    delete rt;
}

std::string DocTree::_innerTree2Str(node * rt, unsigned depth)
{
    std::string css("");

    if (depth > 0)
        css.append(" > ");


    if (rt)
    {
        css.append(rt->element_type);

        if (!rt->class_list.empty())
        {
            for (std::set<std::string>::iterator it(rt->class_list.begin()), offend(rt->class_list.end()); it != offend; ++it)
                css.append("." + *it);
        }
        if (!rt->iden.empty())
        {
            css.append("#" + rt->iden);
        }
        if (!rt->children.empty())
        {
            std::string tabs(depth, '\t');
            for (std::vector<node*>::iterator it(rt->children.begin()), offend(rt->children.end()); it != offend; ++it)
                css.append("\n" + tabs + _innerTree2Str(*it, depth + 1));
        }
    }
    return css;
}

std::string DocTree::_tree2Str(node * rt)
{
    return _innerTree2Str(rt, 0);
}


/* -------------------------- DocTree class implementation end -------------------------------*/

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */


/* ---------------------- HtmlProcessor class implementation begin -------------------------- */


const std::string HtmlProcessor::_elementTypeChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
const std::string HtmlProcessor::_attributeTypeChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_";
const std::string HtmlProcessor::_attributeValChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_ ";

void HtmlProcessor::processFile(const std::string & F, node & nd)
{
/*  F: Path of HTML file
   nd: node element that will be the root of the document tree tree if processing is successful

      Throws an error if any problems occur during the processing
*/
    std::ifstream html_file(F);
    std::string html_str, line;
    while(std::getline(html_file, line)) html_str.append(line);
    /* ----- TEST ----- */
    std::cout << html_str;
    /* ---------------- */


}


bool HtmlProcessor::_processTag(std::string::const_iterator it1, const std::string::const_iterator it2, node & nd)
{
    /*
       [it1, it2): iterators for the range of the string
               nd: node in which classes and ids of the tage are stored

        Returns true or false depending on whether a problem was encountered during the processing.
    */



    /* Get the element type, at the beginning of the tag: */
    std::string elementType("");
    while (_elementTypeChars.find(*it1) != std::string::npos && it1 != it2) elementType.push_back(*it1++);
    if (elementType.empty()) return false;
    nd.element_type = elementType;

    /* Get any attributes: */
    std::vector<std::pair<std::string, std::string>> attributes;
    std::pair<std::string, std::string> thisAttribute;
    while (_hasNextAttribute(it1, it2, thisAttribute)) attributes.push_back(thisAttribute);
    if (!_processAttributes(attributes, nd.class_list, nd.iden)) return false;


    return true;
}

bool HtmlProcessor::_hasNextAttribute(std::string::const_iterator & it1, const std::string::const_iterator & it2, std::pair<std::string, std::string> & attrHolder)
{
      /* Parses the first HTML attributes in the iterator range [it1, it2), adding them to attrHolder; eg.

         class="myClass1 myClass2" id="myId" onsubmit = "myFunction()"

         ----------  _hasNextAttribute  -------->

         attrHolder = (class, myClass1 myClass2)

         When the function terminates, it1 will be the iterator to the last character parsed, will be equal to 
         it2 if no characters were parsed.

      */

    while (*it1 == ' ' && it1 != it2) ++it1; /* Skip through left whitespace padding */
    if (it1 == it2) return true; /* No attributes in tag; only whitespace after the element name. Such is valid HTML. */

    std::string attr(""); /* String to hold the attribute type, expected after any whitespace. Should be non-empty. */
    while (_attributeTypeChars.find(*it1) == std::string::npos && it1 != it2) attr.push_back(*it1++);
    if (attr.empty()) return false;

    while (*it1 == ' ' && it1 != it2) ++it1; /* Skip through whitespace padding between the attribute name and equals sign */
    if (*it1 != '=' || it1++ == it2) return false; /* Current character should be an equals sign */

    while (*it1 == ' ' && it1 != it2) ++it1; /* Skip through whitespace between the equals sign and quotation mark */
    if (*it1 != '"' || it1++ == it2) return false; /* Current character should be a quotation mark */

    std::string val(""); /* String to hold the attribute's value, exepcted after the first quotation mark. */
    while (_attributeValChars.find(*it1) != std::string::npos) val.push_back(*it1++);
    if (attr.empty()) return false;

    if (*it1 != '"' || it1++ != it2) return false; /* Current character should be a quotation mark */

    /* If we're here, it1 should point to the character after the quotation mark that closes off the attribute's value */
    attrHolder = std::make_pair(attr, val);

    return true;

}

bool HtmlProcessor::_processAttributes(const std::vector<std::pair<std::string, std::string>> & attrs, std::set<std::string> &classesTarget, std::string & identifierTarget)
{
    for (std::vector<std::pair<std::string, std::string>>::const_iterator it(attrs.cbegin()), offend(attrs.end()); it != offend; ++it)
    {
        std::string thisAttr(it->first), thisVal(it->second);
        std::transform(thisAttr.begin(), thisAttr.end(), thisAttr.begin(), ::tolower);
        if (thisAttr == "id")
            identifierTarget = thisVal;
        else if (thisAttr == "class")
        {
            /* Since the value for a class attribute can be several classes separated by whitespace, 
               add all of them to set of classes for the node.
            */
            std::stringstream ss(thisAttr);
            std::string thisClass;
            while (std::getline(ss, thisClass, ' ')) classesTarget.insert(thisClass);
        }

    }


    return true;
}

bool HtmlProcessor::_getNextBracket(std::pair<std::string::const_iterator &, const std::string::const_iterator &> & bounds, bool & IQ)
{
    bool foundOne = false;
    while (bounds.first != bounds.second)
    {
        switch (*bounds.first)
        {
            case '<':
                if (!IQ) foundOne = true;
                break;

            case '>':
                if (!IQ) foundOne = true;
                break;
            case '"':
                IQ = !IQ;
                break;
        }
        if (foundOne) break;
        ++bounds.first;
    }
    return foundOne;
}

/* ----------------------------- HtmlProcessor class implementation end --------------------------- */

0 Answers0