Compiler Optimization - Static Int Template

closed account (o1vk4iN6)
I was wondering if any could tell me if a compiler would optimize something like below. I was also wondering if what I am trying to do here can be done with meta-programming, if something similar to what I am trying to do here has been done before?

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

enum Types {
	TYPE_VALUE
};

// abstract virtual base class
template< class T, Types TYPE  >
class BaseElement {
public:
	BaseElement()
	{ if(!T::init) T::types[T::count++] = TYPE; }

	virtual void foo() = 0;
};

template< class T >
class ValueElement : public BaseElement<T, TYPE_VALUE> {
	void foo(){ }
};

template< class T > // using template here to make a static int's for each class
class BaseElementContainer {
public:
	static int count;
	static bool init;

	static Types types[ 512 ]; // if possible using templating could make this be something like this instead:
	// static int types[ COUNT ];
};

template< class T > int BaseElementContainer<T>::count = 0; // define and initialize static template member to zero
template< class T > bool BaseElementContainer<T>::init = false;
template< class T > Types BaseElementContainer<T>::types[ 512 ]; 

class ExampleContainer : public BaseElementContainer<ExampleContainer> {
public:
	ExampleContainer(){ ExampleContainer::init = true; } // after it's initialized make sure it doesn't keep adding

	ValueElement< ExampleContainer > a;
	ValueElement< ExampleContainer > b;
	ValueElement< ExampleContainer > c;
	ValueElement< ExampleContainer > d;
	ValueElement< ExampleContainer > e;
};

ExampleContainer example_init;

int main()
{
	std::cout << ExampleContainer::count << endl; // tada :\
}


I basically need it to know how many variables and what type of variables are in a class and the order they are in it.

I know this isn't the most efficient code, but it saves time and reduces human error. I'd probably use compiler define's to enable and disable this feature to improve performance when something like this isn't needed.

Thanks for any help.
This seems...clever. Makes me cringe though. I would be interested to hear a justification for it. There are a variety of problems of crouse. What if we forgot to set init to true? What if we extended from ExampleContainer? What if our data members are, say, "int" - would ValueElement<int> iMyInt; work as intended? Even if it did, what about ValueElement<int[2]> arrIntegers; Is that 1 variable, or 2?

I applaud your curious use of recursive templates, but I can't imagine the programming overhead is NEAR worth whatever benefit this provides.
closed account (o1vk4iN6)
Well I spent some time after I made that post to give an idea of what I plan to do.

I know there's some human error involved I can easily add a check in BaseElementContainer to makesure the user sets it to true, this is a lot less work than to maintain a static array describing the class:

BaseElementContainer(){ assert( !(!init && count) ); }

Basically if "init" was not set to true and count isn't zero than stop to inform user to do so.

Well array's aren't really a problem, maybe this will clear some things up:

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
// baseelement.h

#ifndef BASEELEMENT_H
#define BASEELEMENT_H

#include <fstream>

#include "baseelementcontainer.h"

template< class T, Types TYPE  >
class BaseElement {
public:
	VarInfo* info; // used to initialize some data in Containers...

	BaseElement()
	{
		assert( T::count < T::VAR_MAX ); // ensure static array is large enough

		if(!T::init){
			info = &T::vars[T::count++];
			info->type = TYPE;
		}
	}

	virtual void read(std::ifstream&){ }
	virtual void write(std::ofstream&){ }
};

#endif 


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
// base element container
#ifndef BASEELEMENTCONTAINER_H
#define BASEELEMENTCONTAINER_H

enum Types {
	TYPE_VALUE	= 1,
	TYPE_VEC3	= 2
};

struct VarInfo {
	Types type;
	const char* name;
};

template< class T > // templating here to have static members for each class
class BaseElementContainer {
public:
	const static int VAR_MAX = 512;

	static int count;
	static bool init;

	static VarInfo vars[ VAR_MAX ]; 
};

template< class T > int BaseElementContainer<T>::count = 0; 
template< class T > bool BaseElementContainer<T>::init = false;
template< class T > VarInfo BaseElementContainer<T>::vars[ BaseElementContainer<T>::VAR_MAX ]; 

#endif


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
// commoncontainer.h

#ifndef COMMONCONTAINER_H
#define COMMONCONTAINER_H

#include <cassert>

#include "elements.h"

template< class T >
class CommonContainer : public BaseElementContainer<T> {
public:
	static bool c_init;

	CommonContainer()
	{
		// make sure inheritence is following initialization
		assert(T::init == CommonContainer<T>::c_init); 
		
		if(!T::init){
			// initalize static information for variables
			position.info->name		= "Position";
			normal.info->name		= "Normal";
			binormal.info->name		= "Binormal";
			tangent.info->name		= "Tangent";

			CommonContainer<T>::c_init = true;
		}
	}

	Vec3Element< T > position, normal, binormal, tangent;
};

template< class T > bool CommonContainer<T>::c_init = false;


#endif


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
// elements.h

#ifndef ELEMENTS_H
#define ELEMENTS_H

#include <fstream>

#include "baseelement.h"

// example class
template< class T >
class ValueElement : public BaseElement<T, TYPE_VALUE /* defines the type */ > {
public:

};

template< class T >
class Vec3Element : public BaseElement<T, TYPE_VEC3 /* defines the type */ > {
public:
	float x, y, z;

	void read(std::ifstream input)
	{ input >> x >> y >> z; }

	void write(std::ofstream output)
	{ output << x << y << z; }
};



#endif 


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
// examplecontainer.h

#ifndef EXAMPLECONTAINER_H
#define EXAMPLECONTAINER_H

#include "commoncontainer.h"

class ExampleContainer : public CommonContainer<ExampleContainer> {
public:
	ExampleContainer()
	{
		if(!ExampleContainer::init)
		{
			a.info->name = "a";
			b.info->name = "b";
			c.info->name = "c";
			d.info->name = "d";
			e.info->name = "e";

			ExampleContainer::init = true; // after it's initialized make sure to terminate
		}
		
	} 

	ValueElement < ExampleContainer > a;
	Vec3Element  < ExampleContainer > b;
	Vec3Element  < ExampleContainer > c;
	ValueElement < ExampleContainer > d;
	ValueElement < ExampleContainer > e;
};

#endif 




thanks all of you as i was trying the same thing some few days before as was not able to do . ..thanks
That works fine, but if I am reading this right, every time you extend to a new class, you need to modify the BASE class(es) to support the new use of BaseElementContainer<T>::init. If I want
1
2
class MyNewContainer : public ExampleContainer
...

I need to go back and alter ExampleContainer (and perhaps its parent as well). This seems counterintuitive to C++ class inheritence.

ValueType seems to have no storage for the actual value associated with it. Say I want an integer value - do I need to create my own IntegerElement class? Would this need to be created for every type I want to include in a class? Also, one of the benefits of inheritance in C++ allows you to pass a Square * to a function expecting a Shape *. Using templates in your base class in the above way prevents you from doing that.

Can't lie - this seemed a very interesting question to me. I came up with another solution, which solves the 'init' issues, and even prevents multiple addition of variables for a single class, but I don't like using typeid().name() as a key. Also, recursive inheritance would need to be resolved.

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#include <iostream>
#include <typeinfo>
#include <map>
#include <list>

using namespace std;

// Type info
struct data_member
{
	data_member(string name, string type)
	{
		strVariableName = name;
		strTypeName = type;
	}
	string strVariableName;
	string strTypeName;
};

class ClassInfo
{
public:
	list<const char *> lstParents;
	list<data_member> lstDataMembers;	
};

map<const char *, ClassInfo> g_Types;

void _PrintDataMembers(const char * className)
{
	map<const char *, ClassInfo>::iterator itr = g_Types.find(className);
	if(itr != g_Types.end())
	{
		for (list<data_member>::iterator itr2 = itr->second.lstDataMembers.begin();  itr2 != itr->second.lstDataMembers.end(); itr2++)
		{
			cout << "(" << itr2->strTypeName.c_str() << ") " << itr2->strVariableName.c_str() << endl;
		}

		for (list<const char *>::iterator itr2 = itr->second.lstParents.begin();  itr2 != itr->second.lstParents.end(); itr2++)
		{
			_PrintDataMembers(*itr2);
		}
	}
}

template <class T>
void PrintDataMembers(T obj)
{
	_PrintDataMembers(typeid(T).name());
}

class DataMemberAggregator
{
public:
	DataMemberAggregator(const char * typeName, string strVariableName, string strTypeName)
	{
		g_Types[typeName].lstDataMembers.push_back(data_member(strVariableName, strTypeName));
	}
};

class ParentAggregator
{
public:
	ParentAggregator(const char * typeName, const char * parent)
	{
		g_Types[typeName].lstParents.push_back(parent);
	}
};

// My classes
class Parent
{
public:
	int a[3];

	static DataMemberAggregator dma1;
};

DataMemberAggregator Parent::dma1(typeid(Parent).name(), "a", "int[]");


class Child : public Parent
{
public:
	string strAddress;

	static DataMemberAggregator dma1;
	static ParentAggregator pa1;
};

DataMemberAggregator Child::dma1(typeid(Child).name(), "strAddress", "string");
ParentAggregator Child::pa1(typeid(Child).name(), typeid(Parent).name());

int main()
{
	Parent myParent = Parent();
	Child myChild = Child();

	cout << "Data members for myParent" << endl;
	PrintDataMembers(myParent);

	cout << endl;

	cout << "Data members for myChild" << endl;
	PrintDataMembers(myChild);

	int a;
	std::cin >> a;
	return 0;
}


closed account (o1vk4iN6)
Thanks for the reply, that is interesting, it takes away from all the over head for my BaseElement class. The one thing I was trying to avoid with my method was to have the order determined by the objects.

For your code if say I wanted to change the class I would than have to also edit the DataMemberAggregator order as well. That's what I was aiming for with this, there would be no need to have to edit multiple lines to ensure the order is the same. I've also been thinking of writing another program that would generate the order for "DataMemberAggregator" in a separate file. Doing something like that though would add more time to compiling and make it a bit more messy to setup the environment to compiling. I assume it'd be easy for an ide like Visual Studio/Eclipse. Not sure if the same could be said for a command-line compiler like g++. If it is generated in such a manor you could easily assign the key to be an integer or something similar, which would solve the problem of having the key as a string.

My ExampleContainer was not intended to be inherited, you can do something like my CommonContainer in my second post. Because of the recursive nature it wouldn't be possible to use a container and have it be inheritable unless it is not being inherited by another container.
Last edited on
Topic archived. No new replies allowed.