Function pointers?



  • I am still learning the basics of programming and writing a little windows app to familiarize myself with C++. I wrote this piece of code, looked at it and thought, there has to be a better way of doing it.

    I need to get data from a series of controls and send it off to be processed, then receive data back and display it in controls. The point is, I want to be able to add controls without having to modify almost identical code in two places. That is, I don't want two seperate functions that each seperately call GetWindowText and SetWindowText. Currently I have a total mess involving 9 different functions, 6 of which take the same arguments and are pointed to by 3 different function pointers with wierd names like GetOrSetFloatFromWindow.

    How would I do this at least marginally more intelligently?  


     



  • maybe if you posted some of the code I would understand what you want to do.



  • Sorry I didn't post code earlier.

    There are two public methods

    bool GetDataFromGUI(StructWithData* Data)
    bool SetDataToGUI(StructWithData* Data)

    They both call GetOrSetGUIData(StructWithData* Data) but first they set a few function pointers;

    There are six private methods

    bool GetTextFromWindow(HWND handle, char* AddressOfBuffer);
    bool GetIntFromWindow(HWND handle, int* AddressOfInt);
    bool GetFloatFromWindow(HWND handle, float* AddressOfFloat);

    bool SetTextToWindow(HWND handle, char* AddressOfString);
    bool SetIntToWindow(HWND handle, int* AddressOfInt);
    bool SetFloatToWindow(HWND handle, float* AddressOfFloat);

    three function pointers

    GetOrSetText(HWND, char*)
    GetOrSetInt(HWND, int*)
    GetOrSetFloat(HWND, float*);

    this is what GetData looks like, SetData is pretty much the same with a bit of data validation
     

    bool GetDataFromGUI(StructWithData* Data)
     {
         GetOrSetText = &GetTextFromWindow;
         GetOrSetInt = &GetIntFromWindow;
         GetOrSetFloat = &GetFloatFromWindow;

        return GetOrSetGUIData(Data);

     

    bool GetOrSetGUIData(StructWithData* Data)
    {
            GetOrSetText(HWND FirstHandle, &Data.FirstValue);
            GetOrSetText(HWND SecondHandle, &Data.SecondValue);

            //snip

            return true; //if no error is thrown, the function succeeded
    }

     

     



  • that seems awfully convoluted.  Wouldn't it be more clear to just have your 6 private methods be public and forget about all this other stuff?  I think building a structure and sending it to a function is more complex that just picked which function to call.  It also makes the other interfacing class have to know too much about the internals of this class.

     



  • [quote user="tster"]

    that seems awfully convoluted.  

    [/quote]

     

    Convoluted is a nice word for it. I have cleaned it up a bit. I have turned all the GetTextFromWindow, GetIntFromWindow... into two, GetText and SetText and let the GetOrSetGUI deal with turning strings into numbers.

    The way I have it set up now, GetText is passed a handle and the address of a string. It takes the control the handle points to and copies whatever is there to the string. If I let the interfacing class call it directly, that would mean the interfacing class would have to have access to the handles. Maybe I am being dense, but I don't see a way around this, the Win32API call GetWindowText requires a handle.

    Is there a way to put a default argument in a function pointer in C++? That is, could I make a function pointer that could point at Win32API calls GetWindowText(HWND hWnd, LPTSTR lpString, int nMaxCount) and SetWindowText(HWND hWnd, LPCSTR lpString)?
     



  • Don't forget that C++ has a notion of const. Get and Set have different const specifications.

     Also you are using far too many pointers and I don't actually see that your code is C++ at all. It looks more like C to me.

    Note that C also has a notion of const. Libraries that are not const-correct are the biggest pain to work with,
    Sybase's Open-Client Server for example, that has functions like ct_props that take either CS_SET or CS_GET and are
    not const-correct when you are supplying a buffer that the function is only supposed to read.

    Note also that when you supply a buffer to write into, you also supply the length of your buffer so that the function will
    not overrun, thus the nMaxCount parameter in GetWindowText (which reads from the window into your buffer). But
    when you are writing to the window, you do not need to supply the length of your buffer as it reads until the first '\0' character.

     



  • [quote user="makito"]

    Is there a way to put a default argument in a function pointer in C++? That is, could I make a function pointer that could point at Win32API calls GetWindowText(HWND hWnd, LPTSTR lpString, int nMaxCount) and SetWindowText(HWND hWnd, LPCSTR lpString)?
     

    [/quote]

    You can't do this in a way that would work for Windows API functions.  These functions use "Pascal linkage".  This is indicated by the WINAPI macro that you'll see in the function declaration.

    In "C linkage", the caller pushes two or three or any number of arguments on the stack, with the leftmost argument on the top of the stack (just after the return address).  The called function can always find the leftmost argument, which may indicate how many more arguments there are (as printf() does for example).  The caller cleans up the stack.

    In "Pascal linkage" the rightmost argument is at the top of the stack, and there can only be a fixed number of arguments.  The called program cleans up the stack before returning.  This is done with a single instruction (RET with a constant, like "RET 12") that takes the return address off the stack and then points the stack pointer past the arguments (making this linkage more efficient).  GetWindowText() must end with "RET 12"; SetWindowText() must end with "RET 8".

    (Note that the Windows API was written in C, not C++, so "decorated" or "magled" function names are not a concern.)

    So there is no simple way you can take a function pointer that could hold the address of GetWindowText() or SetWindowText() and push the correct number of arguments in the correct order for each.

     



  • New and delete

    OK, I have circumvented the problem. I redesigned the GUI so that all boxes are either read only or write only. I should have designed it that way to begin with. I have another question though.

    What is the proper usage of the new and delete operators? I understand how they work, but why is

    char* PointerToString = new char[10];

    any better than,

    char ActualString[10];  

    Thanks for all your help 

     



  • Get the book "C++ FAQs" by Marshall Cline, Greg Lomow and Mike Girou.  Their explanation is much better than anything I could write here.

     http://www.amazon.com/C%2B%2B-FAQs-2nd-Marshall-Cline/dp/0201309831/sr=8-1/qid=1165275458/ref=pd_bbs_sr_1/103-0467545-9560658?ie=UTF8&s=books

    In a nutshell, though, char ActualString[10] exists only within the block in which it is declared, and is automatically destroyed when the block is exited.  All objects created with "new" must be destroyed with "delete".  Also all objects created with "new []" must be destroyed with "delete []".  This is extremely important because C++ lets you do it the wrong way and your program will crash horribly:

       char * PointerToString = new char[10];

       char * PointerToChar = new char;

       delete PointerToString;  //  WRONG!  Crash!

       delete [] PointerToChar;   // WRONG!  Crash!

       delete PointerToChar;   // RIGHT

       delete [] PointerToString; // RIGHT

     



  • Or instead of buying the book, go to the online site at http://www.parashift.com/c++-faq-lite/ and find the answers ... somewhere ... the answers are on page 13 - 18 of the book, so they've got to be on the website somewhere ...



  • In C++, the more common approach to function pointers are so-called "functors".

     

    For more information about hem, see http://www.newty.de/fpt/functor.html#chapter4 for example. 

     

    Regards,

     

    Leon Mergen 



  • [quote user="makito"]

    What is the proper usage of the new and delete operators? I understand how they work, but why is

    char* PointerToString = new char[10];

    any better than,

    char ActualString[10];

    Thanks for all your help 

    [/quote]

    The second one consumes 10 bytes of the stack whereas the first one only consumes 4 bytes(8 bytes if 64-bit). The problem is if a function that uses the the second one is recursive(directly/indirectly calls itself), then it is likely that it will cause a stack overflow.



  • [quote user="makito"]

    OK, I have circumvented the problem. I redesigned the GUI so that all boxes are either read only or write only. I should have designed it that way to begin with. I have another question though.

    What is the proper usage of the new and delete operators? I understand how they work, but why is

    char* PointerToString = new char[10];

    any better than,

    char ActualString[10];  

    Thanks for all your help

    [/quote]

    If the string is expected to live any longer than the scope in which it was declared, then use the new operator.  If it is to live only for the life of that scope, and it is not enormous, just allocate it on the stack (the second declaration).  Your memory manager will reward you by not destroying your performance through function calls, cache misses, and resource contention on the free list (if you're multi-threaded).  Oh, and you'll cut down on fragmentation, too, which would also have led to more cache misses and increased the length of time needed to allocate anything large.


Log in to reply