1
string** flowFile() {
    string line;
    string word[8];
    int i=0;
    static string flow[23][2];
    ifstream myfile ("test.txt");
    if (myfile.is_open())
    {
       while ( getline (myfile,line) )
        {
             strSplit(line,word);
             flow[i][0]=word[1];
             flow[i++][1]=word[2];
        }
        myfile.close();
   }
   else cout << "Unable to open file"; 
   return flow;
 }

 int main()
  {
     string **fl=flowFile();
  }

I'm getting this error:

error: cannot convert ‘std::string (*)[2] {aka std::basic_string<char> (*)[2]}’
                to    ‘std::string** {aka std::basic_string<char>**}’
       in return

What is wrong with my code?

Mat
  • 202,337
  • 40
  • 393
  • 406
Amar Agrawal
  • 141
  • 13

4 Answers4

3

string flow[23][2] and string ** are two different incompatible types. One cannot convert to another implicitly. Thats all. The solution is to make them compatible, by making the later string [23][2], return reference and accept reference, but that would still be a bad solution, because you're still working with raw arrays.

A good solution is to use std::vector and std::string. Maybe, you need std::pair also, or std::array.

Here is one possible solution:

#include <vector>
#include <array>
#include <string>

//C++11 style typedef
using flow_data_t = std::vector<std::array<std::string,2>>; 

//reimplementation of your function
flow_data_t flowFile() 
{
    std::string line;
    std::string word[8];
    int i=0;
    flow_data_t flow;
    std::ifstream myfile ("test.txt");
    if ( !myfile )  
       cout << "Unable to open file"; 
    while ( std::getline (myfile,line) )
    {
       strSplit(line,word);
       flow.push_back({word[0], word[1]});
    }
    return flow;
}

int main()
{
  flow_data_t data=flowFile();

  for(auto const & row : data)
      for(auto const & col : row)
            //work!
}

Hope that helps.

Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • `by making the later string (*)[2]` How do you do this? – SwiftMango Apr 21 '14 at 05:43
  • @texasbruce: By writing that as such : `string (*fl)[2]=flowFile();` and likewise, change the return type of the function also. – Nawaz Apr 21 '14 at 05:44
  • @Nawaz Did you try the function return type to `string(*)[2]`? Does it work for you? – SwiftMango Apr 21 '14 at 05:45
  • @texasbruce: Please try that. Use typedef if it makes your life hell (because I know the syntax would look like HELL). – Nawaz Apr 21 '14 at 05:46
  • If the OP wanted a 2D array, would't a vector> or map,string> be more suitable? – Vadim Apr 21 '14 at 05:46
  • @Nawaz I am asking this because it does not work. You cannot return an array type, and yes I tried that. http://coliru.stacked-crooked.com/a/69be21be4787770c – SwiftMango Apr 21 '14 at 05:48
  • @texasbruce: Oh yes... it is bigger hell than I initially thought. You've to return *reference* to the array, and accept as such (i.e as reference). even the declaration should be changed. HELL! – Nawaz Apr 21 '14 at 05:52
2

You cannot return array from a function even though you can return a pointer and let your array decay to a pointer: Array Decay

However 2D array can decay to neither T* nor T** because of the memory layout of the array is different from "2D pointer array" (it is actually more like flattened), and you cannot return array from function. However in C++ you can return array reference Full Code:

//This does not work
//typedef string * string2d[2];
//typedef string *(&string2d)[2];

typedef string (&string2d)[23][2];

string2d flowFile() {
    static string flow[23][2];
   return flow;
}

Array reference would even preserve the information of how big each row and columns are and no array decaying happen.

Of course, a more suggested "C++ way" to do this is using std::vector (as always).

Community
  • 1
  • 1
SwiftMango
  • 15,092
  • 13
  • 71
  • 136
1
  1. In C++, arrays have type std::vector. You should use these, not low-level builtin arrays declared with [].
  2. In C++, string [23] is sometimes interchangeable with string*, but string[23][2] is never interchangeable with string**. That's one reason you should not use builtin arrays.
  3. In C++, you cannot return a local builtin array variable. It will compile but then your program will probably crash. This is another reason you should not use builtin arrays. (Returning a static array should be OK though).
  4. There are many more reasons.
n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
1

There is nothing wrong with returning a pointer to a static variable. It's just that the return type must be declared properly. It kind of makes sense if you try to reproduce what the declarations mean, and what the compiler accordingly tries to do. Consider the declaration static string flow[23][2];. It declares 23 rows of strings, each with 2 elements. It helps if you look at it as a one-dimensional array. It just so happens that the array elements are arrays, but that's not so important right now (but we'll come back to it). From this perspective the array has just 23 elements, and each element has the size of 2 strings. Like with all arrays, the elements (here: arrys of 2 strings) are simply lined up in memory.

Like any array, flow will in most contexts decay to a pointer to its first element. Incrementing that pointer will point to the next element, i.e the second row. Numerically the compiler must add 2*sizeof(string) to the address of flow in order to compute the address of flow's next element, which would be flow[1]. (It comes directly behind flow[0]. No magic here.)

Now if you declare string **flowpp, flowpp is a pointer already, no need to decay. If we think it is pointing to the first element in an array, what type would the elements have? Sure enough: plain pointers. Incrementing flowpp would let it point to the next element. My pointers are 4 bytes large, so that numerically adding just 4 to flowpp would be enough to access flowpp's next element. Compared to what needs to be added to flow (remember, 2*sizeof(string)), that's completely different. The compiler computes the offsets of elements depending of what the pointers point to! Which is very different in the two cases.

So what can your function return? What does flow decay to when you return it? It decays to a pointer to its first element. The elements are arrays of two strings. It must be string xxx[2], with xxx being a pointer: hence string (*p)[2]. If the pointer is actually returned by a function, we have a function call instead of plain p, so it's (*f())[2].

Here is a complete example:

#include<iostream>
using namespace std;

const int numFlowElems = 3, numArrElems = 2;

/** @return a pointer to the first element of a static array
    of string[numArrElems]s.
*/  
string (*flowFile())[numArrElems]
{   // init so that we see something below.
    static string flow[numFlowElems][numArrElems] 
                = {{"1","2"}, 
                   {"3","4"},
                   {"5","6"}
                  };

    // your function code ...
    return flow;
}

int main()
{
     // array decays to ptr, like usual. Ptr elems are string[numArrElems].
     // ptrToArr is a pointer to arrays of two strings.
     string (*ptrToArr)[numArrElems] = flowFile();

     for( int flowInd= 0; flowInd<numFlowElems; ++flowInd )
     {
        for(int strInd = 0; strInd<numArrElems; ++strInd)
        {
            cout << ptrToArr[flowInd][strInd] << ' ';       
        }
        cout << endl;
     }

     return 0;
}

How do you parse string (*flowFile())[numArrElems]? I needed two attempts to get the declaration right, if that's any consolation. The key is that in C and C++ (not in C#, mind you!) a declaration has the shape of an expression.

You can do it from the inside to the outside: flowFile() is a function. The result is dereferenced because the function call has higher precedence than the star: *flowFile() is the dereferenced result. Apparently that result is an array of size numArrElems, with elements which are strings.

You can do it outside in: The result of (*flowFile())[numArrElems] is declared as a string. (*flowFile()) is an array of strings with numArrElems elements. Apparently flowFile() must be dereferenced to obtain that array so that flowfile is a function which returns a pointer to an array of numArrElems strings. That's true! It returns the first element of flow, which is exactly an array of strings.

Vectors of vectors might indeed be easier; if you want to retain the semantics you should pass references, as others mentioned: After all, all functions in your original program will operate on the same static array. If you pass vectors by value that will not be the case any longer. But then, that may actually be beneficial.

Peter - Reinstate Monica
  • 15,048
  • 4
  • 37
  • 62