I have an array of enums:
typedef enum {
Item0,
Item1,
Item2
} ITEMS_TYPE;
ITEMS_TYPE MyItemsArray[] = {
Item0,
Item1,
Item2
};
bool findIndexOfItemInArray(int32_t valueToFind, int32_t* arrayToScan, int32_t arrayLastIndex, int32_t* indexOfTheSearchedValue)
{
int32_t index;
int32_t value;
for(index = 0; index < arrayLastIndex; index++)
{
//value = static_cast<int32_t>(arrayToScan[index]); //this when C++
value = (int32_t)(arrayToScan[index]); //this when C
if(value == valueToFind)
{
*indexOfTheSearchedValue = index;
return true;
}
}
return false;
}
int main(void)
{
int32_t valueToFind = 0;
int32_t itemIndex;
findIndexOfItemInArray((int32_t)valueToFind, (int32_t*)MyItemsArray, Item2, &itemIndex));
}
My problem is that
arrayToScan[index];
inside the findIndexOfItemInArray()
function is never the integer value of the given item but some weird, long number.
It all works when I compile it in C, but in C++ it doesn't. What am I missing?
Please read attentively all the valuable comments above.
Here is a possible C++ way to go.
#include <cstddef>
#include <limits>
enum class ITEMS_TYPE { Item0, Item1, Item2 };
ITEMS_TYPE MyItemsArray[] = {ITEMS_TYPE::Item0, ITEMS_TYPE::Item1,
ITEMS_TYPE::Item2};
// computing fixed-size plain array size at compile-time
template <typename T, std::size_t N>
constexpr std::size_t Size(T const (&)[N]) {
return N;
}
bool findIndexOfItemInArray(ITEMS_TYPE valueToFind, ITEMS_TYPE* arrayToScan,
std::size_t arrayLastIndex,
std::size_t* indexOfTheSearchedValue) {
std::size_t index;
ITEMS_TYPE value;
for (index = 0; index < arrayLastIndex; index++) {
value = arrayToScan[index];
if (value == valueToFind) {
*indexOfTheSearchedValue = index;
return true;
}
}
return false;
}
int main(void) {
ITEMS_TYPE valueToFind = ITEMS_TYPE::Item2;
std::size_t itemIndex =
static_cast<std::size_t>(std::numeric_limits<int>::max());
findIndexOfItemInArray(valueToFind, MyItemsArray, Size(MyItemsArray),
&itemIndex);
return static_cast<int>(itemIndex);
}
First of all, I would avoid to rely on the actual numerical value of an enum
item. It generally defeats the purpose of using an enum
all-together.
Then I recommend the usage of scoped enum
(enum class
) which avoids name collision.
Use std::size_t
for array index. In this code I added a possible function for automatically deducing the array size (Size
).
Initialize properly variables. Here I give a special value to index
(here, largest int
value). It is not mandatory if you test your function output but, at least, you'll have a well defined value if the search fails.
Set severe compilation options and take care about all diagnosis returned by the compiler.
I hope it will help.
NB many other thing can be said about enum
but I feel it is out of scope for your question.
Live with "severe" compilation options
[EDIT] for the record an even more C++ version:
#include <algorithm>
#include <array>
#include <cassert>
#include <cstddef>
#include <iterator>
#include <limits>
enum class ITEMS_TYPE { Item0, Item1, Item2 };
// ITEMS_TYPE MyItemsArray[] = {ITEMS_TYPE::Item0, ITEMS_TYPE::Item1,
// ITEMS_TYPE::Item2};
// C++14
std::array<ITEMS_TYPE, 3> MyItemsArray = {ITEMS_TYPE::Item0, ITEMS_TYPE::Item1,
ITEMS_TYPE::Item2};
// C++17
// std::array MyItemsArray = {ITEMS_TYPE::Item0, ITEMS_TYPE::Item1,
// ITEMS_TYPE::Item2};
int main(void) {
ITEMS_TYPE valueToFind = ITEMS_TYPE::Item2;
std::size_t itemIndex =
static_cast<std::size_t>(std::numeric_limits<int>::max());
auto first = std::cbegin(MyItemsArray);
auto last = std::cend(MyItemsArray);
auto res = std::find(first, last, valueToFind);
if (res != last) {
itemIndex = static_cast<std::size_t>(std::distance(first, res));
}
return static_cast<int>(itemIndex);
}
More modern C++ could be used also (span
or range
) but I'm not familiar enough with them.
[EDIT] critics on OP code
Above explanations are telling what should be done, not clearly why OP code was also wrong on low-level.
I wil repeat this answer, it cannot be assumed that the underlying type is uint32_t
:
From c++17 std draft [dcl.enum]
10.2.7.
For an enumeration whose underlying type is not fixed, the underlying type is an integral type that can represent all the enumerator values defined in the enumeration. If no integral type can represent all the enumerator values, the enumeration is ill-formed. It is implementation-defined which integral type is used as the underlying type except that the underlying type shall not be larger than int unless the value of an enumerator cannot fit in an int or unsigned int. If the enumerator-list is empty, the underlying type is as if the enumeration had a single enumerator with value 0
Thus the cast of then array of enums to uint32_t *
is undefined behavior.
Instead, reinterpret_cast<std::underlying_type_t<ITEMS_TYPE>*>(MyItemsArray)
could be used but, as said and explained at the beginning of this answer, I would consider such an usage as a bad practice.