views:

487

answers:

7

String[] decode(String message)

Above is an example. I need to get 2 strings s1 and s2 as the return values for the decode function. How should I proceed?

+6  A: 

If you need return few strings, use one of next:
- std::pair
- boost::tuple
- structure

If you don't know how many strings your function will return - use something like std::vector.

bb
Soon, it will be std::tuple, not boost::tuple :-)
Zifre
+4  A: 

If it were me, I'd return a vector of strings. With an array, you'll need to worry about memory management. You could also pass the string in as a const reference, if you don't want to modify it.

Caleb Huitt - cjhuitt
I got it from the Top Coder practice sessions. It's been asked in the question to return an array of strings.
abc
+1  A: 

If you're feeling hardcore and up for the challenge, you could return a char**.

However, this is very error prone, so you should probably stick to a standard container or string class instead.

samoz
Read http://www.lysator.liu.se/c/c-faq/c-2.html#2-10
siddhant3s
@siddhant3s: He means a char**, just like argv in main. Use a NULL terminator, and this is the old fashioned c way of doing thing. You should treat this approach as deprecated in c++, but it *will* work just fine.
dmckee
A: 

Pass the strings to the functions as "out" parameters - either by pointers or references and modify them within the function.

Nemanja Trifunovic
+1  A: 

Did they(Top Coder) mentioned array of C-strings or array of std::strings ? For your information, arrays are evil (http://siddhant3s.googlepages.com/how_to_tell_rusted_cpp.html and http://www.parashift.com/c++-faq-lite/containers.html#faq-34.1) Lately, if you still want to return array of std::strings, you can but only if you promise me that you will delete[] the returned pointer accordingly:

#include<iostream>
#include<string>
std::string* F1()
{
    std::string* s=new std::string[2];
    s[0]="Hello";
    s[1]="World";
    return s;
}

int main()
{
    std::string* ss=F1();
    std::cout<<ss[0]<<ss[1];

    delete[] ss; //important step
}
siddhant3s
Best compile without exceptions, since otherwise you leak memory if anything throws...
Steve Jessop
+3  A: 

I'd say the options are:

For a fixed number of strings:

1) return a pair, a tuple, or a struct that you define yourself. Something like:

struct BrokenDownString {
    std::string firstbit;
    std::string middlebit;
    std::string endbit;
};

Then either:

BrokenDownString decode(std::string message);

or just give BrokenDownString a constructor taking the message as a parameter.

2) Take multiple out params by pointer or non-constant reference:

void decode(const std::string &message, std::string &out_1, std::string &out_2) {
    out_1 = /*whatever*/;
    out_2 = /*whatever*/;
}

For two strings, anything else (even an array) is overkill.

For a variable number of strings, however, you can either:

1) return a std::vector<std::string> (be aware that this may result in excess copying).

2) take a std::vector<std::string> & as a parameter, and append the results (this may copy strings but not the container).

3) make decode a function template, taking an output iterator as a parameter:

template<typename OUT>
void decode(const std::string message, OUT out) {
    // do parsing
    *(out++) = firstbit;
    *(out++) = nextbit;
    // etc.
}

Then if the caller wants the results in a vector:

std::vector<std::string> v;
decode(message, std::back_inserter(v));

If the caller prefers them in a deque:

std::deque<std::string> d;
decode(message, std::back_inserter(d));

If the caller wants them in a list in reverse order:

std::list<std::string> l;
decode(message, std::front_inserter(l));

And so on.

4) If you want something like the above, but for whatever reason you don't want to write template code, make decode take as a parameter an object, which it notifies of each string:

struct DecodeTarget {
    virtual void append(const std::string &) = 0;
};

void decode(std::string message, DecodeTarget &out) {
    // do parsing
    out.append(firstbit);
    out.append(nextbit);
    // etc.
}

Then if the caller wants the results in a vector:

class VectorTarget : public DecodeTarget {
private:
    std::vector<std::string> &results;
public:
    VectorTarget(std::vector<std::string> &v) : results(v) { }
    void append(const std::string &bit) { v.push_back(bit); }
};

std::vector<std::string> v;
VectorTarget vt(v);
decode(message, vt);
Steve Jessop
+1  A: 

If you absolutely must return an array, you can use the fact that raw arrays decay into pointers. Here's some sample code that does what I think you want:

#include <iostream>
#include <ostream>
#include <string>

std::string * decode()
{
    std::string *ret = new std::string[2];
    ret[0] = "foo";
    ret[1] = "bar";
    return ret;
}

int main()
{
    std::string *baz = decode();
    std::cout << baz[0] << baz[1] << std::endl;
    delete [] baz;

    return 0;
}

Notice that this way requires you to keep track of the following:

  • who owns the memory returned by decode()
  • the fact that the pointer returned is really an array
  • the size of the array

Yes, it's painful. That's why everyone else has suggested using a std::pair or std::vector to handle the work for you.

Kristo