Nested Template Structure

closed account (zb0S216C)
Firstly, here's my code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>
#include <vector>

using std::cout;
using std::endl;
using std::vector;

struct Entry
{
      template < typename ENTRY_TYPE >
      struct Sub
      {
            Sub( ENTRY_TYPE init ) : value( init ) { }
            ENTRY_TYPE value;
      };
} a, b, c, d;

struct Register
{
      vector < Entry > entries;
} reg;

int main( )
{
      a.Sub < int > sub( 5 );
      b.Sub < float > sub( 4.5f );
      c.Sub < char > sub( '7' );
      d.Sub < double > sub( 4.23 );

      reg.entries.push_back( a );

      return 0;
}


When I try to compile this, I get the following error messages:

In function 'int main()':
error: invalid use of 'struct Entry::Sub<int>'
error: expected ';' before 'sub'
error: 'b' was not declared in this scope
error: expected primary-expression before 'float'
error: expected ';' before 'float'
error: 'c' was not declared in this scope
error: expected primary-expression before 'char'
error: expected ';' before 'char'
error: 'd' was not declared in this scope
error: expected primary-expression before 'double'
error: expected ';' before 'double'


It's quite obvious what I'm trying to achieve here. So, tell me, what am I doing wrong here?
t's quite obvious what I'm trying to achieve here.
Are you tying to implement a variant type?
closed account (zb0S216C)
Basically, I'm trying to 4 create instances of Entry::Sub with different types. When defined, I push the instances into the reg.entries vector. That's it.

I still don't know why it's disliking the instance definitions.
It looks like you want a „generic” variable type. If your generic variable will only store values of primitive types then union is all you need. Otherwise, your “Entry” structure should contain a pointer to a generic variable’s data, allocated from the heap. Or you could simply use boost::any.
closed account (zb0S216C)
If your generic variable will only store values of primitive types then union is all you need

I've used the basic type specifiers for testing purposes.

Or you could simply use boost::any

I'm trying to avoid the use of Boost. Please don't ask why.
Lines 25-28...where is "sub" defined? Shouldn't it be "Sub" with a capital S?
closed account (zb0S216C)
where is "sub" defined?
sub is the identifier of the new instance of Entry::Sub.
Oh, sorry, I see.

Maybe you could have the templated class inherit the main class?
Last edited on
closed account (zb0S216C)
Maybe you could have the templated class inherit the main class?

Which class are you referring to?
OK, you don’t want to use boost. But you could still check how boost::any is implemented.

What about something like this?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct Entry
{
   struct BaseSub
   {
   };
   
   template <typename T>
   struct Sub : public BaseSub
   {
      Sub(const T & init) : value(init){}
      T value;
   };

   BaseSub * ptr;

   template <typename T>
   Entry(const T & init) : ptr(new Sub<T>(init)){}

   // Need to implement copy constructor / assignment operator / destructor as well.
};
Last edited on
closed account (zb0S216C)
I don't understand the use of BaseSub.
Entry has a pointer to the base of your types. The constructor fills in the appropriate kind of type class.

It's an extensible variant type.
BaseSub is not really needed in the above, quite limited, example. But you will need it to implement even very basic functionality. The problem is, Entry doesn't know anything about the type of data it holds. Lets suppose you want to write an assignment operator. How will you copy the data without knowing its type? It's quite obvious that the method to perform copying should be a part of Sub. And how will you call this method from Entry? Declaring a pure virtual "clone" function in BaseSub, and implementing it in Sub, is the simplest technique to accomplish this task.
If you only have specific types you want to use, you can use inheritance from a generic class and have a vector of the generic class, then push the other class objects onto the vector. I wrote some code below but I can't test it for validity or even to see if it works because I am away from any compiler. I tested this code and fixed some mistakes and it now works perfectly!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <iostream>
#include <vector>
using namespace std;

struct Variable
{
  virtual unsigned char GetType(){ return(0); /*Not a type*/ }
};

struct Character: Variable
{
  char v;
  unsigned char GetType(){ return(1); /*Char*/ }
};

struct Integer: Variable
{
  int v;
  unsigned char GetType(){ return(2); /*Int*/ }
};

struct Double: Variable
{
  double v;
  unsigned char GetType(){ return(3); /*Double*/ }
};

int main()
{
  vector<Variable*> Vars;
  Character ch; ch.v = 5;
  Integer in; in.v = 47;
  Double db; db.v = 3.14159;

  Vars.push_back(&ch);
  Vars.push_back(&in);
  Vars.push_back(&db);

  for(unsigned long i = 0; i < Vars.size(); ++i)
  {
    if(Vars[i]->GetType() == 1)
    {
      cout << "Character: " << (short)((Character*)Vars[i])->v << endl;
    }
    else if(Vars[i]->GetType() == 2)
    {
      cout << "Integer: " << ((Integer*)Vars[i])->v << endl;
    }
    else if(Vars[i]->GetType() == 3)
    {
      cout << "Double: " << ((Double*)Vars[i])->v << endl;
    }
  }
  while(true);
}


Of course, this is just a basic example...I didn't include constructors or assignment operators to make it easier...I just wanted to prove that it works like you want. Also...the pointers in the vector would of course become invalid if the ch, in, and db variables were destroyed by going out of scope...so it may be better to dynamically create them with new.
Last edited on
Topic archived. No new replies allowed.