Click here to Skip to main content
15,890,438 members
Please Sign up or sign in to vote.
4.00/5 (1 vote)
See more:
I need a function to process a two-dimensional char array of variable sizes. Below is one of my attempts:
C++
int GetCodes( char **codes, size_t N );
template<size_t N>
inline int GetCodes( char (*codes)[N] ){ return GetCodes( codes, N ); };

The compiler (VC++ 2008) complains that it "cannot convert parameter 1 from 'char (*)[8]' to 'char **'." So I changed the code to:
C++
int GetCodes( char **codes, size_t N );
template<size_t N>
inline int GetCodes( char (*codes)[N] ){ return GetCodes( (char**)codes, N ); };

That would compile and link. However when it is run I would get a bad pointer error and the program would crash. How can I fix the problem? Is there a way to achieve this without using templates?

--------------------------------------
Thanks so much for all the suggestions, but I would like to add a few clarifications. First of all my problem is with multi-dimensional (two in this case) arrays. I have used references for one-dimensional arrays as Phillipe suggested without any problem. I just cannot extend that to two-dimensions. I did try using references such as,
C++
template<size_t N1, size_t N2>>
inline int GetCodes( char (&codes)[N1][N2] ){...};

But it has the same problem. Besides, I don't need the first dimension. Below is a more complete code to show the intended use.
C++
int GetCodes( char **codes, const size_t N );
template<size_t N>
inline int GetCodes( char (*codes)[N] ){ return GetCodes( codes, N ); };

int GetCodes( char **codes, const size_t N )
{
	**codes = 'A';
	return 1;
}

int main()
{
	char codes[100][8];
	GetCodes( codes );
	return 0;
}

Please note that the compiler does not have problem resolving the function call to the template function. The "cannot convert char (*)[8] to char **" error occurs when the template function tries to call the two-parameter form of the function. Phillipe's suggestion of using "char *(*codes)[N]" as the argument of the template function would not work.

As Phillipie said, I'd like to use template functions in this case. If that's not possible I will use vectors as Barneyman suggested.
Posted
Updated 20-Sep-11 3:28am
v4
Comments
Philippe Mori 19-Sep-11 20:12pm    
The need for a cast indicate that the type does not match. And since the cast is incorrect, then it crash at execution.

Assuming that you want to call the function with data that look like that:

C++
char* aa[] = { "AA", "BB", "CC", "DD" };
GetCodes(aa);


And you want N to be automatically determined by the compiler, then you need to have a reference in the declaration and fix the parenthesis.

C++
template<size_t N>
inline int GetCodes( char *(&codes)[N] ){ return GetCodes( codes, N ); };


The reference is required to avoid the automatic convertion from array to pointers that would otherwise occurs.

As an example:

C++
template <typename T>
void Test1(T t)
{
    std::cout << "Test 1: " << typeid(t).name() << std::endl;
}

template <typename T>
void Test2(T &t)
{
    std::cout << "Test 2: " << typeid(t).name() << std::endl;
}

int main()
{
    int data[] = { 1, 2, 3, 4 };
    Test1(data);
    Test2(data);

    return 0;
}


This will give the following input:

Test 1: int *
Test 2: int [4]
 
Share this answer
 
Comments
Philippe Mori 19-Sep-11 20:19pm    
If you prefers pointers to references, then you can do the following modifications:

GetCodes(&aa); // Take the address of the array

And modify (or add an overload) for that case:

template<size_t N>
inline int GetCodes( char *(*codes)[N] ){ return GetCodes( *codes, N ); };


Notice the extra * when calling the 2 arguments overload from the template function.
h.lee 20-Sep-11 9:31am    
Thanks for your input but the suggestion does not work - please see my expanded question above.
Philippe Mori 20-Sep-11 10:01am    
Only the last dimesion can be converted from an array to a pointer.

If you really want, then you could predent that you have an array of size N1 * N2 (single dimension) but you would need to do a reinterpret_cast as there are no direct conversion between a 2 dimension array and a one dimension array.
I'd be more inclined to just use an STL container - vector or similar
 
Share this answer
 
Comments
Philippe Mori 19-Sep-11 20:25pm    
Effectively, most of the time, STL containers would be a better choice.

But the template trick is sometime useful when we have statically defined arrays.
Stefan_Lang 20-Sep-11 4:52am    
Isn't there also a fixed size array in the STL? carray or something? Never used it myself...
Philippe Mori 20-Sep-11 10:06am    
If not in STL, then you can look at boost. Maybe boost.array might be what you need...
To complement my initial solution and as a response for other solutions and comment, I have provided a solution that reinterpret a two dimensional array as a single dimensional array.

This code is mainly for educational purpose. For real life program, using STL or boost would generally be recommanded.

Well in that solution, I have added code that will set each item to it global index modulo (the index if we would pretend the array to be single dimension).

I have also provides alternative using references or pointers to show the differences.

C++
//void GetCodes(char *data, int count) // would works the same as next line
void GetCodes(char data[], int count)
{
    for (int i = 0; i != count; ++i)
    {
        data[i] = static_cast<char>(i %256);
    }
}

template<size_t M, size_t N>
void GetCodes(char (&data)[M][N])
{
    // Any of these produce the same result.
    GetCodes(reinterpret_cast<char(&)[M * N]>(data), M * N);
    //GetCodes(*reinterpret_cast<char(*)[M * N]>(data), M * N);
}

template<size_t M, size_t N>
void GetCodes(char (*data)[M][N])
{
    // Any of these produce the same result.
    GetCodes(reinterpret_cast<char(&)[M * N]>(*data), M * N);
    //GetCodes(*reinterpret_cast<char(*)[M * N]>(data), M * N);
    //GetCodes(*reinterpret_cast<char(*)[M * N]>(*data), M * N);
}

int main()
{
    char array1[100][8] = { 0 };
    char array2[100][8] = { 0 };

    GetCodes(array1);   // Calls first template
    GetCodes(&array2);  // Calls second template

    return 0;
}
 
Share this answer
 
Comments
Philippe Mori 20-Sep-11 20:13pm    
In the above sample, array[0][0] would be set to 0, array[0][1] to 1, array[1][0] to 8...

That is array[x][y] would be set to (x * 8 + y) % 256.
Philippe Mori 20-Sep-11 20:23pm    
Even simpler.

In the first template function, we could simply write:
GetCodes(&data[0][0], M * N);

Or in the second function:
GetCodes(data[0][0], M * N);
h.lee 21-Sep-11 12:44pm    
Very good suggestions. Thanks.
There is a little trick to do that (but NEVER WITH POINTERS):
C++
#pragma once
#include <stdio.h>
#include <tchar.h>

template <class TSET>
int GetCodes( TSET codes[],const TSET set )
{
  size_t  i;
  for(i=0;i<(sizeof(codes)/sizeof(codes[0]));i++)
  {
    codes[i] = set;
  }
  return 1;
}

template <class TARR,class TSET>
int GetCodes( TARR& codes, const TSET set )
{
  size_t  i;
  int      dim = 0;
  for(i=0;i<(sizeof(codes)/sizeof(codes[0]));i++)
  {
    dim = GetCodes(codes[i],set);
  }
  return 1+dim;
}

int _tmain(int argc, _TCHAR* argv[])
{
  char    array1[8][4];
  char    array2[8][4][33];
  char    array3[11];
  char    array4[1][2][3][4][5][6];
  int      dim;

  dim = GetCodes(array1,(char)42);
  _tprintf(__T("dim: %i\r\n"),dim);
  dim = GetCodes(array2,(char)42);
  _tprintf(__T("dim: %i\r\n"),dim);
  dim = GetCodes(array3,(char)42);
  _tprintf(__T("dim: %i\r\n"),dim);
  dim = GetCodes(array4,(char)42);
  _tprintf(__T("dim: %i\r\n"),dim);
  
  #if(0)
  char*    array_fail[2];
  GetCodes(array_fail,(char)42); // that will crash
  #endif
  
  _gettch();
  return 0;
}

Result:
<br />
dim: 2<br />
dim: 3<br />
dim: 1<br />
dim: 6<br />

Good luck.
 
Share this answer
 
Comments
h.lee 20-Sep-11 14:19pm    
Wow, that's some trick! It would be perfect if there's a way to rid of the second argument.
mbue 20-Sep-11 16:11pm    
Im afraid no. Cause the first template function marks the inner loop. And the inner loop must match the array type and the set type.
Regards.
Philippe Mori 20-Sep-11 19:23pm    
That code is bugged... Since the first function GetCodes received and array of unknown size, it is treated as a pointer by the compiler.

Thus, sizeof(codes)/sizeof(codes[0]) would always give 4 (assuming 32 bits pointers and char data) since it divide the size of a pointer by the size of a char.

If you would inspect array2 with a debugger, then you would see that only the first 4 characters are set to (char)42.

Thus in the end to correctly implement you solution, you would need to modify the declaration to template <class TSET, int N> int GetCodes( TSET (&codes)[N],const TSET set ) and use N for the final dimension.
mbue 20-Sep-11 23:13pm    
Oh yes indeed - we need some additional functions:


template <class TSET>
int GetCodes( TSET& codes,const TSET set )
{
return 0;
}

template <class TSET>
int GetCodes0( TSET& codes,const TSET set )
{
_tprintf(__T(" set\r\n"));
codes = set;
return 0;
}

template <class TARR,class TSET>
int GetCodes0( TARR& codes,const TSET set )
{
return 0;
}

template <class TARR,class TSET>
int GetCodes( TARR& codes, const TSET set )
{
size_t i;
int dim = 0;
_tprintf(__T(" elem: %i\r\n"),(int)(sizeof(codes)/sizeof(codes[0])));
for(i=0;i<(sizeof(codes)/sizeof(codes[0]));i++)
{
dim = GetCodes(codes[i],set);
if(0==dim) GetCodes0(codes[i],set);
}
return 1+dim;
}


Thanks for your help.
Problem solved. As Phillipie suggested I should have pretended the array to be one-dimensional with my second option:
C++
int GetCodes( char *codes, size_t N );
template<size_t N>
inline int GetCodes( char (*codes)[N] ){ return GetCodes( (char*)codes, N ); };

Thank you all!
 
Share this answer
 
Comments
Philippe Mori 20-Sep-11 19:34pm    
I think you have not exactly understand me. Why do you have a cast? And how have you modified your char codes[100][8]; declaration.

The trick I was talking about was that the template would have received both dimension (100 and 8) and would then have pretended an array of 800 items...

I have provide another solution that shows what I mean.
h.lee 21-Sep-11 12:42pm    
No, I did not modify the char codes[100][8]; declaration. As mentioned in my original post, without the (char *) cast I would get the "cannot convert parameter 1 from 'char (*)[8]' to 'char **'." error.

Also I did not use two parameters in my template since I do not need the first dimension. However, I will switch to your other solution to check the bounds.

Thanks.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900