views:

88

answers:

5

Hi everyone. I would appreciate any help with this.
I have a simple container template class Dataset.
Also there is a specialization to allow a different implementation when a Dataset of Datasets is instantiated.

Since a Dataset of Datasets would be a heterogeneous container, there is a base abstract class List wich must declare a common interface for every Dataset. That would be get().

The problem is: I cannot specify a return type in List::get() so I need to override the void* return in descendants.

I´ve read the following restrictions for covariance:

•The function B::f returns a reference or pointer to a class of type T, and A::f returns > a pointer or a reference to an unambiguous direct or indirect base class of T.
•The const or volatile qualification of the pointer or reference returned by B::f has the same or less const or volatile qualification of the pointer or reference returned by A::f.
•The return type of B::f must be complete at the point of declaration of B::f, or it can be of type B.

http://publib.boulder.ibm.com/infocenter/comphelp/v8v101/index.jsp?topic=/com.ibm.xlcpp8a.doc/language/ref/cplr139.htm

Well, make the contained objects descend from the same base class is of no use to me.
So, is there a workaround to this? Better yet: what is the proper c++ way to do this?

In case you wonder, what I am trying to do is is a 'relational container', so to speak. But you won´t see the relational part in the code below anyway.
The goal is to store data keeping the container unaware of any relation beetween list. Then I would store the relations in a list apart. For example:

Person list
0 John
1 Arthur
2 Caroline

Car list
0 - Ford
1 - Volkswagen
2 - Chrysler

Relations list
0 - 1;2
1 - 0;1
2 - 0;2

Pretty much as a relational database. Also, i have minor side problems, like making set() to accept a maxsize number of arguments. Couldn´t achieve this with va_list. Maybe I rushed through this, anyway.

Final point: I´m deliberately avoiding STL for learning sake, BUT if there is already such functionality available I would like to know.

Thanks a lot!

The code below:

#include <iostream>
using namespace std;

//--------------------------------------------------//
// TRIVIAL DUMMY CLASSES

class Person
{ 
 public:
 Person(char* n, unsigned int a): _name(n), _age(a){}

 char*   name(){cout << "Im " << _name << endl; return _name;}
 unsigned int age(){cout << "Im " << _age << " years old." << endl; return _age;} 

 private:
 char*   _name;
 unsigned int _age;
} p1("john", 28), p2("Arthur", 26), p3("Caoline", 31);

class Car
{ 
 public:
 Car(char* m, unsigned int y): _model(m), _year(y){}

 char*   model(){cout << "Its a " << _model << endl; return _model;}
 unsigned int year(){cout << "Im" << _year << " years old." << endl; return _year;}

 private:
 char*   _model;
 unsigned int _year;
} c1("Chrysler C-300", 1955), c2("Chrysler VH Charger", 1971), c3("Ford Fairlane", 1960);
//--------------------------------------------------//

class List
{
 public:  
 List(): length(0){}

 // common interface so Lists of Lists can be created
 // can´t specify a return type so it returns void*
 // BUT need to override it in descendants. WHY IT DOESN´T WORK?
 virtual void* get(unsigned int i) = 0; 

 protected:
 unsigned int length;
};
//--------------------------------------------------//

template <class C, unsigned int maxsize>
class Dataset: public List
{
 public:
 Dataset()
 {
  // initialize pointers to null
  for(int i = 0; i < maxsize; i++){
   data[i] = 0;
  }
 };

 // C* return type is ignored
 C* get(unsigned int i)
 {return data[i];};

 int set(C* dataIn)
 {  
  data[length] = dataIn;
  return length++;
 };

 protected:
 C* data[maxsize];
};

template <unsigned int maxsize>
class Dataset <List, maxsize>: public List
{ 
 public:
 Dataset()
 {
  // initialize pointers to null
  for(int i = 0; i < maxsize; i++){
   data[i] = 0;
  }
 };

 List* get(unsigned int i)
 {return data[i];};

 int set(List* dataIn)
 {  
  data[length] = dataIn;
  return length++;
 };

 protected:
 List* data[maxsize];
};
//--------------------------------------------------//
int main()
{  
 Dataset <Person, 3> columnPerson;
 // populate person list
 columnPerson.set(&p1);
 columnPerson.set(&p2);
 columnPerson.set(&p3); 

 Dataset <Car, 3> columnCar;
 // populate car list
 columnCar.set(&c1);
 columnCar.set(&c2);
 columnCar.set(&c3);


 Dataset <List, 10> relations; // create a list of lists
 // populate it
 relations.set(&columnPerson);
 relations.set(&columnCar);

 // getting a void* and casting it
 Person* ptrPerson = (Person*) relations.get(0)->get(0); 
 ptrPerson->name();

 int i;
 cin >> i;
};
A: 

I'm not gonna try to answer the implementation details, but you can, as you requested, get functionality out of the boost.any library. There is a tutorial on heterogenous containers using it.

André Caron
A: 

When you override a virtual function, everything must be the same (parameters, name, return type). Also, you cannot override a function that differs only by return type (as you are trying to do with the one that returns C*.

You may have to stick with a void * and cast it to the proper type. Of course, you'll need to know what type it is supposed to be. You are pushing the envelope of heterogeneous containers. In normal cases, they need to have a common interface. If you want to return different types, you do not have a common interface.

JoshD
A: 

Hello John. I think it would end up in the same interface mismatch problem. The idea is to store pointers of any class and relate them by their key in the container. I though about inverting things and get rid of List base class, deriving contained objects from an interface class, say IStorable. I liked the idea, but how would be an interface that matches arguments and return type with any or almost any object...

Thank you all. I´ll check Boost meanwhile...

Rodolfo
To reply to an answer, add a comment to it rather than posting a new answer. That way it's clear who you're talking to and it won't be confused as an answer to the question.
JoshD
A: 

Considering the 'cast from void*' solution. What would you think about creating a enum{CLASS1, CLASS2, CHAR, UINT} (enum of types) and storing the corresponding int with the pointer. Also create an ugly function spproximate to this...

function cast_to(int, pointer){  
   switch(int)
       case 0:
           return dynamic_cast<CLASS1*>(pointer);
       break;
       case 1:
           return etc...
}

One would have to extend the function and the enum, for each new storable class... Im one inch of throwing away the 'relational' fiasco.

p.d. Sorry for my other interface inconsistencies (comment vs answer). Im new to stack overflow

Rodolfo
A: 

Well, here it is.

http://calumgrant.net/relational/

Thanks for the replies. I still can´t find the damm add a comment link :)

CASE CLOSED.

Rodolfo