INVOKE MEMBER OF A CLASS FROM STATIC CALLBACK (without using static keyword)

25 views
Skip to first unread message

wm201...@gmail.com

unread,
Nov 18, 2016, 10:30:52 AM11/18/16
to fltk.general

// Hi, Just wanted to share some code created to complete fltk tutorial chapter 6, text editor example...
//  I found that i can call member functions in a class without using static keyword by
//  using templates to create a dispatcher function per class that uses callback data to locate
//  member function. 

#ifndef MY_CALLBACK_H
#define MY_CALLBACK_H

//=======================================================================================
// INVOKE MEMBER OF A CLASS FROM STATIC CALLBACK (without using static keyword)
//=======================================================================================

// Requires: C++11

// generic callback data
template<typename classname>
struct d${
  classname*     inst;
  void (classname::*func)(Fl_Widget*, void*);
  void*          data;
  Fl_Widget*     widget;
};

// generic callback function
template<typename classname>
void x$(Fl_Widget* widget, void* data) {
  d$<classname>* cb_data = (d$<classname>*)data;
  cb_data->widget = widget;
  // given a class instances, call a non-static member function of the class
  (cb_data->inst->*(cb_data->func))(cb_data->widget, cb_data->data);
}


// EXAMPLE USAGE:

// class EditorWindow() : Fl_Window{600, 400, "Editor Window) {
// public:
//     EditorWindow() : Fl_Window{600, 400, "Editor Window) {
//        create_menu();
//     }
//     ~EditorWindow() {}
//     void create_menu();
//
//    Fl_Menu_Item*          menuitems;
//    Fl_Menu_Bar*           menubar;
//};
//
//}
//
//void EditorWindow::create_menu() {
//
//  using data = d$<EditorWindow>; //data for static callback function
//
//  void EditorWindow::create_menu() {
//    menuitems = new Fl_Menu_Item[20] {
//      { "&File",         0,                 0,                0,                                FL_SUBMENU },
//      { "&New File",     0,                 x$<EditorWindow>, new data{this, cb_new,  nullptr}, 0},
//      { "&Open File...", FL_COMMAND + 'o',  x$<EditorWindow>, new data{this, cb_open, nullptr}, 0},
//      { 0 },
//
//      { 0 }
//    };
//
//  menubar = new Fl_Menu_Bar (0, 0, 640, 30);
//  menubar->copy (menuitems);
//}

#endif


Albrecht Schlosser

unread,
Nov 18, 2016, 11:01:05 AM11/18/16
to fltkg...@googlegroups.com
On 18.11.2016 15:17 wm2015email@... wrote:
>
> // Hi, Just wanted to share some code created to complete fltk tutorial
> chapter 6, text editor example...

Thanks for sharing your code.

However, just to let you know, this is not suitable for the FLTK
library. It can't be used to "complete fltk tutorial chapter 6, text
editor example", if that is your intention.

You may take a look at our CMP http://fltk.org/cmp.php to see what we
can use or not. C++11 and templates don't qualify for usage inside the
FLTK library.

You may use it in your own code though and you may share it so others
can use it.

BTW: please don't use titles with all caps.

wm201...@gmail.com

unread,
Nov 18, 2016, 11:22:44 AM11/18/16
to fltk.general, Albrech...@online.de
Its ok... I'm just taking it as a pet project practice c++11 code...
This is actually very tricky code, so i thought somebody might find it useful...
Actually, I found an even better solution that will delete the data pointers when the object is destroyed as well...


#ifndef FL_CALLBACK_H
#define FL_CALLBACK_H


//=======================================================================================
// INVOKE MEMBER OF A CLASS FROM STATIC CALLBACK (without using static keyword)
//=======================================================================================

// Requires: C++11
#include <vector>

using std::vector;


// generic callback data
template<typename classname>
struct d${
  classname*     inst;
  void (classname::*func)(Fl_Widget*, void*);
  void*          data;
  Fl_Widget*     widget;
};

// generic callback function
template<typename classname>
void x$(Fl_Widget* widget, void* data) {
  d$<classname>* cb_data = (d$<classname>*)data;
  cb_data->widget = widget;
  // given a class instances, call a non-static member function of the class
  (cb_data->inst->*(cb_data->func))(cb_data->widget, cb_data->data);
}

// CALLBACK REGISTERY - Keep track of callback data using RAII
template<typename classname>
class cb_registry {
public:

  using datatype = d$<classname>;

  cb_registry(classname* inst) : inst{inst} {}

  ~cb_registry() {
      for (datatype* elem : keeper) {
        delete elem;
    }
  }

  Fl_Callback* func() {
      return (Fl_Callback*) x$<classname>;
  }

  void* data(
    void (classname::*fptr)(Fl_Widget*, void*),
    void*          data)
  {
    datatype* dataptr = new datatype {inst, fptr, data, 0};
    keeper.push_back(dataptr);
    return (void*) dataptr;
  }

private:
  classname*         inst;
  vector<datatype*>  keeper;

};


// EXAMPLE USAGE:

// class EditorWindow() : Fl_Window{600, 400, "Editor Window) {
// public:
//     cb_registry<EditorWindow> cb;
//
//     EditorWindow()
//     : Fl_Window{600, 400, "Editor Window),
//       cb{this}
//     {

//        create_menu();
//     }
//     ~EditorWindow() {}
//     void create_menu();
//
//     void cb_new  (Fl_Widget*, void*);
//     void cb_open (Fl_Widget*, void*);

//
//     Fl_Menu_Item*          menuitems;
//     Fl_Menu_Bar*           menubar;
//};
//
//}
//
//void EditorWindow::create_menu() {
//
//  void EditorWindow::create_menu() {
//    menuitems = new Fl_Menu_Item[20] {
//      { "&File",         0,                 0,          0,                          FL_SUBMENU },
//      { "&New File",     0,                 cb->func(), cb->data(cb_new,  nullptr), 0 },
//      { "&Open File...", FL_COMMAND + 'o',  cb->func(), cb->data(cb_open, nullptr), 0 },

//      { 0 },
//
//      { 0 }
//    };
//
//  menubar = new Fl_Menu_Bar (0, 0, 640, 30);
//  menubar->copy (menuitems);
//}

#endif



//============================================================
// HERE"S the rewrite of text editor so far using this class
//#ifndef FL_CALLBACK_H
#define FL_CALLBACK_H


//=======================================================================================
// INVOKE MEMBER OF A CLASS FROM STATIC CALLBACK (without using static keyword)
//=======================================================================================

// Requires: C++11
#include <vector>

using std::vector;


// generic callback data
template<typename classname>
struct d${
  classname*     inst;
  void (classname::*func)(Fl_Widget*, void*);
  void*          data;
  Fl_Widget*     widget;
};

// generic callback function
template<typename classname>
void x$(Fl_Widget* widget, void* data) {
  d$<classname>* cb_data = (d$<classname>*)data;
  cb_data->widget = widget;
  // given a class instances, call a non-static member function of the class
  (cb_data->inst->*(cb_data->func))(cb_data->widget, cb_data->data);
}

// CALLBACK REGISTERY - Keep track of callback data using RAII
template<typename classname>
class cb_registry {
public:

  using datatype = d$<classname>;

  cb_registry(classname* inst) : inst{inst} {}

  ~cb_registry() {
      for (datatype* elem : keeper) {
        delete elem;
    }
  }

  Fl_Callback* func() {
      return (Fl_Callback*) x$<classname>;
  }

  void* data(
    void (classname::*fptr)(Fl_Widget*, void*),
    void*          data)
  {
    datatype* dataptr = new datatype {inst, fptr, data, 0};
    keeper.push_back(dataptr);
    return (void*) dataptr;
  }

private:
  classname*         inst;
  vector<datatype*>  keeper;
};

#endif


============================================================


#include <FL/Fl.H>
#include <FL/Fl_Input.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Return_Button.H>
#include <FL/Fl_Text_Editor.H>
#include <FL/Fl_Menu_Bar.H>
#include <Fl/Fl_Window.H>
#include <Fl/Fl_Double_Window.H>
#include <Fl/Fl_Text_Editor.H>
#include <iostream>
#include <string>

using std::cout;
using std::string;

#include "fl_callback.h"

class ReplaceWindow : public Fl_Window {;
public:
  Fl_Input*   replace_find;
  Fl_Input*   replace_with;
  Fl_Button*  replace_all;
  Fl_Button*  replace_next;
  Fl_Button*  replace_cancel;

  ReplaceWindow() : Fl_Window{300, 105, "Replace"} {
    replace_find   = new Fl_Input  (70,  10,  200, 25, "Find:");
    replace_with   = new Fl_Input  (70,  40,  200, 25, "Replace:");
    replace_all    = new Fl_Button (10,  70,  90,  25, "Replace All");
    replace_next   = new Fl_Button (105, 70,  120, 25, "Replace Next");
    replace_cancel = new Fl_Button (230, 70,  60,  25, "Cancel");
  }

  ~ReplaceWindow() {
    delete replace_find;
    delete replace_with;
    delete replace_all;
    delete replace_next;
    delete replace_cancel;
  }
};

class EditorWindow : public Fl_Double_Window
{
public:
  FL_CALLBACK(EditorWindow);

  EditorWindow(int w, int h, const char* t);
  ~EditorWindow() {}

    void create_menu();
    cb_registry<EditorWindow>  cb;
    Fl_Window*                 replace_dlg;
    Fl_Input*                  replace_find;
    Fl_Input*                  replace_with;
    Fl_Button*                 replace_all;
    Fl_Return_Button*          replace_next;
    Fl_Button*                 replace_cancel;
    Fl_Text_Editor*            editor;
    Fl_Text_Buffer*            textbuf;
    Fl_Menu_Item*              menuitems;
    Fl_Menu_Bar*               menubar;
    char                       search[256];
    int                        changed       = 0;
    string                     filename {""};
    string                     title {""};
    int                        loading {0};

    void set_title   ();

    void cb_new      (Fl_Widget* widget, void* data) {cout << "new\n";}
    void cb_open     (Fl_Widget* widget, void* data) {cout << "open\n";}
    void cb_insert   (Fl_Widget* widget, void* data) {cout << "insert\n";}
    void cb_save     (Fl_Widget* widget, void* data) {cout << "save\n";}
    void cb_saveas   (Fl_Widget* widget, void* data) {cout << "saveas\n";}
    void cb_view     (Fl_Widget* widget, void* data) {cout << "view\n";}
    void cb_close    (Fl_Widget* widget, void* data) {cout << "close\n";}
    void cb_quit     (Fl_Widget* widget, void* data) {cout << "quit\n";}
    void cb_undo     (Fl_Widget* widget, void* data) {cout << "undo\n";}
    void cb_cut      (Fl_Widget* widget, void* data);
    void cb_copy     (Fl_Widget* widget, void* data);
    void cb_paste    (Fl_Widget* widget, void* data);
    void cb_delete   (Fl_Widget* widget, void* data);
    void cb_find     (Fl_Widget* widget, void* data);
    void cb_find2    (Fl_Widget* widget, void* data);
    void cb_replace  (Fl_Widget* widget, void* data);
    void cb_replace2 (Fl_Widget* widget, void* data);

    void changed_cb(int nInserted, int nDeleted);
    static void changed_cb_static(int, int nInserted, int nDeleted, int, const char*, void* v);
};



#include <Fl/Fl_Menu_Item.H>
#include <Fl/Fl_Menu_Bar.H>
#include <Fl/Fl_Input.H>
#include <FL/fl_ask.H> // fl_input

#include "win_first.h"

EditorWindow::EditorWindow(int w, int h, const char* t) :
    Fl_Double_Window{w, h, t},
    cb{this}
{
    create_menu();
    textbuf  = new Fl_Text_Buffer();
    editor   = new Fl_Text_Editor (0, 30, w, h-30);
    editor->buffer(textbuf);
    editor->textfont(FL_COURIER);
    textbuf->add_modify_callback(changed_cb_static, (void*)this);
    textbuf->call_modify_callbacks ();

    set_title();
}


void EditorWindow::create_menu()
{

   using d = d$<EditorWindow>; // Data for Callback Function

    menuitems = new Fl_Menu_Item[100]
    {

        { "&File",            0,                           0,                0,                                   FL_SUBMENU },
        { "&New File",        0,                           cb.func(), cb.data(cb_new,      nullptr),   0},
        { "&Open File...",    FL_COMMAND + 'o',            cb.func(), cb.data(cb_open,     nullptr),   0},
        { "&Insert File...",  FL_COMMAND + 'i',            cb.func(), cb.data(cb_insert,   nullptr),   FL_MENU_DIVIDER},
        { "&Save File",       FL_COMMAND + 's',            cb.func(), cb.data(cb_save,     nullptr),   0},
        { "Save File &As...", FL_COMMAND + FL_SHIFT + 's', cb.func(), cb.data(cb_saveas,   nullptr),   FL_MENU_DIVIDER},
        { "New &View",        FL_ALT + 'v',                cb.func(), cb.data(cb_view,     nullptr),   0},
        { "&Close View",      FL_COMMAND + 'w',            cb.func(), cb.data(cb_close,    nullptr),   FL_MENU_DIVIDER},
        { "E&xit",            FL_COMMAND + 'q',            cb.func(), cb.data(cb_quit,     nullptr),   0},
        { 0 },

        { "&Edit",            0,                           0,                0,                               FL_SUBMENU },
        { "&Undo",            FL_COMMAND + 'z',            cb.func(), cb.data(cb_undo,     nullptr),   FL_MENU_DIVIDER},
        { "Cu&t",             FL_COMMAND + 'x',            cb.func(), cb.data(cb_cut,      nullptr),   0},
        { "&Copy",            FL_COMMAND + 'c',            cb.func(), cb.data(cb_copy,     nullptr),   0},
        { "&Paste",           FL_COMMAND + 'v',            cb.func(), cb.data(cb_paste,    nullptr),   0},
        { "&Delete",          0,                           cb.func(), cb.data(cb_delete,   nullptr),   0},
        { 0 },

        { "&Search", 0, 0, 0, FL_SUBMENU,                  0,                0,                               0},
        { "&Find...",         FL_COMMAND + 'f',            cb.func(), cb.data(cb_find,     nullptr),   0},
        { "F&ind Again",      FL_COMMAND + 'g',            cb.func(), cb.data(cb_find2,    nullptr),   0},
        { "&Replace...",      FL_COMMAND + 'r',            cb.func(), cb.data(cb_replace,  nullptr),   0},
        { "Re&place Again",   FL_COMMAND + 't',            cb.func(), cb.data(cb_replace2, nullptr),   0},
        { 0 },

        { 0 }
    };


    menubar = new Fl_Menu_Bar (0, 0, 640, 30);
    menubar->copy (menuitems);
}


void EditorWindow::set_title()
{

    if (!filename[0])
        title = "Untitled";
    else {

        title = filename;
        const size_t last_slash_idx = title.find_last_of("\\/");
        if (std::string::npos != last_slash_idx) {
            title.erase(0, last_slash_idx + 1);
        }
    }

    if (changed) {
        title += " (modified)";
    }

    label (title.c_str());
}



void EditorWindow::cb_copy(Fl_Widget* widget, void* data)
{
    Fl_Text_Editor::kf_copy (0, editor);
}

void EditorWindow::cb_cut(Fl_Widget*, void* data)
{
    Fl_Text_Editor::kf_cut (0, editor);
}

void EditorWindow::cb_delete(Fl_Widget*, void* data)
{
    textbuf->remove_selection ();
}

void EditorWindow::cb_find(Fl_Widget* w, void* v)
{
    const char *val;

    val = fl_input ("Search String:", search);
    if (val != NULL)
    {
        // User entered a string - go find it!
        strcpy(search, val);
        cb_find2(w, v);
    }
}



void EditorWindow::cb_find2(Fl_Widget* w, void* v)
{
    if (search[0] == '\0')
    {
        // Search string is blank; get a new one...
        cb_find(w, v);
        return;
    }

    int pos = editor->insert_position();
    int found = textbuf->search_forward (pos, search, &pos);
    if (found)
    {
        // Found a match; select and update the position...
        textbuf->select (pos, pos+strlen(search));
        editor->insert_position(pos+strlen(search));
        editor->show_insert_position();
    }
    else fl_alert ("No occurrences of \'%s\' found!", search);
}

void EditorWindow::cb_paste(Fl_Widget* w, void* v) {
    Fl_Text_Editor::kf_paste (0, editor);
}



void EditorWindow::changed_cb_static(int, int nInserted, int nDeleted, int, const char*, void* v) {
 EditorWindow *w = (EditorWindow *)v;
 w->changed_cb(nInserted, nDeleted);
}

void EditorWindow::changed_cb(int nInserted, int nDeleted)
{
    if ((nInserted || nDeleted) && !loading)
        changed = 1;

    set_title();

    if (loading)
        editor->show_insert_position();
}

void EditorWindow::cb_replace  (Fl_Widget* widget, void* data) {
        cout << "replace\n";
        ReplaceWindow* w = new ReplaceWindow();
        w->show();
        while (w->shown()) Fl::wait();
        cout << "replace done\n";
}

void EditorWindow::cb_replace2 (Fl_Widget* widget, void* data) {
    cout << "replace2\n";
}



wm201...@gmail.com

unread,
Nov 18, 2016, 11:43:11 AM11/18/16
to fltk.general
Personally, I think the founders of C++ just had some strange rule against having pointers to non-static class members that nobody really understands why they won't fix it.... because that's one of those things that has been making C++ code ugly for years.  Thus, you have strange macro hacks or strange template hacks, or lately lamba code...  seems to me they could have just avoided it all together if they just fixed the pointers to non-static member problem and built it into the language without all the strange code to go along with it...

wm201...@gmail.com

unread,
Nov 18, 2016, 3:27:26 PM11/18/16
to fltk.general
After solving the problem of invoking non-static class methods generically... I discovered that this is a great way to implement signal/slot for fltk.. (not sure if it already has this... but i guess if i'm already on chapter 6 and haven't seen this yet in the code, then it doesn't exist in the library... so I'll give you my implementation for the signals and slots, hopefully its helpful for somebody.)

#ifndef EXT_FL_CALLBACK_H
#define EXT_FL_CALLBACK_H

// Disclaimers: This is just some quick code that I put together to experiment with FLTK
//   library and that seems to work well for what I needed.
//
//=======================================================================================
// Provide mechanisms to:
//
// [1] Invoke a non-static member of a class as a callback function,
//   using a callback registry object instantiated once per class.
//
// [2] Communicate between windows objects of different types by using a
// simple Signal/Slot Mechanism for publishing signals, emitting signals,
//   and connecting signals to slots.
//
// Author: Bill Moore (wm2015email a t gmail.com)
// Data:   11/18/2016

//=======================================================================================

// Requires: C++11
#include <vector>

using std::vector;

// generic callback data
template<typename classname>
struct d${
  classname*     inst;
  void (classname::*func)(Fl_Widget*, void*);
  void*          data;
  Fl_Widget*     widget;
};

// generic callback function
template<typename classname>
void x$(Fl_Widget* widget, void* data) {
  d$<classname>* cb_data = (d$<classname>*)data;
  cb_data->widget = widget;
  // given a class instances, call a non-static member function of the class
  (cb_data->inst->*(cb_data->func))(cb_data->widget, cb_data->data);
}

// CALLBACK SLOT
class cb_slot {
public:
  Fl_Callback* func;
  void*        data;

};

// CALLBACK REGISTERY - Keep track of callback data using RAII
template<typename classname>
class cb_registry {
public:

  using datatype = d$<classname>;

  cb_registry(classname* inst) : inst{inst} {}

  ~cb_registry() {
      for (datatype* elem : keeper) {
        delete elem;
    }
  }

  Fl_Callback* func() {
      return (Fl_Callback*) x$<classname>;
  }

  void* data(
    void (classname::*fptr)(Fl_Widget*, void*),
    void*          data)
  {
    datatype* dataptr = new datatype {inst, fptr, data, 0};
    keeper.push_back(dataptr);
    return (void*) dataptr;
  }

  const cb_slot slot(
    void (classname::*fptr)(Fl_Widget*, void*),
    void*          dptr)
  {
     cb_slot slot1;
     slot1.func = func();
     slot1.data = data(fptr, dptr);
     return slot1; // return copy of slot

  }

private:
  classname*         inst;
  vector<datatype*>  keeper;
};

// CALLBACK SIGNAL
class cb_signal {
  public:
    cb_signal(Fl_Widget* widget) : widget{widget}, func{nullptr}, data{nullptr} {}

    ~cb_signal() {}

    void connect(const cb_slot& slot) {
        func = slot.func;
        data = slot.data;
    }

    void connect(Fl_Callback* callback, void* data) {
        func = callback;
        data = data;
    }

    void emit() {
        if(func) func(widget, data);
    }

  private:
    Fl_Widget*   widget;
    Fl_Callback* func;
    void*        data;
};


#include <FL/Fl.H>
#include <FL/Fl_Input.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Return_Button.H>
#include <FL/Fl_Text_Editor.H>
#include <FL/Fl_Menu_Bar.H>
#include <Fl/Fl_Window.H>
#include <Fl/Fl_Double_Window.H>
#include <Fl/Fl_Text_Editor.H>
#include <iostream>
#include <string>

using std::cout;
using std::string;

#include "fl_callback.h"

class EditorWindow;


class ReplaceWindow : public Fl_Window {;
public:

  cb_registry<ReplaceWindow>  cb {this};

  cb_signal     sig_replace_all  {this};
  cb_signal     sig_replace_next {this};

  Fl_Input*   inp_find;
  Fl_Input*   inp_with;
  Fl_Button*  btn_all;
  Fl_Button*  btn_next;
  Fl_Button*  btn_cancel;


  ReplaceWindow() : Fl_Window{300, 105, "Replace"}  {
    inp_find   = new Fl_Input  (70,  10,  200, 25, "Find:");
    inp_with   = new Fl_Input  (70,  40,  200, 25, "Replace:");
    btn_all    = new Fl_Button (10,  70,  90,  25, "Replace All");
    btn_next   = new Fl_Button (105, 70,  120, 25, "Replace Next");
    btn_cancel = new Fl_Button (230, 70,  60,  25, "Cancel");

    btn_all->callback    (cb.func(),  cb.data(cb_btn_all,    nullptr));
    btn_next->callback   (cb.func(),  cb.data(cb_btn_next,   nullptr));
    btn_cancel->callback (cb.func(),  cb.data(cb_btn_cancel, nullptr));
  }

  ~ReplaceWindow() {
    delete inp_find;
    delete inp_with;
    delete btn_all;
    delete btn_next;
    delete btn_cancel;
  }

  void cb_btn_all(Fl_Widget* , void* data) {
      cout << "cb_btn_all\n";
       sig_replace_all.emit();
  }

  void cb_btn_next(Fl_Widget* , void* data) {
      cout << "cb_btn_next\n";
       sig_replace_next.emit();
  }

  void cb_btn_cancel(Fl_Widget* , void* data) {
      cout << "cb_btn_cancel\n";

  }

};

class EditorWindow : public Fl_Double_Window
{
public:

  EditorWindow(int w, int h, const char* t);
  ~EditorWindow() {}

    void create_menu();
    cb_registry<EditorWindow>  cb {this};
    void cb_replace_all  (Fl_Widget* widget, void* data);
    void cb_replace_next (Fl_Widget* widget, void* data);


    void changed_cb(int nInserted, int nDeleted);
    static void changed_cb_static(int, int nInserted, int nDeleted, int, const char*, void* v);
};

#include <Fl/Fl_Menu_Item.H>
#include <Fl/Fl_Menu_Bar.H>
#include <Fl/Fl_Input.H>
#include <FL/fl_ask.H> // fl_input

#include "win_first.h"

EditorWindow::EditorWindow(int w, int h, const char* t) :
    Fl_Double_Window{w, h, t}
{
    create_menu();
    textbuf  = new Fl_Text_Buffer();
    editor   = new Fl_Text_Editor (0, 30, w, h-30);
    editor->buffer(textbuf);
    editor->textfont(FL_COURIER);
    textbuf->add_modify_callback(changed_cb_static, (void*)this);
    textbuf->call_modify_callbacks ();

    set_title();
}


void EditorWindow::create_menu()
{
    menuitems = new Fl_Menu_Item[100]
    {
        { "&File",            0,                           0,                0,                        FL_SUBMENU },
        { "&New File",        0,                           cb.func(), cb.data(cb_new,      nullptr),   0},
        { "&Open File...",    FL_COMMAND + 'o',            cb.func(), cb.data(cb_open,     nullptr),   0},
        { "&Insert File...",  FL_COMMAND + 'i',            cb.func(), cb.data(cb_insert,   nullptr),   FL_MENU_DIVIDER},
        { "&Save File",       FL_COMMAND + 's',            cb.func(), cb.data(cb_save,     nullptr),   0},
        { "Save File &As...", FL_COMMAND + FL_SHIFT + 's', cb.func(), cb.data(cb_saveas,   nullptr),   FL_MENU_DIVIDER},
        { "New &View",        FL_ALT + 'v',                cb.func(), cb.data(cb_view,     nullptr),   0},
        { "&Close View",      FL_COMMAND + 'w',            cb.func(), cb.data(cb_close,    nullptr),   FL_MENU_DIVIDER},
        { "E&xit",            FL_COMMAND + 'q',            cb.func(), cb.data(cb_quit,     nullptr),   0},
        { 0 },

        { "&Edit",            0,                           0,                0,                        FL_SUBMENU },
        { "&Undo",            FL_COMMAND + 'z',            cb.func(), cb.data(cb_undo,     nullptr),   FL_MENU_DIVIDER},
        { "Cu&t",             FL_COMMAND + 'x',            cb.func(), cb.data(cb_cut,      nullptr),   0},
        { "&Copy",            FL_COMMAND + 'c',            cb.func(), cb.data(cb_copy,     nullptr),   0},
        { "&Paste",           FL_COMMAND + 'v',            cb.func(), cb.data(cb_paste,    nullptr),   0},
        { "&Delete",          0,                           cb.func(), cb.data(cb_delete,   nullptr),   0},
        { 0 },

        { "&Search",          0,                           0,         0,                               FL_SUBMENU},

        { "&Find...",         FL_COMMAND + 'f',            cb.func(), cb.data(cb_find,     nullptr),   0},
        { "F&ind Again",      FL_COMMAND + 'g',            cb.func(), cb.data(cb_find2,    nullptr),   0},
        { "&Replace...",      FL_COMMAND + 'r',            cb.func(), cb.data(cb_replace,  nullptr),   0},
        { "Re&place Again",   FL_COMMAND + 't',            cb.func(), cb.data(cb_replace2, nullptr),   0},
        { 0 },

        { 0 }
    };

    menubar = new Fl_Menu_Bar (0, 0, 640, 30);
void EditorWindow::cb_replace_all  (Fl_Widget* widget, void* data) {
  cout << "EW:cb_replace_all\n";
}

void EditorWindow::cb_replace_next (Fl_Widget* widget, void* data) {
  cout << "EW:cb_replace_next\n";

}

void EditorWindow::cb_replace  (Fl_Widget* widget, void* data) {
        cout << "replace\n";
        ReplaceWindow* w = new ReplaceWindow();
        w->sig_replace_all.connect  (cb.slot(cb_replace_all,  nullptr));
        w->sig_replace_next.connect (cb.slot(cb_replace_next, nullptr));

Hannu Vuolasaho

unread,
Nov 18, 2016, 5:24:04 PM11/18/16
to fltkg...@googlegroups.com
Hello!

Nice magic I would say. I couldn't get the idea of the use case. What
is wrong with
http://www.fltk.org/doc-1.3/common.html at the bottom? If I remember
it correctly, fluid does this automatically.

Consider this untested piece of C code.
struct foo{
int a;
int sum;
void (*Set)(struct foo *self, int add);
}
void SetFunc(struct foo *self, int val){
self->sum += self->a;
self->a = val;
}
struct foo * CreateFoo(){
struct foo * ret = malloc(sizeof(struct foo));
if(foo){
ret->a = 0;
ret->rum = 0;
ret->Set = SetFunc;
}
return ret;
}

Now what kind of instantiation is required? ptr->Set(ptr, val); There
are two pointers. ptr and Set and same is happening in C++. the self
pointer is this pointer and it is automagically passed. Normal Set
pointer would be rather useless if you didn't have self available.
That's why it isn't possible to have non-static function pointers in
C++. The lifespan of objects, C compatibility and many other things
make fixing the pointer issue from impossible to totally unwanted.

And still it is possible:
http://www.dietmar-kuehl.de/mirror/c++-faq/pointers-to-members.html

Hopefully this clarified some aspects of the function pointer problem.

Best regards,
Hannu Vuolasaho
> --
> You received this message because you are subscribed to the Google Groups
> "fltk.general" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to fltkgeneral...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

wm201...@gmail.com

unread,
Nov 19, 2016, 8:29:39 AM11/19/16
to fltk.general
I wouldn't say never... Given how much they have already changed in C++11/14, it just seems like such a small step for them to also fix the C++ compiler so that you can actually get a pointer to a non-static class member  without using pointer magic.  (Maybe they already fixed it for all i know?)

All the compiler needs to do is create a temporary static function behind the scene and return a pointer to the temporary function, and then use said temporary function to in turn call the non-static class member for a specific class instance.

Call it C++16 for kicks.  But, I know they would never do anything that useful however... ;-)

Edzard Egberts

unread,
Nov 21, 2016, 2:24:55 AM11/21/16
to 'ed' via fltk.general
wm201...@gmail.com wrote:
> get a pointer to a non-static
> class member without using pointer magic.

I like to use an interface to get such a pointer:

struct i_callback
{ // pure virtual interface
virtual ~i_callback() {} // prevents warning
virtual void Cb_Foo()?= 0; // the class member
};

class something: public i_callback
{ // class inherited from interface
public:
virtual void Cb_Foo() { do_some_foo(); } // non-static member
};


class anything: public i_callback
{ // class inherited from interface
public:
virtual void Cb_Foo() { do_any_foo(); } // non-static member
};


// Usage:
something SomeThing; // Instance of class with interface
anything AnyThing;

i_callback* ptr_Cb= &SomeThing; // Get pointer to i_callback::Cb_Foo()
ptr_Cb->Cb_Foo(); // calls nonstatic member something::Cb_Foo()
ptr_Cb= &AnyThing; // Get pointer to Cb_Foo()
ptr_Cb->Cb_Foo(); // calls nonstatic member anything::Cb_Foo()



Reply all
Reply to author
Forward
0 new messages