1

I am stuck at this for 2 days since I am so new to C++.I want to use this function to convert from CString Array to Int Array but I dont know how it could be done. Is there any suggestions, Thanks in Advance !

Here are my code:

 void CCalculationBasicDlg::StringToIntegerArr(const CStringArray& arFields)
{
 int length = arFields.GetSize();
 int* arNum = new int[length];
 int tmp = 0; 
 for (int i = 0; i < length; i++)
 {
    tmp = _tstof(arFields[i]);
    arNum[i] = tmp;
 }
}

  // button to test function 
  void CCalculationBasicDlg::OnBnClickedBtnResult()
  { 
    UpdateData(TRUE);
    CString str_1, strDelimiters;
    CStringArray arFields1;

    edit_number_one.GetWindowText(str_1);
    m_ctrlDelimiters.GetWindowText(strDelimiters);

   // take String 1 and store in arFields1
   MyTokenizer(str_1, strDelimiters, arFields1);

   StringToIntegerArr(arFields1);
   // Can I put a breakpoint to test the integer array

   UpdateData(FALSE);
 }
NguyenHai
  • 57
  • 1
  • 8
  • I think that the MFC containers have been superseded by the STL containers. I doubt even Microsoft uses the MFC containers any more in the C++ apps they produce. So maybe you should learn STL, and drop the usage of these legacy, outdated MFC ones that had their purpose 15 or so years ago, but not now. – PaulMcKenzie Sep 09 '16 at 03:23
  • And what exactly is your problem? Please describe what it is. – Ken Y-N Sep 09 '16 at 03:28
  • 1
    BTW, using STL it becomes a 1 line program, not 15 or 20 lines: `std::vector s;...std::vector v;...std::transform(s.begin(), s.end(), std::back_inserter(v), std::stoi);` – PaulMcKenzie Sep 09 '16 at 03:31
  • @PaulMcKenzie: *"I think that the MFC containers have been superseded by the STL containers."* - This is not correct. Both container classes have distinct feature sets. The C++ Standard Library containers don't work with MFC Serialization, for example. You cannot recommend a replacement without knowing the OP's requirements. – IInspectable Sep 09 '16 at 06:16

2 Answers2

3

The conversion is a simple matter of calling std::stoi (or std::atoi if you don't need error handling). The issue is complicated, because CString stores either ANSI (code page) or Unicode encoded characters.

Since std::stoi has overloads for both std::string and std::wstring, this is conveniently dealt with, by having the compiler construct the appropriate temporary from the CString's controlled sequence:

std::stoi(cstr.GetString());  // invokes either string::string(const char*) or
                              //                wstring::wstring(const wchar_t*)

The conversion function can then be written as:

int* CCalculationBasicDlg::StringToIntegerArr(const CStringArray& arFields)
{
    int length = arFields.GetSize();
    int* arNum = new int[length];  // alternatively: std::vector<int> arNum(length);
    for (int i = 0; i < length; i++)
    {
        int value = std::stoi(arFields[i].GetString());
        arNum[i] = value;
    }
    return arNum;  // caller is responsible for clean-up
}


A few notes on the implementation:
  • Using a naked pointer (int* arNum) fails to address the requirements of exception safety. Both stoi as well as the (invisible) string/wstring constructors can throw exceptions, leaving the code with a memory leak. Use a smart pointer (e.g. std::unique_ptr) instead.
  • Better yet, use a standard container to manage the storage entirely.
  • Use move semantics for better performance. When using a std::vector you don't have to do anything in particular. Simply return the local object, and the compiler will do the rest.
  • Since the code can throw C++ exceptions (just as your original code), make sure you understand the rules. In particular, all stack frames in between throwing and catching an exception must know about C++ exceptions. This is not in general true. As soon as you are called by the OS, all bets are off.
IInspectable
  • 46,945
  • 8
  • 85
  • 181
  • Thank you for you help ! The conversion function worked perfectly; I had 3 editBoxes , I presumed that I do not know about the input String, Can I call this function and get 3 different Interger Arrays from 3 EditBoxes ? – NguyenHai Sep 12 '16 at 01:10
  • Thank you for your great assistance again ! I called the function: int length1 = arFields1.GetSize(); int* arNum1 = new int[length1]; arNum1 = StringToIntegerArr(arFields1); int length2 = arFields2.GetSize(); int* arNum2 = new int[length2]; arNum2 = StringToIntegerArr(arFields2); int result = arNum1[3] + arNum2[3]; My button Worked ! – NguyenHai Sep 12 '16 at 03:01
  • @NguyenHai: That produces a memory leak. You are allocating the array twice. It's really a lot easier, too: `int* arNum1 = StringToIntegerArr(arrFields1);` You also have to properly clean up `arNum1`, when you're done with it by calling `delete [] arNum1;`. Since you are new to C++, have a look at [The Definitive C++ Book Guide and List](http://stackoverflow.com/q/388242/1889329). – IInspectable Sep 12 '16 at 10:17
  • You are absolutely right @IInspectable , I got the error of memory leak, **Unhandled exception at 0x7696C54F in CalculationBasic.exe: Microsoft C++ exception: std::invalid_argument at memory location 0x0041DD84.** I will follow your advice and look into the Book, but can u suggest me the way to get rid of that memory leak. I am not aware of how to use properly either a smart pointer or Standard container – NguyenHai Sep 13 '16 at 00:07
  • @NguyenHai: Memory leaks are reported in the debugger, after the application terminates. The unhandled exception is raised somewhere else. You'll have to use the debugger to find out, why/where it is thrown. More information at [Managing Exceptions with the Debugger](https://msdn.microsoft.com/en-us/library/x85tt0dd.aspx). Tick the *Break When Thrown* checkbox for the [std::invalid_argument](http://en.cppreference.com/w/cpp/error/invalid_argument) exception (under *C++ Exceptions*). That will reveal the full call stack of this exception, and makes it easy to see, what's wrong. – IInspectable Sep 13 '16 at 08:08
  • Thank you for your valuable advice ! @IInspectable. I will keep practicing more with C++. – NguyenHai Sep 14 '16 at 23:48
1

First of all, why do you use CStringArray and not a std::vector ? Do you know your array size over the hole Programm? When not please use vector. To create an array is a big task because you must allocate memory which creates a performance problem when it's used too often. The vector doesn't have these problems because of it's flexible size of alocated memory.

To convert a CString to Int you use std::atoi(CString). My Solution looks like this:

CStringArray test;
int help[100];
for (int i = 0; i < test.GetSize(); i++) {
    help[i] = std::atoi(test.ElementAt(i));
}
Jan Hohenheim
  • 3,552
  • 2
  • 17
  • 42
Lord_Curdin
  • 957
  • 1
  • 14
  • 27
  • *"To create an array is a big task because you must allocate memory"* - Uhm, yes. If you need to allocate memory, you need to allocate memory. Hardly a *"big task"*. *"The vector doesn't have these problems because of it's flexible size of alocated memory."* - A vector has **exactly** the same issues as a manually managed dynamic array. You just don't see all those calls to `operator new[]`. The code you provided exhibits UB for string array sizes above 100. And you are suggesting to use a function (`atoi`) that doesn't have appropriate error reporting. – IInspectable Sep 09 '16 at 09:13
  • Yes "big task" was a bad expression. Yes i know that vector also allocate memory, but when you often change the size of the array, vector will be more efficient because the vectors reserve extra memory, vectors doesn't allocate Memory for each push_back. – Lord_Curdin Sep 09 '16 at 09:30
  • Plus, the call to `std::atoi` won't compile, if you have `UNICODE` defined (which you should). – IInspectable Sep 09 '16 at 10:04
  • Thank you for your suggestion, I tried your solution and it showed the error *int atoi(const char *)': cannot convert argument 1 from 'CString' to 'const char * ; I had an EditBox, I presumed that I dont know about the input String, and I just want to get my array Integer from that. – NguyenHai Sep 12 '16 at 00:46