Author Topic: Problem using friend functions of a class built with wxSmith  (Read 5667 times)

Offline Grouch

  • Multiple posting newcomer
  • *
  • Posts: 40
Problem using friend functions of a class built with wxSmith
« on: September 05, 2012, 05:02:18 am »
I am having trouble making a “friend” function work with a class built with wxSmith to provide a user interface for a computation-intensive statistical program. I do not want to make all of the coding of the statistical routines members of the interface class, so the natural thing to do is to create a function to write results to the interface that is globally accessible but is a “friend” of the interface class.  This sounds simple, but I have been unable to do it. After much guesswork, I got a program that compiles and starts to run but crashes when the friend function is used.

The interface program, if I may call it that, is essentially that of Tutorial 9 of on-line wxSmith tutorials. I can illustrate the problem, however, with an even simpler program.

Use Code::Blocks to create a wxWidgets application called GwxAmicus. Put on the window a box sizer; in the box sizer, put a panel and mark its Expand property. Onto the panel, put another box sizer and make it vertical. Into this sizer put (1) a button with label Insider, name InsiderBtn, and proportion 0; below it put (2) a button with label Amicus, name AmicusBtn, and proportion 0; finally, in the third box, put (3) a wxTextCtrl, make the Text field blank, give it the name Results, uncheck Default size and make it 300 by 200, check the Expand property, drop down the Size property and give it the AutoScroll, Multiline, and Full_Repaint_on_Resize properties. Now double-click the Insider button and code its response like this:

void GwxAmicusFrame::OnInsiderBtnClick(wxCommandEvent& event){
    Results->AppendText(_("From insider\n"));
    }

Double-click the Amicus button and code its response like this:
void Amicus();
void GwxAmicusFrame::OnAmicusBtnClick(wxCommandEvent& event){
    Amicus();
    }

The Amicus() function is going to be a friend of the GwxAmicusFrame class, so we edit GwxAmicusMain.h by addition of one line so it begins as follows:
class GwxAmicusFrame: public wxFrame
{
    public:

        GwxAmicusFrame(wxWindow* parent,wxWindowID id = -1);
        virtual ~GwxAmicusFrame();
        friend void Amicus(); // added by me

The last line was added by me.

Now we need to write the Amicus() function. Basically, we want something like this:
void Amicus(){
     xxxx->Results->AppendText(_("From Amicus\n"));
    }

The first problem is what to put where I have written xxxx. We cannot just leave it blank; Amicus() is a friend but not a member of the GwxAmicusFrame class, so we need a pointer to the class, but what exactly should it be?  After considerable unsuccessful guessing and producing programs which would not compile (usually producing the enigmatic error message “expected unqualified-id before -> token) I happened to look at the GwxAmicusApp.cpp file and there I saw the following:

#include "wx_pch.h"
#include "GwxAmicusApp.h"

//(*AppHeaders
#include "GwxAmicusMain.h"
#include <wx/image.h>
//*)

IMPLEMENT_APP(GwxAmicusApp);

bool GwxAmicusApp::OnInit()
{
    //(*AppInitialize
    bool wxsOK = true;
    wxInitAllImageHandlers();
    if ( wxsOK )
    {
       GwxAmicusFrame* Frame = new GwxAmicusFrame(0);
       Frame->Show();
       SetTopWindow(Frame);
    }
    //*)
    return wxsOK;
}

This code suggested that what I wanted in place of the xxxx was Frame->. That, however, would not work as long as Frame remained a local variable inside the OnInit() function. So I modified this code as follows:
IMPLEMENT_APP(GwxAmicusApp);
GwxAmicusFrame* Frame; //Added by me
bool GwxAmicusApp::OnInit()
{
    //(*AppInitialize
    bool wxsOK = true;
    wxInitAllImageHandlers();
    if ( wxsOK )
    {
       GwxAmicusFrame* Frame = new GwxAmicusFrame(0);
       Frame->Show();
       SetTopWindow(Frame);
    }
    //*)
    return wxsOK;
}
// Next three lines added by me:
void Amicus(){
    Frame->Results->AppendText(_("From Amicus\n"));
    }

At last, the program compiled, linked, and began execution. The Insider button worked perfectly. But when, full of hope, I clicked the Amicus button, it crashed and exited.

What am I doing wrong? What is the right way to deal with this problem?

Sorry to have to take so much of your time to explain the problem, but I did not want to be cryptic. Any help would be most appreciated.

Offline ollydbg

  • Developer
  • Lives here!
  • *****
  • Posts: 6079
  • OpenCV and Robotics
    • Chinese OpenCV forum moderator
Re: Problem using friend functions of a class built with wxSmith
« Reply #1 on: September 05, 2012, 05:12:47 am »
Quote
At last, the program compiled, linked, and began execution. The Insider button worked perfectly. But when, full of hope, I clicked the Amicus button, it crashed and exited.

What am I doing wrong? What is the right way to deal with this problem?
I can't say much, but you should debug your program. This is the only way to fix your crash bug.
If some piece of memory should be reused, turn them to variables (or const variables).
If some piece of operations should be reused, turn them to functions.
If they happened together, then turn them to classes.

Offline jarod42

  • Multiple posting newcomer
  • *
  • Posts: 87
Re: Problem using friend functions of a class built with wxSmith
« Reply #2 on: September 05, 2012, 10:59:56 am »
Your global Frame is 'shadowed' by the local Frame.

I suggest to change Amicus() by Amicus(GwxAmicusFrame* frame);
so you will have
Code
void GwxAmicusFrame::OnAmicusBtnClick(wxCommandEvent& event) {
    Amicus(this);
}
and
Code
void Amicus(GwxAmicusFrame* frame) {
     frame->Results->AppendText(_("From Amicus\n"));
}

Offline Grouch

  • Multiple posting newcomer
  • *
  • Posts: 40
Re: Problem using friend functions of a class built with wxSmith
« Reply #3 on: September 05, 2012, 05:53:06 pm »
Many, many thanks, jarod42 for your fundamental insight that the problem was that the global Frame variable was not being used because a local variable by the same name was being was being created. Your proposals worked fine, but did not accomplish the objective of making an Amicus() function which could be called from anywhere, not just from the interface class. A simpler change accomplished that purpose and worked fine. Here it is:

In the GwxAmicusApp::OnInit() function, replace the line     
 GwxAmicusFrame* Frame = new GwxAmicusFrame(0);

with just
Frame = new GwxAmicusFrame(0);
That gets the definition put into the global Frame variable. In practice, it is probably a good idea to replace the word "Frame" with something more distinctive, maybe "MainFrame".

To share your insight more broadly, I will revise the wxSmith Tutorial 9 (of which I wrote the original version) to make the printg() function -- a sort of replacement for C's printf() -- callable from anywhere, just as printf() is.

As for the advice to "debug" my program, I appreciate your reading my post. I had run the program under the debugger but learned only that it was referencing an illegal address, the most common cause of a crash, so I did not mention that in the post. The question was, Why was the address illegal? Jarod42 put his finger on the problem exactly.


Offline jarod42

  • Multiple posting newcomer
  • *
  • Posts: 87
Re: Problem using friend functions of a class built with wxSmith
« Reply #4 on: September 05, 2012, 06:14:17 pm »
You should read this related topic:
How to do Frame ptr be a member of app class when using wxSmith
since the code you want to modify is auto-generated code.
(for short, remove "//(*AppInitialize" and "//*)" ).

Offline MortenMacFly

  • Administrator
  • Lives here!
  • *****
  • Posts: 9723
Re: Problem using friend functions of a class built with wxSmith
« Reply #5 on: September 06, 2012, 09:25:39 am »
(for short, remove "//(*AppInitialize" and "//*)" ).
IMHO You don't even need to. Why don't you simply use the "extra code" feature to assign the pointer to your member variable inside this section using wxSmith?
Compiler logging: Settings->Compiler & Debugger->tab "Other"->Compiler logging="Full command line"
C::B Manual: https://www.codeblocks.org/docs/main_codeblocks_en.html
C::B FAQ: https://wiki.codeblocks.org/index.php?title=FAQ