2

Base question Please read this question, even if you don't have time to look at all the other details. :) - I need to store multiple objects of my custom class (Tuple) into a set. That class contains only a vector of string. (As required by the project). How do I implement this so that it recognizes each element uniquely (and therefore only eliminates identical vectors)? 2 - I was suggested by my professor that I could inherit from vector string in my custom class (Tuple) so that I wouldn't have to write my own "operator<"

  • 1 - Is this true?
  • 2 - If not is there something else I could use inheritance from to get this functionality?

Further Details, if you have time :)

I've looked around on the web for a bit and haven't found a solution to my problem. First off, I will admit this is a school assignment (A simple Relational Database) so I have some requirements to follow (even If I don't now see why some of these things are to be done this way).

  • Required Classes

  • --> "Relation" ------> must use a std::set to hold Tuples

  • -->"Tuple" ------> is a list of "attribute values"

I was given the hint in class that if I extended the vector of string that the I would not to write my own operator< in class "Tuple". I don't know that this is a kosher suggestion my professor gave, but not having to write a complicated operator< class sounds appealing to me.

The Issue: I need to store a set of "Tuples" a class which I am to create. The problem I have right now is that only the first "Tuple" is stored. I used a vector alongside the set just so I could see if my code is trying to do what I want, and it looks like my problem is that the set is recognizing each "Tuple" I insert as equivalent to the already existing "Tuple", thus I end up with a set of only 1 "Tuple" even though I should have more.

I've included code that I see as relevant below. To keep you from looking at way to much stuff, at the bottom I show the function that is inserting new "Tuples" into an already existing "Relation", if you look in the Relation class you'll see the function "insertTuple(Tuple tupIn);" which receives a tuple and adds it to the set in question.

What I need to know?

  • Why is only one item being stored in the set?
  • How do I fix this so that all non-duplicate items are stored on the set? (given that I am not all insertions are duplicates, because they are not).

Thank you StackOverflow, you are a bunch of intelligent, patient human beings!

Tuple.h

 #include <iostream>
 #include <vector>
 #include "DatalogProgram.h"
 using namespace std;


 class Tuple : public vector<string>
 {
 private:
     vector<string> attributevals;
 public:
     Tuple();
     Tuple(Predicate input);
     ~Tuple();
     vector<string> returnAttributeVals();
     string toString();
 };

Tuple.cpp

 #include "Tuple.h"
 Tuple :: Tuple()
 {

 }
 Tuple :: Tuple(Predicate input)
 {
     // Here I will populate the tuple!
     attributevals.clear();
     for(int x = 0; x < input.returnParams().size(); x++)
     {
         //cout << "Adding value to Tuple: " << endl;
         //cout << input.returnParams().at(x).toString() << endl;
         attributevals.push_back(input.returnParams().at(x).toString());
     }
 }
 Tuple :: ~Tuple()
 {
     attributevals.clear();
 }
 vector<string> Tuple :: returnAttributeVals()
 {
     return attributevals;
 }

 string Tuple :: toString()
 {
     string value = "";
     for(int x = 0 ; x < attributevals.size() ; x++)
     {
         value = value + attributevals.at(x) + " -|- ";
     }
     return "Attribute Values : \n" + value;
 }

Relation.h

 #include <iostream>
 #include <set>
 #include <vector>
 #include <string>
 #include "Scheme.h"
 #include "Tuple.h"
 //#include "DatalogProgram.h"
 #pragma once
 using namespace std;

 class Relation
 {
 private:
     string Rname;
     Scheme Rscheme;
     set<Tuple> Rtuples;
     vector<Tuple> RtempVec;
 public:
     Relation(string nameIn, Scheme schemeIn);
     Relation(string nameIn, Scheme schemeIn, set<Tuple> tuplesIn);
     ~Relation();
     void insertTuple(Tuple tupIn);
     void select();
     void project();
     void rename();
     void testOutput();
     string returnName();
     Scheme returnScheme();
     set<Tuple> returnTuples();
     string toString();
 };

Relation.cpp #include "Relation.h"

 Relation :: Relation(string nameIn, Scheme schemeIn)
 {
     Rname = nameIn;
     Rscheme = schemeIn;
 }
 Relation :: Relation(string nameIn, Scheme schemeIn, set<Tuple> tuplesIn)
 {
     Rname = nameIn;
     Rscheme = schemeIn;
     Rtuples = tuplesIn;
 }
 Relation :: ~Relation()
 {
     Rname.clear();
     Rscheme.clear();
     Rtuples.clear();
 }
 void Relation :: insertTuple(Tuple tupIn)
 {
     cout << Rname << "  ->  Inserting the following Tuple :  \n" << tupIn.toString() << endl;
     Tuple temp = tupIn;
     Rtuples.insert(temp);
     cout << "Current # of tuples in this relation" << Rtuples.size() << endl;
     RtempVec.push_back(temp);
 }
 string Relation :: returnName()
 {
     return Rname;
 }
 Scheme Relation :: returnScheme()
 {
     return Rscheme;
 }
 set<Tuple> Relation :: returnTuples()
 {
     return Rtuples;
 }
 string Relation :: toString()
 {
cout << "Printing Relation " + Rname << endl;
cout << "Number of Tuples: " << Rtuples.size() << endl;
cout << "Number of tuples in temp Vector" << RtempVec.size() << endl;
set<Tuple> temp = Rtuples;
string result = "";
result = Rname + "\n" + Rscheme.toString() + "\n";
std::set<Tuple>::iterator setiter;
for(setiter = Rtuples.begin(); setiter!=Rtuples.end();setiter++)
{
    cout << "looping through tuples" << endl;
    Tuple temp = *setiter;
    result = result + " " + temp.toString() + "'\n";

}
return result;
 }
 void select();
 void project();
 void rename();
 void testOutput();

Now I've already Included way more code than anyone would want to look at but here is the function that creates the tuples I'm trying to insert into the relation (which go into the set).

 void Database :: ProcessFacts(vector<Predicate> input)
 {
     // So for facts I need to match the ID, then add the Paramaters to the tuple in the relation.
     // this means I itterate through the Relations already present, if it maches then I add a tuple!
     for (int x = 0; x < Drelations.size(); x++)
     {
         for(int y = 0; y < input.size() ; y++)
         {
             if(input.at(y).returnID() == Drelations.at(x).returnName())
             {
                 cout << "adding a tuple to  -->  " << Drelations.at(x).returnName() << endl;
                 cout << "Tuple is: " << Tuple(input.at(y)).toString() << endl;
                 Drelations.at(x).insertTuple(Tuple(input.at(y)));
             }
         }
     }
 }
M_J
  • 316
  • 5
  • 20
  • You may want to post less code here, if needed put it on a site like [liveworkspace](http://liveworkspace.org). Also when investigating a problem you don't understand, especially when you share it with others, try to produce a [minimal case](http://sscce.org/). Good luck;) – Antoine Aug 02 '14 at 22:16
  • It looks like you inherit from vector but don't actually store anything in that inherited vector, you don't override `operator<`, so all your Tuples are compared as empty vectors which are all the same. Based on your edit, if you were encouraged to inherit from `vector`, which is generally a bad idea, you should remove `attributevals` and store the strings in the object you inherited from, so just `push_back(...)` – Retired Ninja Aug 02 '14 at 22:23
  • @RetiredNinja The most useful input so far! How would I implement this so that I'm storing my information in an inherited vector and thus inheriting the correct operator< without writing my own operator -- writing my own operator< would be quite complicated when comparing vectors... – M_J Aug 02 '14 at 22:28
  • 1
    Currently your tuple has 2 vectors: one inherited and one in the attributevals field. You store in the field but < uses the inherited one (empty). Fix it by removing attributevals and just use the inherited push_back – Antoine Aug 02 '14 at 23:20
  • That seems to do the trick, I'll keep working with this, but so far that has helped. The code definitely feels weird. – M_J Aug 02 '14 at 23:39

1 Answers1

2

The problem is that you are inheriting from vector to avoid writing your own operator<, but you are storing the data in a member vector. The inherited operator< has no idea about the member vector, and since you never add anything to the inherited vector all of your objects compare as the same since all empty vectors are the same.

Inheriting from a standard container will work, but it isn't always the best idea since they do not have a virtual destructor. For this assignment it should be okay, but you should do some research so you are aware of the issues in the future.

Here's an example that demonstrates the issue with your current code as well as showing two alternatives using inheritance and a member vector. I'd recommend the member vector since it avoids the issues of inheriting from a standard container and the operator< is just one line, but it's up to you.

#include <cstdlib>
#include <iostream>
#include <set>
#include <string>
#include <vector>

class ProblemTuple : public std::vector<std::string>
{
public:
    void AddAttribute(const std::string& item)
    {
        attributes.push_back(item);
    }

    void Print() const
    {
        for(const std::string& a : attributes)
        {
            std::cout << a << " ";
        }
        std::cout << "\n";
    }
private:
    std::vector<std::string> attributes;
};

class InheritedTuple : public std::vector<std::string>
{
public:
    void AddAttribute(const std::string& item)
    {
        push_back(item);
    }

    void Print() const
    {
        for(const std::string& a : *this)
        {
            std::cout << a << " ";
        }
        std::cout << "\n";
    }
};

class CompositeTuple
{
public:
    void AddAttribute(const std::string& item)
    {
        attributes.push_back(item);
    }

    void Print() const
    {
        for(const std::string& a : attributes)
        {
            std::cout << a << " ";
        }
        std::cout << "\n";
    }

    bool operator<(const CompositeTuple& rhs) const
    {
        return attributes < rhs.attributes;
    }

private:
    std::vector<std::string> attributes;
};

std::string randomString()
{
    std::string ret;
    for(int i = 0; i < 3; ++i)
    {
        ret += 'a' + (std::rand() % 26);
    }
    return ret;
}

template<typename T>
void PrintSet(const std::string& title, const std::set<T>& set)
{
    std::cout << title << "\n";
    for(const auto& s : set)
    {
        std::cout << "   ";
        s.Print();
    }
}

int main()
{
    //Not calling srand on purpose so the result should be the same every time.
    std::set<ProblemTuple> ProblemSet;
    std::set<InheritedTuple> InheritedSet;
    std::set<CompositeTuple> CompositeSet;

    for(int i = 0; i < 3; ++i)
    {
        ProblemTuple pt;
        InheritedTuple it;
        CompositeTuple ct;
        for(int j = 0; j < 3; ++j)
        {
            std::string s(randomString());
            pt.AddAttribute(s);
            it.AddAttribute(s);
            ct.AddAttribute(s);
        }
        ProblemSet.insert(pt);
        InheritedSet.insert(it);
        CompositeSet.insert(ct);
    }

    PrintSet("Problem", ProblemSet);
    PrintSet("Inherited", InheritedSet);
    PrintSet("Composite", CompositeSet);

    return 0;
}
Retired Ninja
  • 4,785
  • 3
  • 25
  • 35
  • Wonderful help! Thanks for taking the time to explain this, hopefully this is not only useful for me, but others that have a similar lack of understanding of inheritance. – M_J Aug 03 '14 at 00:39