[Webinar] Streamline your web hosting managementRegister Today

x
?
Solved

Using enumeration type as an array index in C++

Posted on 2008-01-31
13
Medium Priority
?
3,068 Views
Last Modified: 2011-10-19
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.
0
Comment
Question by:justinvoels
  • 9
  • 3
13 Comments
 
LVL 40

Accepted Solution

by:
evilrix earned 2000 total points
ID: 20787422
You can do this using a templated contructor.
0
 
LVL 40

Expert Comment

by:evilrix
ID: 20787451
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

0
 
LVL 40

Expert Comment

by:evilrix
ID: 20787462
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

0
Never miss a deadline with monday.com

The revolutionary project management tool is here!   Plan visually with a single glance and make sure your projects get done.

 
LVL 46

Expert Comment

by:Kent Olsen
ID: 20787484
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
0
 
LVL 40

Expert Comment

by:evilrix
ID: 20787523
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

0
 
LVL 40

Expert Comment

by:evilrix
ID: 20787581
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 :)
0
 
LVL 40

Expert Comment

by:evilrix
ID: 20787618
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.
0
 

Author Comment

by:justinvoels
ID: 20787752
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.
0
 
LVL 40

Expert Comment

by:evilrix
ID: 20787799
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.
0
 

Author Comment

by:justinvoels
ID: 20788940
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
0
 
LVL 40

Expert Comment

by:evilrix
ID: 20789390
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

0
 

Author Comment

by:justinvoels
ID: 20791977
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.
0
 
LVL 40

Expert Comment

by:evilrix
ID: 20792607
>> 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.
0

Featured Post

The new generation of project management tools

With monday.com’s project management tool, you can see what everyone on your team is working in a single glance. Its intuitive dashboards are customizable, so you can create systems that work for you.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

IntroductionThis article is the second in a three part article series on the Visual Studio 2008 Debugger.  It provides tips in setting and using breakpoints. If not familiar with this debugger, you can find a basic introduction in the EE article loc…
Article by: evilrix
Looking for a way to avoid searching through large data sets for data that doesn't exist? A Bloom Filter might be what you need. This data structure is a probabilistic filter that allows you to avoid unnecessary searches when you know the data defin…
The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.
The viewer will learn additional member functions of the vector class. Specifically, the capacity and swap member functions will be introduced.

591 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question