I'm using a pattern of assigning a base class to a templated class so that I can put different types in a vector, vis-a-vis Attribute<String>
and Attribute<int>
, and the reason for this is that I want a vector containing different objects that inherit the same base object.
The problem that I am getting of spurious text being generated relates to the output that is generated once the Base object is retrieved from the vector and cast back to the original Attribute template object.
Problem Output, using inline comments to show where output differed from expectation:
T (String)
ID: Id-1
Key: -�'��,�'�8���Id-1 // expected key1
Value: // expected one
T (String)
ID: Id-2
Key: -�'��,�'�8���Id-2 // expected key2
Value: // expected two
T (String)
ID: Id-3
Key: -�'��,�'�8���Id-3 // expected key3
Value: // expected three
T (int)
ID: Id-4
Key: -�'��,�'�8���Id-4 // expected key4
Value: 0 // expected 4
T (String)
ID: Id-5
Key: -�'�-�'�8���Id-5 // expected key5
Value: // expected 5
T (int)
ID: Id-6
Key: -�'�0-�'�8���Id-6 // expected key6
Value: 0 // expected 6
Here is the reproducible example, I've added a Makefile which uses the c++ compiler instead of g++ compiler as on Mac (where I am doing this) C++17 isn't fully implemented yet.
harness.cpp
#include <iostream>
#include "Attribute.h"
#include <vector>
using namespace std;
using String = std::string;
int main()
{
// TEST THE Attribute CLASS BY ITSELF
Attribute <String> att("testkey","testvalue", TypeRef::String, "testId");
cout << "Key: "+att.getKey() << endl;;
cout << "Value: "+att.getValue() << endl;
cout << "Id: "+att.getId() << endl;
cout << endl;
/* Output:
Key: testkey
Value: testvalue
Id: testId
*/
// TEST SIX INSTANCES OF Attribute CLASS BEFORE ADDING TO vector
std::vector<AttributeObject> vector;
Attribute<String> q("key1","one",TypeRef::String, "Id-1"); AttributeObject &qBase = q;
cout << "T (String)" << endl;
cout << "Id1: " << q.getId() << endl;
cout << "Key1: " << q.getKey() << endl;
cout << "Value1: " << q.getValue() << endl;
cout << endl;
Attribute<String> w("key2","two",TypeRef::String, "Id-2"); AttributeObject &wBase = w;
cout << "T (String)" << endl;
cout << "Id2: " << w.getId() << endl;
cout << "Key2: " << w.getKey() << endl;
cout << "Value2: " << w.getValue() << endl;
cout << endl;
Attribute<String> e("key3","three",TypeRef::String, "Id-3"); AttributeObject &eBase = e;
cout << "T (String)" << endl;
cout << "Id3: " << e.getId() << endl;
cout << "Key3: " << e.getKey() << endl;
cout << "Value3: " << e.getValue() << endl;
cout << endl;
Attribute<int> r("key4",4,TypeRef::Int, "Id-4"); AttributeObject &rBase = r;
cout << "T (int)" << endl;
cout << "Id4: " << r.getId() << endl;
cout << "Key4: " << r.getKey() << endl;
cout << "Value4: " << r.getValue() << endl;
cout << endl;
Attribute<int> t("key5",5,TypeRef::String, "Id-5"); AttributeObject &tBase = t;
cout << "T (int)" << endl;
cout << "Id5: " << t.getId() << endl;
cout << "Key5: " << t.getKey() << endl;
cout << "Value5: " << t.getValue() << endl;
cout << endl;
Attribute<int> y("key6",6,TypeRef::Int, "Id-6"); AttributeObject &yBase = y;
cout << "T (int)" << endl;
cout << "Id6: " << y.getId() << endl;
cout << "Key6: " << y.getKey() << endl;
cout << "Value6: " << y.getValue() << endl;
cout << endl;
cout << endl;
/* Output:
T (String)
Id1: Id-1
Key1: key1
Value1: one
T (String)
Id2: Id-2
Key2: key2
Value2: two
T (String)
Id3: Id-3
Key3: key3
Value3: three
T (int)
Id4: Id-4
Key4: key4
Value4: 4
T (int)
Id5: Id-5
Key5: key5
Value5: 5
T (int)
Id6: Id-6
Key6: key6
Value6: 6
*/
vector.push_back(qBase);
vector.push_back(wBase);
vector.push_back(eBase);
vector.push_back(rBase);
vector.push_back(tBase);
vector.push_back(yBase);
// TEST ALL Attribute CLASS INSTANCES AS EXTRACTED FROM A vector
int x = 0;
for (AttributeObject baseObject : vector) {
TypeRef typeRef = baseObject.getTypeRef();
if(typeRef == TypeRef::String)
{
cout << endl;
cout << "T (String)" << endl;
Attribute <String> *pChild = (Attribute <String> *) &baseObject;
cout << "ID: " << pChild->getId() << endl;
const String sKey = pChild->getKey();
cout << "Key: " << sKey << endl;
const String sValue = pChild->getValue();
cout << "Value: " << sValue << endl;
}
else if(typeRef == TypeRef::Int)
{
cout << endl;
cout << "T (int)" << endl;
Attribute <int> *pChild = (Attribute <int> *) &baseObject;
cout << "ID: " << pChild->getId() << endl;
const String sKey = pChild->getKey();
cout << "Key: " << sKey << endl;
const int iValue = pChild->getValue();
cout << "Value: " << (int)iValue << endl;
}
x++;
}
/* Output (with differing expected values added as inline comments)
T (String)
ID: Id-1
Key: -�'��,�'�8���Id-1 // expected key1
Value: // expected one
T (String)
ID: Id-2
Key: -�'��,�'�8���Id-2 // expected key2
Value: // expected two
T (String)
ID: Id-3
Key: -�'��,�'�8���Id-3 // expected key3
Value: // expected three
T (int)
ID: Id-4
Key: -�'��,�'�8���Id-4 // expected key4
Value: 0 // expected 4
T (String)
ID: Id-5
Key: -�'�-�'�8���Id-5 // expected key5
Value: // expected 5
T (int)
ID: Id-6
Key: -�'�0-�'�8���Id-6 // expected key6
Value: 0 // expected 6
*/
return 0;
}
Attribute.cpp (here just for the sake of the Makefile because the c++ complier generates a nasty warning if you don't use a .cpp file):
#include "Attribute.h"
Attribute.h:
#include <iostream>
#include <string>
#include <type_traits>
#include <vector>
using String = std::string;
enum class TypeRef {
String,
Int
};
class AttributeObject{
public:
AttributeObject() {}
AttributeObject(TypeRef typeRef, String Id) : typeRef(typeRef), id(Id) {}
TypeRef getTypeRef()
{
return this->typeRef;
}
String getId()
{
return this->id;
}
protected:
TypeRef typeRef;
String id;
};
template<class T>
class Attribute : public AttributeObject {
public:
using value_type = T;
Attribute(const String& Key, const T& Value, const TypeRef& TypeRef, const String& Id) :
AttributeObject(TypeRef, Id),
key(Key),
value(Value)
{}
String const& getKey() const {
return key;
};
T const& getValue() const {
return value;
}
TypeRef const& getTypeRef() const {
return typeRef;
}
private:
String key;
T value;
};
Makefile:
CC=c++
FLAGS=-c -g -std=c++17
All: build
mkdirs:
# In mkdirs:
mkdir -p obj
build: clean mkdirs harness.o Attribute.o
# In build:
$(CC) obj/harness.o obj/Attribute.o -o harness
ls
harness.o: harness.cpp
# harness.o:
$(CC) $(FLAGS) harness.cpp -o obj/harness.o
ls obj
Attribute.o: Attribute.cpp
$(CC) $(FLAGS) Attribute.cpp -o obj/Attribute.o
ls obj
clean:
# In clean:
rm -rf obj
ls
Kind regards.