Link to home
Start Free TrialLog in
Avatar of justinvoels
justinvoelsFlag for United States of America

asked on

Using enumeration type as an array index in C++

In C++ I need to use the enumeration type as an array index. I have a class that allows any integer rang (ex. -100 - 100) or any single character range  (ex. 'A' - 'Z'). I have to continue to extend it so you can use enumeration type as the index, for example when declaring the array "SafeArray array1(mon,fri);" you would use mon,tue,wed,fri as indexes. The problem is the class must perform independently of the program, so the user woudl declare any enum such as "enum blahblah {ted, donkey, banana, pen};" and then create the array saying something like "SafeArray whatever(donkey,pen)".

The problem I've run into, is passing the type (which happens to be ANY type the user want, created from enum) to the constructor (there is a parameter constructor for int, char as well). The compiler complains about not know the type, when obviously it can't, all I can assume in the class is that it will be recieving enum type, which isn't the actual type. If that makes any sense.

A better example hopefully is this:
int main()
{
   enum whateverIWant {whatever, I, Wanted};
   SafeArray safeArrayOne(whatever,Wanted);

   safeArrayOne[I] = 5;         //SafeArray is an array of int with whatever indexes th euser wants
}

etc.

Any ideas as to how I can create the constructor and operator[] overload for a type that will be made up by user at his or her whim? I cannot include a header or any special declaration outside of the SafeArray header and implementation.
ASKER CERTIFIED SOLUTION
Avatar of evilrix
evilrix
Flag of United Kingdom of Great Britain and Northern Ireland image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Example.
#include <memory> 
#include <stdexcept>
 
class SafeArray
{
public:
	template <typename T>
	SafeArray(T const & t) : m_nIndex(static_cast<size_t>(t))
	{
	}
 
	int m_nIndex;
};
 
enum{ FOO, BAR };
 
int main()
{
	SafeArray sa1(FOO);
	SafeArray sa2(BAR);
	return 0;
}

Open in new window

Oops. m_nIndex should have been a size_t type: -
#include <memory> 
#include <stdexcept>
 
class SafeArray
{
public:
	template <typename T>
	SafeArray(T const & t) : m_nIndex(static_cast<size_t>(t))
	{
	}
 
	size_t m_nIndex;
};
 
enum{ FOO, BAR };
 
int main()
{
	SafeArray sa1(FOO);
	SafeArray sa2(BAR);
	return 0;
}

Open in new window

Hi justinvoels,

You can overload all of the methods, including the constructor.  But if you're passing an integer, or a char 'A', the compiler automatically converts the 'A' to an integer.  There is no difference to the called function so it's impossible to know what the source code looked like.

A decent example is passing 'A' to a library routine with a parameter type of integer.  The compiler MUST generate a type that is compatible with the called routine.  A char is really an 8-bit integer, so converting it to int is trivial and the compiler does that.

But knowing that the original source contained an 'A' is just not possible.



Good Luck,
Kent
I've now added an operator[] and made the SafeArray templated to make it closer to what I think you are doing.

-Rx.
#include <memory> 
#include <stdexcept>
 
template <typename U, size_t SIZE>
class SafeArray
{
public:
	template <typename T>
	SafeArray(T const & t) : m_nIndex(static_cast<size_t>(t))
	{
	}
 
	template <typename T>
	U operator[](T const & t)
	{
		return m_array[static_cast<size_t>(t)];
	}
 
 
 
	size_t m_nIndex;
	U m_array[SIZE];
};
 
enum{ FOO, BAR };
 
int main()
{
	SafeArray<int, 10> sa1(FOO);
	SafeArray<char, 5> sa2(BAR);
	return 0;
}

Open in new window

Although, to be honest, if you are just talking about passing enum values they should automatically convert to int anyway. The advantage of using templates is that you can use types that wouldn't normally automatically convert to a size_t type. The disadvantage is you lose some of the type checking the compiler will do for you. It really depends just how flexible you want your class to be -- the more flexible you are the more chance there is you give you classes user the power to do something really dumb :)
Also, the other useful thing about doing it using templates is that you give the user the power to specialize the member templates so they can support their own composite/complex types without the need to modify your code.
Avatar of justinvoels

ASKER

Thanks all,

evilrix,
Your example was very close to what I am trying to do, got me started (maybe a better way is excepted) in the right direction. For these kind of assignments I was trying to avoid templates and anything that would make ti any more complex/ambiguous then it had to be (but I'm out of time). ex. I used my own code for int and char indexes instead of the stdlib <map> class. I'll see how this turns out in a few minutes. I also will admit oversight on various things the past few weeks getting back into C++ because I've spent too much time in between on digital design and low level programming.
Hi Justin.

No worries. Obviously my example (rushed cos I'm about to go home from work -- hence typos) was done without any knowledge of your code so it's not ment to be representative of what you're actually doing, rather just to give you an idea of how to use the template. I'll check how you're getting on when I get home (about an hour).

If you get stuck give me more of an idea of what you're trying to do and I'll be able to give it more thought at home.

Good luck.

-Rx.
I got it worked out using templates in no time (documentation is another story, bah). Thanks for the tip Kdo, but I had already had that part done.

evilrix,
Thanks again for showing me how templates could be used to solve the problem quite easily.

I've attach my header file for a bit more information on what was being done for anyone who has a similar problem.

safearray.txt
Hi, I gave this some thought on the way home (sorry, sometimes at work it is not always possible to give full and considered answers) and I think what you are after is something (I hope) like the class I've written below. I hope this might serve as a useful starting point (???) for you.

Kind regards,

-Rx.

NB. I've only done limited testing on this code, you should ensure it is thoroughly tested before you run it in a production environment.
#include <stdexcept>
#include <vector>
 
//////////////////////////////////////////////////////////////////////////////
// Your class
template <typename TYPE, typename IDX>
class SafeArray
{
public:
	SafeArray(IDX const & min, IDX const & max) : m_min(min), m_max(max)
	{
		m_array.resize(max + 1);
	}
 
	TYPE & operator[](IDX const & idx)
	{
		if((idx < m_min) || (idx > m_max)) { throw std::runtime_error("Invalid index"); }
		return m_array[idx];
	}
 
	TYPE const & operator[](IDX const & idx) const
	{
		if((idx < m_min) || (idx > m_max)) { throw std::runtime_error("Invalid index"); }
		return m_array[idx];
	}
 
private:
	IDX m_min;
	IDX m_max;
 
	typedef std::vector<TYPE> array_t;
	array_t m_array;
};
 
//////////////////////////////////////////////////////////////////////////////
// THE REST OF THIS CODE IS USER DEFINED
//////////////////////////////////////////////////////////////////////////////
 
// This is a user defined integer type
struct MyInt
{
	MyInt(int n_ = 0):n(n_){}
	int n;
};
 
// Just an arbitrary type to be used in SafeArray (this can be anything)
struct MyType
{
	MyType(int n_ = 0):n(n_){}
	int n;
};
 
// ====================================================================================
// *** These specializations are "injected" into the scope of your class (neat eh?) ***
// ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
 
// User defined specialization for contructor (not part of your code!
template <>
SafeArray<MyType, MyInt>::SafeArray(MyInt const & min, MyInt const & max) : m_min(min), m_max(max)
{
	m_array.resize(max.n + 1);
}
 
// User defined specialization for non-const indexer (not part of your code!
template <>
MyType & SafeArray<MyType, MyInt>::operator[](MyInt const & idx)
{
	if((idx.n < m_min.n) || (idx.n > m_max.n)) { throw std::runtime_error("Invalid index"); }
	return m_array[idx.n];
}
 
// User defined specialization for const indexer (not part of your code!
template <>
MyType const & SafeArray<MyType, MyInt>::operator[](MyInt const & idx) const
{
	if((idx.n < m_min.n) || (idx.n > m_max.n)) { throw std::runtime_error("Invalid index"); }
	return m_array[idx.n];
}
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
 
//////////////////////////////////////////////////////////////////////////////
// Test code
int main()
{
	MyInt n0(0);
	MyInt n1(1);
	MyInt n2(2);
	SafeArray<MyType, MyInt> sa1(n0, n2);
	sa1[n1] = MyType(5);
 
	enum DAYS { SUN, MON, TUE, WED, THU, FRI, SAT };
	SafeArray<MyType, DAYS> sa2(SUN, SAT);
	sa2[WED] = MyType(5);
 
	return 0;
}

Open in new window

Fully templated is exactly how I would have done it had I not been done with 2/3 of it without. But that is the way it goes, there's always the other things to remember later. But hey, guess what my next assignment happened to be, hehe, it is templating the class and overloading every operator applicable. Review is good, should be easy.

As of my last post I was done with and tested through what was needed that time (I really don't like posting entire implementation if not necessary). That example is the perfect starting point for what I have to do next.
>> That example is the perfect starting point for what I have to do next
I'm glad I was eventually able to provide you with something useful.

Good luck my friend.

-Rx.