1

I got stuck with a C++ compilation error while doing something that is probably not really "conventional". To make things easier I just re-wrote the mechanism I am trying to use in a easier-to-read way and I checked that I got the same issue.

First of all here is the code:

test.h // -- C++ --

template <typename MODULE> class item;

template <typename MODULE>
class init {
public:
  typedef int (MODULE::*funcPtr)(int);
private:
  funcPtr m_fp;
public:
  init& has_funcPtr(funcPtr fp) { m_fp = fp;}
  init() {}
  virtual ~init() {}
private:
  friend class item<MODULE>;
};

template <typename MODULE>
class item {
public:
  typedef int (MODULE::*funcPtr)(int);
private:
  funcPtr m_fp;
public:
  item(init<MODULE> params) : m_fp(params.m_fp) {}
  virtual ~item() {}
};

class user {
public:
  typedef init<user>::funcPtr funcPtr;
private:
  // Method CB
  int func1(int i);
  // Item member
  item<user> m_item;
public:
  user();
  virtual ~user();
};

test.cpp // -- C++ --

#include "test.h"

user::user() : m_item(init<user>().has_funcPtr(this->func1) ) {}

int user::func1(int i) {return 1;}

and here is the error:

/test.cpp:5:59: error: invalid use of non-static member function
 user::user() : m_item(init<user>().has_funcPtr(this->func1) ) {
                                                       ^

So, I am not sure this is the best way to achieve what I want (probably not, anyway if you have other suggestions they are very welcome) but my goal now is to make it work or to understand exactly why it can't work so that I learn something from it!

The basic idea is that:

  • the class "item" can be initialized with the named parameter idiom using the method "has_funcPtr" of the class "init" concatenated to its constructor like: "init().has_funcPtr(&function_name)".
  • the class "user" can store a pointer to its private method "func1" as a private member of its private member of type "item".

In this way, when a specific method of an object "item" is called (for simplicity I don't include this long part here since it is not relevant to the error but it is just to describe the goal of this snippet of code) that method can do stuff and call the private method of its father object "user" through that pointer to function (I hope this is clear enough...).

Now, I think there is an issue with the order of initialization of the objects but I am not sure where and how to fix it. In particular I thought that since the "func1" method doesn't operate on any member of the class "user", then its reference could be used directly in the initialization list to initialize an "init" object and feed it to an "item" object.

Thank you all in advance

Bertone
  • 756
  • 2
  • 9
  • 23
  • A comment if somebody in the future wants to reuse this code. In the method "has_funcPtr" in the "init" class I forgot to return the object itself. It should be: init& has_funcPtr(funcPtr fp) { m_fp = fp; return *this;} – Bertone Oct 20 '15 at 13:04

2 Answers2

4

this->func1 doesn't form a member function pointer. It should look like &user::func1 if you are in the user class.

Bo Persson
  • 90,663
  • 31
  • 146
  • 203
  • Thank you, it is incredible that I managed to loose so much time on something so basic... One more things, though. If I use &user::func1 am I using a pointer to that instance's method? Because At the beginning I tought that simply "has_funcPtr(func1) should work since I am already in the "user" scope and, in case, if I wanted to access an instance's method I should have used somehow the this pointer. – Bertone Oct 20 '15 at 13:01
  • A member function pointer is not bound to a specific instance, so you will also need to combine it with an instance when you use the pointer. See [How do I call a pointer-to-member function?](http://stackoverflow.com/questions/1928431/how-do-i-call-a-pointer-to-member-function) – Bo Persson Oct 20 '15 at 13:20
0

I post here the complete answer to my issue. I developed it after the suggestion from Bo and after understanding how to point to an instance specific method through a pointer to it.

In short, two things are really important to note:

  1. A pointer to a non-static class member function could be thought at as just an offset rather than an "absolute address" (http://www.codeguru.com/cpp/cpp/article.php/c17401/C-Tutorial-PointertoMember-Function.htm). This means that you can't access that function (it is just an offset) without first having an instance pointer. Once you have the instance pointer, with this "offset pointer" you can call that method using:

    (object_ptr->*method_ptr)(parameters_here)

    A better way would be to use a #define macro since this syntax is really error prone and complex to read (https://isocpp.org/wiki/faq/pointers-to-members):

    #define CALL_MEMBER_FN(ptrToObject,ptrToMember) ((ptrToObject)->*(ptrToMember))

    and then use it as:

    CALL_MEMBER_FN(object_ptr, method_ptr)(parameters_here)

  2. Following the first point, if you want a nested class to be able to call the upper class method by a pointer to it, you also need to pass the upper class instance pointer to access that function. In my case, since I wanted to be able to decide case by case if that method should be called or not, I used the Named Parameter Idiom (below note that func2 is not registered for example).

Finally here is the revised code that it works (tested):

-- C++ -- test.h

#include <iostream>

template <typename MODULE> class item;

template <typename MODULE>
class init {
public:
  typedef int  (MODULE::*funcPtr)(int);
  typedef bool (MODULE::*func2Ptr)(bool);
private:
  funcPtr  m_fp;
  func2Ptr m_fp2;
  MODULE* m_dad;
public:
  init& has_funcPtr(funcPtr fp) { m_fp = fp; return *this;}
  init& has_func2Ptr(func2Ptr fp2) { m_fp2 = fp2; return *this;}
  init(MODULE* dad) : m_dad(dad) { std::cout << "init constructor called\n"; }
  ~init() {}
private:
  friend class item<MODULE>;
};

template <typename MODULE>
class item {
public:
  typedef int  (MODULE::*funcPtr)(int);
  typedef bool (MODULE::*func2Ptr)(bool);
private:
  funcPtr  m_fp;
  func2Ptr m_fp2;
  MODULE*  m_dad;
public:
  item(init<MODULE> params) :
    m_fp(params.m_fp),
    m_fp2(params.m_fp2),
    m_dad(params.m_dad)
  {
    std::cout << "item constructor called\n";
  }
  ~item() {}
  // Method invoked externally
  int callback() {
    std::cout << "item class method callback invoked\n";
    // In the real case here do general stuff
    if(m_fp) {
      int i = (m_dad->*m_fp)(1); // call member function through its pointer
      return i;
    } else {
      std::cout << "callback not registered\n";
      return 0;
    }
  }
  // Method invoked externally
  bool callback2() {
    std::cout << "items class method callback2 invoked\n";
    // In the real case here do general stuff
    if(m_fp2) {
      bool b = (m_dad->*m_fp2)(true); // call member function through its pointer
      return b;
    } else {
      std::cout << "callback2 not registered\n";
      return false;
    }
  }
};

class user {
public:
  typedef init<user>::funcPtr funcPtr;
private:
  // Methods that optionally add more functionalities to the 2 callbacks
  int  func1(int i);
  bool func2(bool b);
public:
  // Item member
  item<user> m_item;
public:
  user();
  ~user();
};

-- C++ -- test.cpp

#include "test.h"

user::user() : m_item(init<user>(this).has_funcPtr(&user::func1) ) {
  std::cout << "user constructor called\n";
}

int user::func1(int i) {return i;}
bool user::func2(bool b) {return b;} // func2 won't be registered


int main() {
  user* u = new user();
  // Test callbacks
  int i = u->m_item.callback();
  bool b = u->m_item.callback2();
  std::cout << "main is printing i=" << i << " and b=" << b << "\n";
  std::cout << "expected results are i=1 and b=0\n" << "END\n";
  return 0;
}

OUTPUT:

init constructor called
item constructor called
user constructor called
item class method callback invoked
items class method callback2 invoked
callback2 not registered
main is printing i=1 and b=0
expected results are i=1 and b=0
END
Bertone
  • 756
  • 2
  • 9
  • 23