6

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 );
}
19
  • May we know what are you trying to store in foo? Commented Mar 26, 2014 at 19:53
  • I'm pretty sure this is a duplicate.. std::array's size and alignment are not specified AFAIK, therefore a std::array<X,Y>* is not compatible to a X (*arr)[Y] Commented Mar 26, 2014 at 19:59
  • 2
    Related: Treat C cstyle array as std::array Commented Mar 26, 2014 at 20:02
  • You say that the function can be defined either of those ways, but must it be defined in one of those ways? With template<class I> void bar(I start, I end), you can pass iterators to either of those containers or indeed any standard container or c-style array or just part of a container. Commented Mar 26, 2014 at 20:06
  • dyp, I think the point is that the alignment isn't specified, and yes, both versions of foo should be exactly compatible. black, This variable is going to contain 3 dynamically allocated arrays of c-strings. Commented Mar 27, 2014 at 12:24

2 Answers 2

3

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.

Sign up to request clarification or add additional context in comments.

3 Comments

I think I could do a static_assert with extents to ensure that the size was 3, right? Then I should be able to proceed with uniform instantiation.
@JonathanMee You might be able to. I actually have never used std::extent before, so would like to know if you have any success with that.
Nathan Monteleone Well I had less success than I would have hoped for. I'm having trouble using std::extent on both an std::array and a C-style array. I'd like to make this work though cause according to Ivan Shcherbakov, I'm a bad person if I don't. To that end I've created a new question: stackoverflow.com/questions/22712965/…
2

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.