Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cast between const char* [][3] and std::array< const char*, 3 >*

I need a way to cast between these two types of variables:

std::array< const char*, 3 >* foo;
const char* foo[][3];

Because I need to be able to pass both types to a function. The function can be defined either of these ways, whichever makes conversion easier:

void bar( std::array< const char*, 3 >* param );
void bar( const char* param[][3] );

In this question, Jarod42 suggests using the method here. Is there a cleaner way to do this?

Edit in response to dyp's link

This reinterpret_cast did work for me, but Ivan Shcherbakov describes it as an "ugly dirty hack" I've pasted the code, below... I don't understand why this is a hack, or why something could go wrong with it? Is the template method suggested by Nathan Monteleone necessarily better than this?

void bar( std::array< const char*, 3 >* param ){}

void main( void )
{
    static const char* cStyle[][3] = { NULL };
    std::array< const char*, 3 > foo = { NULL };
    std::array< const char*, 3 >* stlStyle = &foo;

    bar( reinterpret_cast< std::array< const char*, 3 >* >( cStyle ) );
    bar( stlStyle );
}
like image 270
Jonathan Mee Avatar asked Mar 26 '14 19:03

Jonathan Mee


2 Answers

Off the top of my head, the easiest most elegant thing you could do is just make your bar function a template.

template <class T> void Tbar( T param ) {
    char c12 = param[1][2];    // (for example)
}

Of course at that point you lose the ability to enforce that it be of size [3]. So you could do something like hide the template implementation down in a source file, expose both of your original functions as prototypes in a header, and in the cpp file just implement as

void bar( std::array< const char*, 3 >* param ) { Tbar(param); }
void bar( const char* param[][3] ) { Tbar(param); }

The advantage of this sort of approach is that it avoids casting entirely. The disadvantage, I suppose, is that you're a little bit more limited in what you can do inside Tbar, namely you're restricted to whatever operations both char*[][3] and array<char*, 3>* have in common. It would also be harder to store off a pointer to the array if, for example, Tbar was a setter in a non-template class.

like image 127
Nathan Monteleone Avatar answered Nov 11 '22 04:11

Nathan Monteleone


Based on Nathan Monteleone's solution:

template<typename T>
enable_if_t<conditional_t<is_array_v<T>, extent<T>, tuple_size<T>>::value == 3U> bar(T* param){}

I believe this solution is the best of all worlds, cause it avoids the reinterpret_cast that is implementation dependent. And it enforces that param must be of size 3 at compile time.

Note that the conditional_t's value is called only after a type is selected. For more on this see: Short Circuiting Operators in an enable_if

Live Example

like image 2
Jonathan Mee Avatar answered Nov 11 '22 02:11

Jonathan Mee