Hi. I apologize if this is in the wrong place - I'm not entirely a "Beginner" in C++, but at the same time this post deals almost entirely with aspects of it that I'm very unfamiliar with. If this should be in the Beginner section, let me know, or just move it, or whatever's appropriate.
Anyway, I have a series of very similar functions that each go through an array (actually, a custom array-like class; I didn't write it and can't change it, though - see below for some details if you feel it's necessary), and, after checking various aspects of the array to make sure it fits the criteria I need, does various things to the elements of the array, and depending on the exact function in question, may return values of differing types.
I'd like to re-use this code as most of it is the same, but the fact that different things happen in the middle of the loop, and the fact that each return different types of values, is causing issues with this. I can't just make a utility function of it because things need to occur in the middle of it that vary, and even if I passed a function as an argument to the utility function to use in the middle of the loop, that singular function wouldn't be able to return every data type it would need to.
Instead of return values, I could pass pointers or references to the various possible return values, but this is awkward because every call of the function would need to supply all of the types. I could (I think?) use a void* argument instead, but I'm a bit shaky on the best way to do that.
I also tried to use #define to create a macro to do it, but that didn't seem to work (I honestly know next to nothing about preprocessor commands, including #define, so I may have easily done this incorrectly).
At this point, I have two possibilities that I'm not quite sure how to use properly (the void* argument or the #define macro), and both seem rather awkward, so I was wondering what the sort of canonical "best practice" here would be. I'm largely self-taught, so a lot of the background knowledge here is lacking.
Just in case it matters, some info:
I'm using Visual C++ 2005 Express Edition (yes, I know it's wildly out-of-date, but the project I'm updating requires it).
The array itself, as I mentioned, is not a standard C array or STD vector, and the process of getting at data is a little more complicated (most of the element access functions are private, so you have to request values using a friend class that maintains the arrays), which is all the more reason why I want to re-use the code - it's not entirely trivial stuff to go through the array. For these purposes, what we're talking about is most similar to a double** that has the height and width packaged with it - it is not nearly as robust as vector, but important for the things it needs to do unrelated to these functions.
I also do not want to copy the array over to another data type (like a more convenient vector), because it has to end up in this custom array type, and a certain amount of performance optimization is necessary - I'd really like to avoid going through the array more than once.
Would creating iterators be out of the question? You would then be able to use STL algorithms to apply the desired transformations to the elements. To make a single pass, you might be able to create a function object that does all of the transformations to a single element and then use std::for_each or std::transform...
// Converts custom array class to std::vector
// ArrayID refers to the custom array class
// vecOut is the vector
// h & w are height and width of the 2d array
// returns true when successful, false otherwise.
bool AsMatrix(ArrayID id, vector< vector <double> > &vecOut, UInt32& h, UInt32& w)
{
double x;
vecOut.clear(); // overwriting anything already in vecOut
ArrayVar* arr = g_ArrayMap.Get(id); // getting the actual array from the id
if (!arr || ( g_ArrayMap.GetTypeString(id).compare("Array") != 0 ))
// this makes sure it's an "Array", as opposed to a "Map" or "StringMap"
returnfalse;
h = g_ArrayMap.SizeOf(id); // can't be empty
if ( h <= 0 )
returnfalse;
ArrayElement ele;
ArrayKey key;
g_ArrayMap.GetFirstElement(id, &ele, &key);
// one dimensional arrays
if ( !(ele.GetAsArray(&id)) ) { // basically, if the first element is not an array itself
std::vector<const ArrayElement*> vecO;
if ( !(g_ArrayMap.AsVector(id,vecO)) ) // converts custom-array to std::vector
returnfalse;
h = 1;
w = vecO.size();
vecOut.push_back(vector<double>()); // only one row
for ( UInt32 i = 0; i < w; ++i ) {
if ( vecO[i]->GetAsNumber(&x) ) // add each number
vecOut[0].push_back(x);
elsereturnfalse; // fails if there are any non-numbers
}
}
// two dimensional arrays
else {
ArrayVar* row = g_ArrayMap.Get(id);
if (!row || ( g_ArrayMap.GetTypeString(id).compare("Array") != 0 ))
// each sub-array also needs to be an Array, not a Map or StringMap
returnfalse;
w = g_ArrayMap.SizeOf(id);
if ( w <= 0 )
returnfalse;
for (UInt32 i = 0; i < h; ++i) // for each row
{
if ( !(g_ArrayMap.GetElementArray(id,i,&id)) )
returnfalse;
row = g_ArrayMap.Get(id);
if ( !row || ( g_ArrayMap.GetTypeString(id).compare("Array") != 0 ) || g_ArrayMap.SizeOf(id) != w )
returnfalse;
vecOut.push_back(std::vector<double>()); // create a row in the vector
for (UInt32 j = 0; j < w; ++j)
if ( g_ArrayMap.GetElementNumber(id,i,&x) )
vecOut[i].push_back(x); // add each element to vector
elsereturnfalse; // fails on a non-number
}
}
returntrue;
}
The use of "vecOut", and the places where numbers are added to it, are not going to be constant, those will change from function to function.