Error C2228 left of '.size' must have class/struct/union on Winform - string

I'm creating my first program on Winform with C++. I have a function and a string input. However, when I run the program, I get the following error with text.size(): "Error C2228 left of '.size' must have class/struct/union"
Relevant code:
private: System::Void bntGet_Click(System::Object^ sender, System::EventArgs^ e) {
in_itext = txtText->Text;
itext = Convert::ToString(in_itext);
for (Int32 index = 1; index <= itext.size(); index++)
{
if (itext[index] == '#')
{
while (itext[index - 1] != ' ')
{
index--;
}
while (itext[index] != ' ')
{
next_email += itext[index];
index++;
if (itext[index] == '\0')
break;
}
break;
}
}
txtEmail->Text = next_email;
}
Any suggestions? Thanks everyone!

Standard warning: While it's certainly possible to write the main body of your application in C++/CLI, or even write the GUI in C++/CLI using WinForms, it is not recommended. C++/CLI is intended for interop scenarios: where C# or other .Net code needs to interface with unmanaged C++, C++/CLI can provide the translation between the two. For primary development, it is recommended to use C# with either WinForms or WPF if you want managed code, or C++ with MFC if you want unmanaged.
That said:
for (Int32 index = 1; index <= itext.size(); index++)
and
"Error C2039 'length': is not a member of 'System::String'"
Size is not how you get the length of a System::String in .Net. For that, you want the Length property. You were close on your second attempt, but you need a capital "L". (size() is how std::string does it, but std::string is a completely different beast from System::String.)
Other issues:
in_itext = txtText->Text;
itext = Convert::ToString(in_itext);
It's already a string, you don't need to convert it.
while (itext[index - 1] != ' ')
{
index--;
}
You can easily run off the beginning of the string if you do this. (You'll get an exception when index is equal to zero.) Add a check against zero.
while (itext[index] != ' ')
{
next_email += itext[index];
index++;
if (itext[index] == '\0')
break;
}
Strings in .Net are not null-terminated, you need to check against the Length.
If we take a step back, what problem are you actually trying to solve here? My best guess is that you've got some text with an email address somewhere in the middle, and you just want to extract the email address. If that's the case, then I'd probably try using a regular expression to extract the email address. You've got the full power of the .Net Library available to you, use it! See Using a regular expression to validate an email address for the regex to use, and read up on regular expressions with the introduction page and class reference.

Related

Resolving code analysis warnings with the BOLDDAY macro (used with CMonthCalCtrl)

I have some issues with the CMonthCalCtrl control and modernizing my code. The first problem is related to the BOLDDAY macro.
This macro is used to adjust day states (making specific dates bold on the calendar) and the concept is described in detail here. As documented, you need to define a macro:
#define BOLDDAY(ds, iDay) if(iDay > 0 && iDay < 32) \
(ds) |= (0x00000001 << (iDay-1))
Here is my code that uses this macro so that you have some context:
void CMeetingScheduleAssistantDlg::InitDayStateArray(int iMonthCount, LPMONTHDAYSTATE pDayState, COleDateTime datStart)
{
int iMonth = 0;
COleDateTimeSpan spnDay;
CString strKey;
SPECIAL_EVENT_S *psEvent = nullptr;
if (pDayState == nullptr)
return;
memset(pDayState, 0, sizeof(MONTHDAYSTATE)*iMonthCount);
if (m_pMapSPtrEvents == nullptr && m_Reminders.Count() == 0)
{
return;
}
spnDay.SetDateTimeSpan(1, 0, 0, 0);
auto datDay = datStart;
const auto iStartMonth = datStart.GetMonth();
auto iThisMonth = iStartMonth;
auto iLastMonth = iThisMonth;
do
{
strKey = datDay.Format(_T("%Y-%m-%d"));
if (m_pMapSPtrEvents != nullptr)
{
psEvent = nullptr;
m_pMapSPtrEvents->Lookup(strKey, reinterpret_cast<void*&>(psEvent));
if (psEvent != nullptr)
{
BOLDDAY(pDayState[iMonth], datDay.GetDay());
}
}
if (m_Reminders.HasReminder(datDay))
{
BOLDDAY(pDayState[iMonth], datDay.GetDay());
}
datDay = datDay + spnDay;
iThisMonth = datDay.GetMonth();
if (iThisMonth != iLastMonth)
{
iLastMonth = iThisMonth;
iMonth++;
}
} while (iMonth < iMonthCount);
}
Everywhere I use this BOLDDAY macro I get a code analysis warning (C26481):
warning C26481: Don't use pointer arithmetic. Use span instead (bounds.1).
It is not clear to me if the problem is with the BOLDDAY macro or my own code?
Update
I still get the warning when I turn the macro into a function:
Update 2
If it helps, I currently call the InitDayStateArray function in the following ways:
Method 1:
void CMeetingScheduleAssistantDlg::SetDayStates(CMonthCalCtrl &rCalendar)
{
COleDateTime datFrom, datUntil;
const auto iMonthCount = rCalendar.GetMonthRange(datFrom, datUntil, GMR_DAYSTATE);
auto pDayState = new MONTHDAYSTATE[iMonthCount];
if (pDayState != nullptr)
{
InitDayStateArray(iMonthCount, pDayState, datFrom);
VERIFY(rCalendar.SetDayState(iMonthCount, pDayState));
delete[] pDayState;
}
}
Method 2
void CMeetingScheduleAssistantDlg::OnGetDayStateEnd(NMHDR* pNMHDR, LRESULT* pResult)
{
NMDAYSTATE* pDayState = reinterpret_cast<NMDAYSTATE*>(pNMHDR);
MONTHDAYSTATE mdState[3]{}; // 1 = prev 2 = curr 3 = next
const COleDateTime datStart(pDayState->stStart);
if (pDayState != nullptr)
{
InitDayStateArray(pDayState->cDayState, &mdState[0], datStart);
pDayState->prgDayState = &mdState[0];
}
if (pResult != nullptr)
*pResult = 0;
}
Perhaps if the container for the LPMONTHDAYSTATE information is tweaked somehow it would contribute to resolve this span issue?
Sample code provided by Microsoft used to be published as code that compiles both with a C and C++ compiler. That limits availability of language features, frequently producing code that particularly C++ clients shouldn't be using verbatim.
The case here being the BOLDDAY function-like macro, that's working around not having reference types in C. C++, on the other hand, does, and the macro can be replaced with a function instead:
void bold_day(DWORD& day_state, int const day) noexcept {
if (day > 0 && day < 32) {
day_state |= (0x00000001 << (day - 1));
}
}
Using this function in place of the BOLDDAY macro silences the C26481 diagnostic.
While that works, I'm at a complete loss to understand where the compiler is seeing pointer arithmetic in the macro version. Regardless, replacing a function-like macro with an actual function (or function template) where possible is always desirable.
Update
Things are starting to make sense now. While replacing the function-like macro with a function, as suggested above, is desirable, it will not resolve the issue. My test happened to have used pDayState[0] which still raises C26481 for the macro, but not for the function. Using pDayState[1] instead, the diagnostic is raised in either case.
Let's put the pieces of the puzzle together: Recall that the array subscript expression p[N] is exactly identical to the expression *(p + N) when p is a pointer type and N an integral type. That explains why the compiler is complaining about "pointer arithmetic" when it sees pDayState[iMonth].
Solving that is fairly straight forward. As suggested by the diagnostic, use a std::span (requires C++20). The following changes to InitDayStateArray() make the C26481 diagnostic go away:
void CMeetingScheduleAssistantDlg::InitDayStateArray(int iMonthCount,
LPMONTHDAYSTATE pDayState,
COleDateTime datStart)
{
std::span const day_month_state(pDayState, iMonthCount);
// ...
// memset(pDayState, 0, sizeof(MONTHDAYSTATE)*iMonthCount);
std::fill(begin(day_month_state), end(day_month_state), 0);
// ...
do
{
// ...
{
bold_day(day_month_state[iMonth], datDay.GetDay());
}
}
if (m_Reminders.HasReminder(datDay))
{
bold_day(day_month_state[iMonth], datDay.GetDay());
}
// ...
} while (iMonth < day_month_state.size());
}
A std::span "describes an object that can refer to a contiguous sequence of objects". It takes the decomposed pointer and size arguments that describe an array and reunites them into a single object, recovering the full fidelity of the array.
That sounds great. But remember, this is C++, and there's a caveat: Just like its evil C++17 ancestor std::string_view, a std::span is an unhesitating factory for dangling pointers. You can freely pass them around, and hang on to them far beyond the referenced data being alive. And this is guaranteed for every specialization, starting with C++23.
The other issue is, that addressing this one diagnostic now has several others pop out of nowhere, suggesting that std::span isn't good enough, and gsl::span should be used instead. Addressing those would probably warrant another Q&A altogether.

C++ Converting textbox content to a Float

Tried carefully code from this thread
Converting textbox string to float?
Tried to leavemy question in a comment, but yet not allowed to do so...
The summary of that goes like having two text boxes, taking data from first, doing something, and returning the result to the second.
String^ i1 = Textbox1->Text;
float rez = (float)(Convert::ToDouble(i1)*4);
Textbox2->Text = rez.ToString();
and it works pretty good unless Textbox1 got a float within itself (upd. it works ok with '65', but doesn't work with '65.5').
Trying to execute that code - crushes a program
> Calc.exe!Calc::Form1::Button0_Click(System::Object^ sender = 0x01b29c58, System::EventArgs^ e = 0x01b45e40) Line 123 + 0x30 byte C++
Convert::ToDouble
Using the ToDouble(String) method is equivalent to passing value to the Double.Parse(String) method. Value is interpreted by using the formatting conventions of the current thread culture.
So, you need
Catch possible exceptions
try {
float rez = (float)(Convert::ToDouble(i1)*4);
}
catch (FormatException) {
// handle format error exception here
}
catch (OverflowException) {
// handle overflow exception here
}
Use IFormatProvider

Read a String with spaces till a new line in C

I am in a pickle right now. I'm having trouble taking in an input of example
1994 The Shawshank Redemption
1994 Pulp Fiction
2008 The Dark Knight
1957 12 Angry Men
I first take in the number into an integer, then I need to take in the name of the Movie into a string using a character array, however i have not been able to get this done.
here is the code atm
while(scanf("%d", &myear) != EOF)
{
i = 0;
while(scanf("%[^\n]", &ch))
{
title[i] = ch;
i++;
}
addNode(makeData(title,myear));
}
The title array is arbitrarily large and the function is to add the data as a node to a linked list. right now the output I keep getting for each node is as follows
" hank Redemption"
" ion"
" Knight"
" Men"
Yes, it oddly prints a space in front of the cut-off title. I checked the variables and it adds the space in the data. (I am not printing the year as that is taken in correctly)
How can I fix this?
You are using the wrong type of argument passed to scanf() -- instead of scanning a character, try scanning to the string buffer immediately. %[^\n] scans an entire string up to (but not including) the newline. It does not scan only one character.
(Marginal secondary problem: I don't know from where you people are getting the idea that scanf() returns EOF at end of input, but it doesn't - you'd be better off reading the documentation instead of making incorrect assumptions.)
I hope you see now: scanf() is hard to get right. It's evil. Why not input the whole line at once then parse it using sane functions?
char buf[LINE_MAX];
while (fgets(buf, sizeof buf, stdin) != NULL) {
int year = strtol(buf, NULL, 0);
const char *p = strchr(buf, ' ');
if (p != NULL) {
char name[LINE_MAX];
strcpy(name, p + 1); // safe because strlen(p) <= sizeof(name)
}
}

VC++ Compiler options

I have a VC++ 6.0 project which I am now compiling using VS2008 . I have this piece of code that used to compile under VC++ 6 but throws an error under VS2008 :
int CIDStorage::Length()
{
CIDStorage* m_ptr = this;
for(int i = 0;m_ptr->m_ptrNext != NULL;i++)
m_ptr = m_ptr->m_ptrNext;
if(i == 0)
if(m_ID.IsEmpty())
return 0;
return i+1;
}
the error is 'i' : undeclared identifier
No probs with that I can see how that came about . So ... Do I change the source code . Or is there a compiler setting I could set that cures this ?
VC++ 6 (normally1) follows a pre-standard rule where a variable defined in a for loop remains defined for the rest of the scope in which that for loop resides. VC++ 2008 follows the standard rule where the for loop defines a new scope, and the variable is defined only within that scope.
The cure is pretty simple -- define the variable outside the loop:
int CIDStorage::Length()
{
CIDStorage* m_ptr = this;
int i;
for(i = 0; m_ptr->m_ptrNext != NULL; i++)
m_ptr = m_ptr->m_ptrNext;
if(i == 0)
if(m_ID.IsEmpty())
return 0;
return i+1;
}
1 The compiler in VC++ is actually capable of following the correct rules for scoping of variables defined in a for loop. Unfortunately, to follow the rule, you have to use the /Za flag, which tries to enforce all the rules it knows as strictly as possible. That turns out to be completely unusable because with that turned on, it rejects (virtually?) all of its own headers as containing errors!

Listing files in directory

I have created a windows form in c++ which, upon a button click, opens a dialog box for folder selection.
Now what I would like to do is get the list of files in that directory so that I can process them one by one.
I have googled it in many ways, and found many ways which include external libraries (such as boost and diren.h). I would not like to use external resources, but the ones at my disposal, the default ones.
I've read about FindFirstFile and FindNextFile, but couldnt get that combination to work.
Could you please assist?
Thanks a lot,
Idan.
Here is the updated code:
HANDLE hFind;
WIN32_FIND_DATA FindFileData;
FolderBrowserDialog^ folderBrowserDialog1 = gcnew FolderBrowserDialog;
if (folderBrowserDialog1->ShowDialog() == System::Windows::Forms::DialogResult::OK)
{
String ^ selected = folderBrowserDialog1->SelectedPath;
selected += "\\*";
char* stringPointer = (char*) Marshal::StringToHGlobalAnsi(selected).ToPointer();
hFind = FindFirstFile((LPCWSTR)stringPointer, &FindFileData);
while(hFind != INVALID_HANDLE_VALUE)
{
printf("Found file: %s\r\n", FindFileData.cFileName);
if(FindNextFile(hFind, &FindFileData) == FALSE)
break;
}
}
You obviously compile for UNICODE (wide char) since you need to cast the newStr for the lpFileName parameter of FindFirstFile. But since you pass an ANSI string, you probable won't get a useful result. Youd didn't write, what you expect to find.
In the code beforer FindFirstFile you manually convert the SelectedPath value to ANSI char. That makes no sense, when you need a wide char string anyway. Get the LPCWSTR from the String selected with the StringToHGlobalUni method. This looks somehow like this (not tested):
LPCWSTR stringPointer = Marshal::StringToHGlobalAnsi(selected).ToPointer();
hFind = FindFirstFile(stringPointer, &FindFileData);
In general: Don't use casts except when you need to adapt a bad designed interface. Use it only when you know exactly what you are doing.
Further you don't check the hFind result of FindFirstFile. It will be INVALID_HANDLE_VALUE if you pass a pointer to the wrong string format.

Resources