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?
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; }
virtualvoid 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:
staticint count;
staticbool 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.
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.
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:
// base element container
#ifndef BASEELEMENTCONTAINER_H
#define BASEELEMENTCONTAINER_H
enum Types {
TYPE_VALUE = 1,
TYPE_VEC3 = 2
};
struct VarInfo {
Types type;
constchar* name;
};
template< class T > // templating here to have static members for each class
class BaseElementContainer {
public:
conststaticint VAR_MAX = 512;
staticint count;
staticbool 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
// 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
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.
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.